LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2012, 2013 Canonical Ltd. | 1 // Copyright 2012, 2013 Canonical Ltd. |
2 // Licensed under the AGPLv3, see LICENCE file for details. | 2 // Licensed under the AGPLv3, see LICENCE file for details. |
3 | 3 |
4 // Stub provider for OpenStack, using goose will be implemented here | 4 // Stub provider for OpenStack, using goose will be implemented here |
5 | 5 |
6 package openstack | 6 package openstack |
7 | 7 |
8 import ( | 8 import ( |
9 "encoding/json" | 9 "encoding/json" |
10 "errors" | 10 "errors" |
11 "fmt" | 11 "fmt" |
12 "io/ioutil" | 12 "io/ioutil" |
13 "launchpad.net/goose/client" | 13 "launchpad.net/goose/client" |
14 gooseerrors "launchpad.net/goose/errors" | 14 gooseerrors "launchpad.net/goose/errors" |
15 "launchpad.net/goose/identity" | 15 "launchpad.net/goose/identity" |
16 "launchpad.net/goose/nova" | 16 "launchpad.net/goose/nova" |
17 "launchpad.net/goose/swift" | 17 "launchpad.net/goose/swift" |
18 "launchpad.net/juju-core/constraints" | 18 "launchpad.net/juju-core/constraints" |
19 "launchpad.net/juju-core/environs" | 19 "launchpad.net/juju-core/environs" |
20 "launchpad.net/juju-core/environs/cloudinit" | 20 "launchpad.net/juju-core/environs/cloudinit" |
21 "launchpad.net/juju-core/environs/config" | 21 "launchpad.net/juju-core/environs/config" |
22 "launchpad.net/juju-core/environs/imagemetadata" | 22 "launchpad.net/juju-core/environs/imagemetadata" |
23 "launchpad.net/juju-core/environs/instances" | 23 "launchpad.net/juju-core/environs/instances" |
24 "launchpad.net/juju-core/environs/tools" | 24 "launchpad.net/juju-core/environs/tools" |
25 coreerrors "launchpad.net/juju-core/errors" | 25 coreerrors "launchpad.net/juju-core/errors" |
| 26 "launchpad.net/juju-core/instance" |
26 "launchpad.net/juju-core/log" | 27 "launchpad.net/juju-core/log" |
27 "launchpad.net/juju-core/state" | 28 "launchpad.net/juju-core/state" |
28 "launchpad.net/juju-core/state/api" | 29 "launchpad.net/juju-core/state/api" |
29 "launchpad.net/juju-core/state/api/params" | |
30 "launchpad.net/juju-core/utils" | 30 "launchpad.net/juju-core/utils" |
31 "net/http" | 31 "net/http" |
32 "strconv" | 32 "strconv" |
33 "strings" | 33 "strings" |
34 "sync" | 34 "sync" |
35 "time" | 35 "time" |
36 ) | 36 ) |
37 | 37 |
38 type environProvider struct{} | 38 type environProvider struct{} |
39 | 39 |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 } else if addr != "" { | 151 } else if addr != "" { |
152 return addr, nil | 152 return addr, nil |
153 } | 153 } |
154 return p.PrivateAddress() | 154 return p.PrivateAddress() |
155 } | 155 } |
156 | 156 |
157 func (p environProvider) PrivateAddress() (string, error) { | 157 func (p environProvider) PrivateAddress() (string, error) { |
158 return fetchMetadata("local-ipv4") | 158 return fetchMetadata("local-ipv4") |
159 } | 159 } |
160 | 160 |
161 func (p environProvider) InstanceId() (state.InstanceId, error) { | 161 func (p environProvider) InstanceId() (instance.Id, error) { |
162 str, err := fetchInstanceUUID() | 162 str, err := fetchInstanceUUID() |
163 if err != nil { | 163 if err != nil { |
164 str, err = fetchLegacyId() | 164 str, err = fetchLegacyId() |
165 } | 165 } |
166 » return state.InstanceId(str), err | 166 » return instance.Id(str), err |
167 } | 167 } |
168 | 168 |
169 // metadataHost holds the address of the instance metadata service. | 169 // metadataHost holds the address of the instance metadata service. |
170 // It is a variable so that tests can change it to refer to a local | 170 // It is a variable so that tests can change it to refer to a local |
171 // server when needed. | 171 // server when needed. |
172 var metadataHost = "http://169.254.169.254" | 172 var metadataHost = "http://169.254.169.254" |
173 | 173 |
174 // fetchMetadata fetches a single atom of data from the openstack instance metad
ata service. | 174 // fetchMetadata fetches a single atom of data from the openstack instance metad
ata service. |
175 // http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/AESDG-chapter-insta
ncedata.html | 175 // http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/AESDG-chapter-insta
ncedata.html |
176 // (the same specs is implemented in ec2, hence the reference) | 176 // (the same specs is implemented in ec2, hence the reference) |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
263 novaUnlocked *nova.Client | 263 novaUnlocked *nova.Client |
264 storageUnlocked environs.Storage | 264 storageUnlocked environs.Storage |
265 publicStorageUnlocked environs.StorageReader // optional. | 265 publicStorageUnlocked environs.StorageReader // optional. |
266 // An ordered list of paths in which to find the simplestreams index fil
es used to | 266 // An ordered list of paths in which to find the simplestreams index fil
es used to |
267 // look up image ids. | 267 // look up image ids. |
268 imageBaseURLs []string | 268 imageBaseURLs []string |
269 } | 269 } |
270 | 270 |
271 var _ environs.Environ = (*environ)(nil) | 271 var _ environs.Environ = (*environ)(nil) |
272 | 272 |
273 type instance struct { | 273 type openstackInstance struct { |
274 » e *environ | |
275 *nova.ServerDetail | 274 *nova.ServerDetail |
276 » address string | 275 » e *environ |
277 } | 276 » instType *instances.InstanceType |
278 | 277 » arch *string |
279 func (inst *instance) String() string { | 278 » address string |
| 279 } |
| 280 |
| 281 func (inst *openstackInstance) String() string { |
280 return inst.ServerDetail.Id | 282 return inst.ServerDetail.Id |
281 } | 283 } |
282 | 284 |
283 var _ environs.Instance = (*instance)(nil) | 285 var _ instance.Instance = (*openstackInstance)(nil) |
284 | 286 |
285 func (inst *instance) Id() state.InstanceId { | 287 func (inst *openstackInstance) Id() instance.Id { |
286 » return state.InstanceId(inst.ServerDetail.Id) | 288 » return instance.Id(inst.ServerDetail.Id) |
| 289 } |
| 290 |
| 291 func (inst *openstackInstance) Metadata() *instance.Metadata { |
| 292 » metadata := &instance.Metadata{Arch: inst.arch} |
| 293 » if inst.instType != nil { |
| 294 » » metadata.Mem = &inst.instType.Mem |
| 295 » » metadata.CpuCores = &inst.instType.CpuCores |
| 296 » » metadata.CpuPower = inst.instType.CpuPower |
| 297 » } |
| 298 » return metadata |
287 } | 299 } |
288 | 300 |
289 // instanceAddress processes a map of networks to lists of IP | 301 // instanceAddress processes a map of networks to lists of IP |
290 // addresses, as returned by Nova.GetServer(), extracting the proper | 302 // addresses, as returned by Nova.GetServer(), extracting the proper |
291 // public (or private, if public is not available) IPv4 address, and | 303 // public (or private, if public is not available) IPv4 address, and |
292 // returning it, or an error. | 304 // returning it, or an error. |
293 func instanceAddress(addresses map[string][]nova.IPAddress) (string, error) { | 305 func instanceAddress(addresses map[string][]nova.IPAddress) (string, error) { |
294 var private, public, privateNet string | 306 var private, public, privateNet string |
295 for network, ips := range addresses { | 307 for network, ips := range addresses { |
296 for _, address := range ips { | 308 for _, address := range ips { |
(...skipping 15 matching lines...) Expand all Loading... |
312 public = prv[1].Address | 324 public = prv[1].Address |
313 } | 325 } |
314 } | 326 } |
315 // Juju assumes it always needs a public address and loops waiting for o
ne. | 327 // Juju assumes it always needs a public address and loops waiting for o
ne. |
316 // In fact a private address is generally fine provided it can be sshed
to. | 328 // In fact a private address is generally fine provided it can be sshed
to. |
317 // (ported from py-juju/providers/openstack) | 329 // (ported from py-juju/providers/openstack) |
318 if public == "" && private != "" { | 330 if public == "" && private != "" { |
319 public = private | 331 public = private |
320 } | 332 } |
321 if public == "" { | 333 if public == "" { |
322 » » return "", environs.ErrNoDNSName | 334 » » return "", instance.ErrNoDNSName |
323 } | 335 } |
324 return public, nil | 336 return public, nil |
325 } | 337 } |
326 | 338 |
327 func (inst *instance) DNSName() (string, error) { | 339 func (inst *openstackInstance) DNSName() (string, error) { |
328 if inst.address != "" { | 340 if inst.address != "" { |
329 return inst.address, nil | 341 return inst.address, nil |
330 } | 342 } |
331 // Fetch the instance information again, in case | 343 // Fetch the instance information again, in case |
332 // the addresses have become available. | 344 // the addresses have become available. |
333 server, err := inst.e.nova().GetServer(string(inst.Id())) | 345 server, err := inst.e.nova().GetServer(string(inst.Id())) |
334 if err != nil { | 346 if err != nil { |
335 return "", err | 347 return "", err |
336 } | 348 } |
337 inst.address, err = instanceAddress(server.Addresses) | 349 inst.address, err = instanceAddress(server.Addresses) |
338 if err != nil { | 350 if err != nil { |
339 return "", err | 351 return "", err |
340 } | 352 } |
341 return inst.address, nil | 353 return inst.address, nil |
342 } | 354 } |
343 | 355 |
344 func (inst *instance) WaitDNSName() (string, error) { | 356 func (inst *openstackInstance) WaitDNSName() (string, error) { |
345 for a := longAttempt.Start(); a.Next(); { | 357 for a := longAttempt.Start(); a.Next(); { |
346 addr, err := inst.DNSName() | 358 addr, err := inst.DNSName() |
347 » » if err == nil || err != environs.ErrNoDNSName { | 359 » » if err == nil || err != instance.ErrNoDNSName { |
348 return addr, err | 360 return addr, err |
349 } | 361 } |
350 } | 362 } |
351 return "", fmt.Errorf("timed out trying to get DNS address for %v", inst
.Id()) | 363 return "", fmt.Errorf("timed out trying to get DNS address for %v", inst
.Id()) |
352 } | 364 } |
353 | 365 |
354 // TODO: following 30 lines nearly verbatim from environs/ec2 | 366 // TODO: following 30 lines nearly verbatim from environs/ec2 |
355 | 367 |
356 func (inst *instance) OpenPorts(machineId string, ports []params.Port) error { | 368 func (inst *openstackInstance) OpenPorts(machineId string, ports []instance.Port
) error { |
357 if inst.e.Config().FirewallMode() != config.FwInstance { | 369 if inst.e.Config().FirewallMode() != config.FwInstance { |
358 return fmt.Errorf("invalid firewall mode for opening ports on in
stance: %q", | 370 return fmt.Errorf("invalid firewall mode for opening ports on in
stance: %q", |
359 inst.e.Config().FirewallMode()) | 371 inst.e.Config().FirewallMode()) |
360 } | 372 } |
361 name := inst.e.machineGroupName(machineId) | 373 name := inst.e.machineGroupName(machineId) |
362 if err := inst.e.openPortsInGroup(name, ports); err != nil { | 374 if err := inst.e.openPortsInGroup(name, ports); err != nil { |
363 return err | 375 return err |
364 } | 376 } |
365 log.Infof("environs/openstack: opened ports in security group %s: %v", n
ame, ports) | 377 log.Infof("environs/openstack: opened ports in security group %s: %v", n
ame, ports) |
366 return nil | 378 return nil |
367 } | 379 } |
368 | 380 |
369 func (inst *instance) ClosePorts(machineId string, ports []params.Port) error { | 381 func (inst *openstackInstance) ClosePorts(machineId string, ports []instance.Por
t) error { |
370 if inst.e.Config().FirewallMode() != config.FwInstance { | 382 if inst.e.Config().FirewallMode() != config.FwInstance { |
371 return fmt.Errorf("invalid firewall mode for closing ports on in
stance: %q", | 383 return fmt.Errorf("invalid firewall mode for closing ports on in
stance: %q", |
372 inst.e.Config().FirewallMode()) | 384 inst.e.Config().FirewallMode()) |
373 } | 385 } |
374 name := inst.e.machineGroupName(machineId) | 386 name := inst.e.machineGroupName(machineId) |
375 if err := inst.e.closePortsInGroup(name, ports); err != nil { | 387 if err := inst.e.closePortsInGroup(name, ports); err != nil { |
376 return err | 388 return err |
377 } | 389 } |
378 log.Infof("environs/openstack: closed ports in security group %s: %v", n
ame, ports) | 390 log.Infof("environs/openstack: closed ports in security group %s: %v", n
ame, ports) |
379 return nil | 391 return nil |
380 } | 392 } |
381 | 393 |
382 func (inst *instance) Ports(machineId string) ([]params.Port, error) { | 394 func (inst *openstackInstance) Ports(machineId string) ([]instance.Port, error)
{ |
383 if inst.e.Config().FirewallMode() != config.FwInstance { | 395 if inst.e.Config().FirewallMode() != config.FwInstance { |
384 return nil, fmt.Errorf("invalid firewall mode for retrieving por
ts from instance: %q", | 396 return nil, fmt.Errorf("invalid firewall mode for retrieving por
ts from instance: %q", |
385 inst.e.Config().FirewallMode()) | 397 inst.e.Config().FirewallMode()) |
386 } | 398 } |
387 name := inst.e.machineGroupName(machineId) | 399 name := inst.e.machineGroupName(machineId) |
388 return inst.e.portsInGroup(name) | 400 return inst.e.portsInGroup(name) |
389 } | 401 } |
390 | 402 |
391 func (e *environ) ecfg() *environConfig { | 403 func (e *environ) ecfg() *environConfig { |
392 e.ecfgMutex.Lock() | 404 e.ecfgMutex.Lock() |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
477 if err != nil { | 489 if err != nil { |
478 break | 490 break |
479 } | 491 } |
480 } | 492 } |
481 if err == nil { | 493 if err == nil { |
482 return fmt.Errorf("environment is already bootstrapped") | 494 return fmt.Errorf("environment is already bootstrapped") |
483 } | 495 } |
484 if !coreerrors.IsNotFoundError(err) { | 496 if !coreerrors.IsNotFoundError(err) { |
485 return fmt.Errorf("cannot query old bootstrap state: %v", err) | 497 return fmt.Errorf("cannot query old bootstrap state: %v", err) |
486 } | 498 } |
| 499 err = environs.VerifyStorage(e.Storage()) |
| 500 if err != nil { |
| 501 return err |
| 502 } |
487 | 503 |
488 possibleTools, err := environs.FindBootstrapTools(e, cons) | 504 possibleTools, err := environs.FindBootstrapTools(e, cons) |
489 if err != nil { | 505 if err != nil { |
490 return err | 506 return err |
491 } | 507 } |
492 inst, err := e.startInstance(&startInstanceParams{ | 508 inst, err := e.startInstance(&startInstanceParams{ |
493 machineId: "0", | 509 machineId: "0", |
494 machineNonce: state.BootstrapNonce, | 510 machineNonce: state.BootstrapNonce, |
495 series: e.Config().DefaultSeries(), | 511 series: e.Config().DefaultSeries(), |
496 constraints: cons, | 512 constraints: cons, |
497 possibleTools: possibleTools, | 513 possibleTools: possibleTools, |
498 stateServer: true, | 514 stateServer: true, |
499 withPublicIP: e.ecfg().useFloatingIP(), | 515 withPublicIP: e.ecfg().useFloatingIP(), |
500 }) | 516 }) |
501 if err != nil { | 517 if err != nil { |
502 return fmt.Errorf("cannot start bootstrap instance: %v", err) | 518 return fmt.Errorf("cannot start bootstrap instance: %v", err) |
503 } | 519 } |
504 err = e.saveState(&bootstrapState{ | 520 err = e.saveState(&bootstrapState{ |
505 » » StateInstances: []state.InstanceId{inst.Id()}, | 521 » » StateInstances: []instance.Id{inst.Id()}, |
506 }) | 522 }) |
507 if err != nil { | 523 if err != nil { |
508 // ignore error on StopInstance because the previous error is | 524 // ignore error on StopInstance because the previous error is |
509 // more important. | 525 // more important. |
510 » » e.StopInstances([]environs.Instance{inst}) | 526 » » e.StopInstances([]instance.Instance{inst}) |
511 return fmt.Errorf("cannot save state: %v", err) | 527 return fmt.Errorf("cannot save state: %v", err) |
512 } | 528 } |
513 // TODO make safe in the case of racing Bootstraps | 529 // TODO make safe in the case of racing Bootstraps |
514 // If two Bootstraps are called concurrently, there's | 530 // If two Bootstraps are called concurrently, there's |
515 // no way to use Swift to make sure that only one succeeds. | 531 // no way to use Swift to make sure that only one succeeds. |
516 // Perhaps consider using SimpleDB for state storage | 532 // Perhaps consider using SimpleDB for state storage |
517 // which would enable that possibility. | 533 // which would enable that possibility. |
518 | 534 |
519 return nil | 535 return nil |
520 } | 536 } |
(...skipping 17 matching lines...) Expand all Loading... |
538 insts, err := e.Instances(st.StateInstances) | 554 insts, err := e.Instances(st.StateInstances) |
539 if err != nil && err != environs.ErrPartialInstances { | 555 if err != nil && err != environs.ErrPartialInstances { |
540 log.Debugf("error getting state instance: %v", err.Error
()) | 556 log.Debugf("error getting state instance: %v", err.Error
()) |
541 return nil, nil, err | 557 return nil, nil, err |
542 } | 558 } |
543 log.Debugf("started processing instances: %#v", insts) | 559 log.Debugf("started processing instances: %#v", insts) |
544 for _, inst := range insts { | 560 for _, inst := range insts { |
545 if inst == nil { | 561 if inst == nil { |
546 continue | 562 continue |
547 } | 563 } |
548 » » » name, err := inst.(*instance).DNSName() | 564 » » » name, err := inst.(*openstackInstance).DNSName() |
549 if err != nil { | 565 if err != nil { |
550 continue | 566 continue |
551 } | 567 } |
552 if name != "" { | 568 if name != "" { |
553 statePortSuffix := fmt.Sprintf(":%d", config.Sta
tePort()) | 569 statePortSuffix := fmt.Sprintf(":%d", config.Sta
tePort()) |
554 apiPortSuffix := fmt.Sprintf(":%d", config.APIPo
rt()) | 570 apiPortSuffix := fmt.Sprintf(":%d", config.APIPo
rt()) |
555 stateAddrs = append(stateAddrs, name+statePortSu
ffix) | 571 stateAddrs = append(stateAddrs, name+statePortSu
ffix) |
556 apiAddrs = append(apiAddrs, name+apiPortSuffix) | 572 apiAddrs = append(apiAddrs, name+apiPortSuffix) |
557 } | 573 } |
558 } | 574 } |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
650 productStreamsURL, err := e.client.MakeServiceURL("product-streams", nil
) | 666 productStreamsURL, err := e.client.MakeServiceURL("product-streams", nil
) |
651 if err == nil { | 667 if err == nil { |
652 e.imageBaseURLs = append(e.imageBaseURLs, productStreamsURL) | 668 e.imageBaseURLs = append(e.imageBaseURLs, productStreamsURL) |
653 } | 669 } |
654 // Add the default simplestreams base URL. | 670 // Add the default simplestreams base URL. |
655 e.imageBaseURLs = append(e.imageBaseURLs, imagemetadata.DefaultBaseURL) | 671 e.imageBaseURLs = append(e.imageBaseURLs, imagemetadata.DefaultBaseURL) |
656 | 672 |
657 return e.imageBaseURLs, nil | 673 return e.imageBaseURLs, nil |
658 } | 674 } |
659 | 675 |
660 func (e *environ) StartInstance(machineId, machineNonce string, series string, c
ons constraints.Value, info *state.Info, apiInfo *api.Info) (environs.Instance,
error) { | 676 func (e *environ) StartInstance(machineId, machineNonce string, series string, c
ons constraints.Value, info *state.Info, apiInfo *api.Info) (instance.Instance,
error) { |
661 possibleTools, err := environs.FindInstanceTools(e, series, cons) | 677 possibleTools, err := environs.FindInstanceTools(e, series, cons) |
662 if err != nil { | 678 if err != nil { |
663 return nil, err | 679 return nil, err |
664 } | 680 } |
665 return e.startInstance(&startInstanceParams{ | 681 return e.startInstance(&startInstanceParams{ |
666 machineId: machineId, | 682 machineId: machineId, |
667 machineNonce: machineNonce, | 683 machineNonce: machineNonce, |
668 series: series, | 684 series: series, |
669 constraints: cons, | 685 constraints: cons, |
670 info: info, | 686 info: info, |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
760 err = e.nova().AddServerFloatingIP(serverId, fip.IP) | 776 err = e.nova().AddServerFloatingIP(serverId, fip.IP) |
761 if err == nil { | 777 if err == nil { |
762 return nil | 778 return nil |
763 } | 779 } |
764 } | 780 } |
765 return err | 781 return err |
766 } | 782 } |
767 | 783 |
768 // startInstance is the internal version of StartInstance, used by Bootstrap | 784 // startInstance is the internal version of StartInstance, used by Bootstrap |
769 // as well as via StartInstance itself. | 785 // as well as via StartInstance itself. |
770 func (e *environ) startInstance(scfg *startInstanceParams) (environs.Instance, e
rror) { | 786 func (e *environ) startInstance(scfg *startInstanceParams) (instance.Instance, e
rror) { |
771 series := scfg.possibleTools.Series() | 787 series := scfg.possibleTools.Series() |
772 if len(series) != 1 { | 788 if len(series) != 1 { |
773 return nil, fmt.Errorf("expected single series, got %v", series) | 789 return nil, fmt.Errorf("expected single series, got %v", series) |
774 } | 790 } |
775 if series[0] != scfg.series { | 791 if series[0] != scfg.series { |
776 return nil, fmt.Errorf("tools mismatch: expected series %v, got
%v", series, series[0]) | 792 return nil, fmt.Errorf("tools mismatch: expected series %v, got
%v", series, series[0]) |
777 } | 793 } |
778 arches := scfg.possibleTools.Arches() | 794 arches := scfg.possibleTools.Arches() |
779 spec, err := findInstanceSpec(e, &instances.InstanceConstraint{ | 795 spec, err := findInstanceSpec(e, &instances.InstanceConstraint{ |
780 Region: e.ecfg().region(), | 796 Region: e.ecfg().region(), |
(...skipping 28 matching lines...) Expand all Loading... |
809 } | 825 } |
810 var groupNames = make([]nova.SecurityGroupName, len(groups)) | 826 var groupNames = make([]nova.SecurityGroupName, len(groups)) |
811 for i, g := range groups { | 827 for i, g := range groups { |
812 groupNames[i] = nova.SecurityGroupName{g.Name} | 828 groupNames[i] = nova.SecurityGroupName{g.Name} |
813 } | 829 } |
814 | 830 |
815 var server *nova.Entity | 831 var server *nova.Entity |
816 for a := shortAttempt.Start(); a.Next(); { | 832 for a := shortAttempt.Start(); a.Next(); { |
817 server, err = e.nova().RunServer(nova.RunServerOpts{ | 833 server, err = e.nova().RunServer(nova.RunServerOpts{ |
818 Name: e.machineFullName(scfg.machineId), | 834 Name: e.machineFullName(scfg.machineId), |
819 » » » FlavorId: spec.InstanceTypeId, | 835 » » » FlavorId: spec.InstanceType.Id, |
820 ImageId: spec.Image.Id, | 836 ImageId: spec.Image.Id, |
821 UserData: userData, | 837 UserData: userData, |
822 SecurityGroupNames: groupNames, | 838 SecurityGroupNames: groupNames, |
823 }) | 839 }) |
824 if err == nil || !gooseerrors.IsNotFound(err) { | 840 if err == nil || !gooseerrors.IsNotFound(err) { |
825 break | 841 break |
826 } | 842 } |
827 } | 843 } |
828 if err != nil { | 844 if err != nil { |
829 return nil, fmt.Errorf("cannot run instance: %v", err) | 845 return nil, fmt.Errorf("cannot run instance: %v", err) |
830 } | 846 } |
831 detail, err := e.nova().GetServer(server.Id) | 847 detail, err := e.nova().GetServer(server.Id) |
832 if err != nil { | 848 if err != nil { |
833 return nil, fmt.Errorf("cannot get started instance: %v", err) | 849 return nil, fmt.Errorf("cannot get started instance: %v", err) |
834 } | 850 } |
835 » inst := &instance{e, detail, ""} | 851 » inst := &openstackInstance{ |
| 852 » » e: e, |
| 853 » » ServerDetail: detail, |
| 854 » » arch: &spec.Image.Arch, |
| 855 » » instType: &spec.InstanceType, |
| 856 » } |
836 log.Infof("environs/openstack: started instance %q", inst.Id()) | 857 log.Infof("environs/openstack: started instance %q", inst.Id()) |
837 if scfg.withPublicIP { | 858 if scfg.withPublicIP { |
838 if err := e.assignPublicIP(publicIP, string(inst.Id())); err !=
nil { | 859 if err := e.assignPublicIP(publicIP, string(inst.Id())); err !=
nil { |
839 » » » if err := e.terminateInstances([]state.InstanceId{inst.I
d()}); err != nil { | 860 » » » if err := e.terminateInstances([]instance.Id{inst.Id()})
; err != nil { |
840 // ignore the failure at this stage, just log it | 861 // ignore the failure at this stage, just log it |
841 log.Debugf("environs/openstack: failed to termin
ate instance %q: %v", inst.Id(), err) | 862 log.Debugf("environs/openstack: failed to termin
ate instance %q: %v", inst.Id(), err) |
842 } | 863 } |
843 return nil, fmt.Errorf("cannot assign public address %s
to instance %q: %v", publicIP.IP, inst.Id(), err) | 864 return nil, fmt.Errorf("cannot assign public address %s
to instance %q: %v", publicIP.IP, inst.Id(), err) |
844 } | 865 } |
845 log.Infof("environs/openstack: assigned public IP %s to %q", pub
licIP.IP, inst.Id()) | 866 log.Infof("environs/openstack: assigned public IP %s to %q", pub
licIP.IP, inst.Id()) |
846 } | 867 } |
847 return inst, nil | 868 return inst, nil |
848 } | 869 } |
849 | 870 |
850 func (e *environ) StopInstances(insts []environs.Instance) error { | 871 func (e *environ) StopInstances(insts []instance.Instance) error { |
851 » ids := make([]state.InstanceId, len(insts)) | 872 » ids := make([]instance.Id, len(insts)) |
852 for i, inst := range insts { | 873 for i, inst := range insts { |
853 » » instanceValue, ok := inst.(*instance) | 874 » » instanceValue, ok := inst.(*openstackInstance) |
854 if !ok { | 875 if !ok { |
855 » » » return errors.New("Incompatible environs.Instance suppli
ed") | 876 » » » return errors.New("Incompatible instance.Instance suppli
ed") |
856 } | 877 } |
857 ids[i] = instanceValue.Id() | 878 ids[i] = instanceValue.Id() |
858 } | 879 } |
859 log.Debugf("environs/openstack: terminating instances %v", ids) | 880 log.Debugf("environs/openstack: terminating instances %v", ids) |
860 return e.terminateInstances(ids) | 881 return e.terminateInstances(ids) |
861 } | 882 } |
862 | 883 |
863 // collectInstances tries to get information on each instance id in ids. | 884 // collectInstances tries to get information on each instance id in ids. |
864 // It fills the slots in the given map for known servers with status | 885 // It fills the slots in the given map for known servers with status |
865 // either ACTIVE or BUILD. Returns a list of missing ids. | 886 // either ACTIVE or BUILD. Returns a list of missing ids. |
866 func (e *environ) collectInstances(ids []state.InstanceId, out map[state.Instanc
eId]environs.Instance) []state.InstanceId { | 887 func (e *environ) collectInstances(ids []instance.Id, out map[instance.Id]instan
ce.Instance) []instance.Id { |
867 var err error | 888 var err error |
868 serversById := make(map[string]nova.ServerDetail) | 889 serversById := make(map[string]nova.ServerDetail) |
869 if len(ids) == 1 { | 890 if len(ids) == 1 { |
870 // most common case - single instance | 891 // most common case - single instance |
871 var server *nova.ServerDetail | 892 var server *nova.ServerDetail |
872 server, err = e.nova().GetServer(string(ids[0])) | 893 server, err = e.nova().GetServer(string(ids[0])) |
873 if server != nil { | 894 if server != nil { |
874 serversById[server.Id] = *server | 895 serversById[server.Id] = *server |
875 } | 896 } |
876 } else { | 897 } else { |
877 var servers []nova.ServerDetail | 898 var servers []nova.ServerDetail |
878 servers, err = e.nova().ListServersDetail(e.machinesFilter()) | 899 servers, err = e.nova().ListServersDetail(e.machinesFilter()) |
879 for _, server := range servers { | 900 for _, server := range servers { |
880 serversById[server.Id] = server | 901 serversById[server.Id] = server |
881 } | 902 } |
882 } | 903 } |
883 if err != nil { | 904 if err != nil { |
884 return ids | 905 return ids |
885 } | 906 } |
886 » var missing []state.InstanceId | 907 » var missing []instance.Id |
887 for _, id := range ids { | 908 for _, id := range ids { |
888 if server, found := serversById[string(id)]; found { | 909 if server, found := serversById[string(id)]; found { |
889 if server.Status == nova.StatusActive || server.Status =
= nova.StatusBuild { | 910 if server.Status == nova.StatusActive || server.Status =
= nova.StatusBuild { |
890 » » » » out[id] = &instance{e, &server, ""} | 911 » » » » // TODO(wallyworld): lookup the flavor details t
o fill in the instance type data |
| 912 » » » » out[id] = &openstackInstance{e: e, ServerDetail:
&server} |
891 } | 913 } |
892 continue | 914 continue |
893 } | 915 } |
894 missing = append(missing, id) | 916 missing = append(missing, id) |
895 } | 917 } |
896 return missing | 918 return missing |
897 } | 919 } |
898 | 920 |
899 func (e *environ) Instances(ids []state.InstanceId) ([]environs.Instance, error)
{ | 921 func (e *environ) Instances(ids []instance.Id) ([]instance.Instance, error) { |
900 if len(ids) == 0 { | 922 if len(ids) == 0 { |
901 return nil, nil | 923 return nil, nil |
902 } | 924 } |
903 missing := ids | 925 missing := ids |
904 » found := make(map[state.InstanceId]environs.Instance) | 926 » found := make(map[instance.Id]instance.Instance) |
905 // Make a series of requests to cope with eventual consistency. | 927 // Make a series of requests to cope with eventual consistency. |
906 // Each request will attempt to add more instances to the requested | 928 // Each request will attempt to add more instances to the requested |
907 // set. | 929 // set. |
908 for a := shortAttempt.Start(); a.Next(); { | 930 for a := shortAttempt.Start(); a.Next(); { |
909 if missing = e.collectInstances(missing, found); len(missing) ==
0 { | 931 if missing = e.collectInstances(missing, found); len(missing) ==
0 { |
910 break | 932 break |
911 } | 933 } |
912 } | 934 } |
913 if len(found) == 0 { | 935 if len(found) == 0 { |
914 return nil, environs.ErrNoInstances | 936 return nil, environs.ErrNoInstances |
915 } | 937 } |
916 » insts := make([]environs.Instance, len(ids)) | 938 » insts := make([]instance.Instance, len(ids)) |
917 var err error | 939 var err error |
918 for i, id := range ids { | 940 for i, id := range ids { |
919 if inst := found[id]; inst != nil { | 941 if inst := found[id]; inst != nil { |
920 insts[i] = inst | 942 insts[i] = inst |
921 } else { | 943 } else { |
922 err = environs.ErrPartialInstances | 944 err = environs.ErrPartialInstances |
923 } | 945 } |
924 } | 946 } |
925 return insts, err | 947 return insts, err |
926 } | 948 } |
927 | 949 |
928 func (e *environ) AllInstances() (insts []environs.Instance, err error) { | 950 func (e *environ) AllInstances() (insts []instance.Instance, err error) { |
929 servers, err := e.nova().ListServersDetail(e.machinesFilter()) | 951 servers, err := e.nova().ListServersDetail(e.machinesFilter()) |
930 if err != nil { | 952 if err != nil { |
931 return nil, err | 953 return nil, err |
932 } | 954 } |
933 for _, server := range servers { | 955 for _, server := range servers { |
934 if server.Status == nova.StatusActive || server.Status == nova.S
tatusBuild { | 956 if server.Status == nova.StatusActive || server.Status == nova.S
tatusBuild { |
935 var s = server | 957 var s = server |
936 » » » insts = append(insts, &instance{e, &s, ""}) | 958 » » » // TODO(wallyworld): lookup the flavor details to fill i
n the instance type data |
| 959 » » » insts = append(insts, &openstackInstance{ |
| 960 » » » » e: e, |
| 961 » » » » ServerDetail: &s, |
| 962 » » » }) |
937 } | 963 } |
938 } | 964 } |
939 return insts, err | 965 return insts, err |
940 } | 966 } |
941 | 967 |
942 func (e *environ) Destroy(ensureInsts []environs.Instance) error { | 968 func (e *environ) Destroy(ensureInsts []instance.Instance) error { |
943 log.Infof("environs/openstack: destroying environment %q", e.name) | 969 log.Infof("environs/openstack: destroying environment %q", e.name) |
944 insts, err := e.AllInstances() | 970 insts, err := e.AllInstances() |
945 if err != nil { | 971 if err != nil { |
946 return fmt.Errorf("cannot get instances: %v", err) | 972 return fmt.Errorf("cannot get instances: %v", err) |
947 } | 973 } |
948 » found := make(map[state.InstanceId]bool) | 974 » found := make(map[instance.Id]bool) |
949 » var ids []state.InstanceId | 975 » var ids []instance.Id |
950 for _, inst := range insts { | 976 for _, inst := range insts { |
951 ids = append(ids, inst.Id()) | 977 ids = append(ids, inst.Id()) |
952 found[inst.Id()] = true | 978 found[inst.Id()] = true |
953 } | 979 } |
954 | 980 |
955 // Add any instances we've been told about but haven't yet shown | 981 // Add any instances we've been told about but haven't yet shown |
956 // up in the instance list. | 982 // up in the instance list. |
957 for _, inst := range ensureInsts { | 983 for _, inst := range ensureInsts { |
958 » » id := state.InstanceId(inst.(*instance).Id()) | 984 » » id := instance.Id(inst.(*openstackInstance).Id()) |
959 if !found[id] { | 985 if !found[id] { |
960 ids = append(ids, id) | 986 ids = append(ids, id) |
961 found[id] = true | 987 found[id] = true |
962 } | 988 } |
963 } | 989 } |
964 err = e.terminateInstances(ids) | 990 err = e.terminateInstances(ids) |
965 if err != nil { | 991 if err != nil { |
966 return err | 992 return err |
967 } | 993 } |
968 | 994 |
(...skipping 20 matching lines...) Expand all Loading... |
989 return fmt.Sprintf("juju-%s-%s", e.Name(), state.MachineTag(machineId)) | 1015 return fmt.Sprintf("juju-%s-%s", e.Name(), state.MachineTag(machineId)) |
990 } | 1016 } |
991 | 1017 |
992 // machinesFilter returns a nova.Filter matching all machines in the environment
. | 1018 // machinesFilter returns a nova.Filter matching all machines in the environment
. |
993 func (e *environ) machinesFilter() *nova.Filter { | 1019 func (e *environ) machinesFilter() *nova.Filter { |
994 filter := nova.NewFilter() | 1020 filter := nova.NewFilter() |
995 filter.Set(nova.FilterServer, fmt.Sprintf("juju-%s-.*", e.Name())) | 1021 filter.Set(nova.FilterServer, fmt.Sprintf("juju-%s-.*", e.Name())) |
996 return filter | 1022 return filter |
997 } | 1023 } |
998 | 1024 |
999 func (e *environ) openPortsInGroup(name string, ports []params.Port) error { | 1025 func (e *environ) openPortsInGroup(name string, ports []instance.Port) error { |
1000 novaclient := e.nova() | 1026 novaclient := e.nova() |
1001 group, err := novaclient.SecurityGroupByName(name) | 1027 group, err := novaclient.SecurityGroupByName(name) |
1002 if err != nil { | 1028 if err != nil { |
1003 return err | 1029 return err |
1004 } | 1030 } |
1005 for _, port := range ports { | 1031 for _, port := range ports { |
1006 _, err := novaclient.CreateSecurityGroupRule(nova.RuleInfo{ | 1032 _, err := novaclient.CreateSecurityGroupRule(nova.RuleInfo{ |
1007 ParentGroupId: group.Id, | 1033 ParentGroupId: group.Id, |
1008 FromPort: port.Number, | 1034 FromPort: port.Number, |
1009 ToPort: port.Number, | 1035 ToPort: port.Number, |
1010 IPProtocol: port.Protocol, | 1036 IPProtocol: port.Protocol, |
1011 Cidr: "0.0.0.0/0", | 1037 Cidr: "0.0.0.0/0", |
1012 }) | 1038 }) |
1013 if err != nil { | 1039 if err != nil { |
1014 // TODO: if err is not rule already exists, raise? | 1040 // TODO: if err is not rule already exists, raise? |
1015 log.Debugf("error creating security group rule: %v", err
.Error()) | 1041 log.Debugf("error creating security group rule: %v", err
.Error()) |
1016 } | 1042 } |
1017 } | 1043 } |
1018 return nil | 1044 return nil |
1019 } | 1045 } |
1020 | 1046 |
1021 func (e *environ) closePortsInGroup(name string, ports []params.Port) error { | 1047 func (e *environ) closePortsInGroup(name string, ports []instance.Port) error { |
1022 if len(ports) == 0 { | 1048 if len(ports) == 0 { |
1023 return nil | 1049 return nil |
1024 } | 1050 } |
1025 novaclient := e.nova() | 1051 novaclient := e.nova() |
1026 group, err := novaclient.SecurityGroupByName(name) | 1052 group, err := novaclient.SecurityGroupByName(name) |
1027 if err != nil { | 1053 if err != nil { |
1028 return err | 1054 return err |
1029 } | 1055 } |
1030 // TODO: Hey look ma, it's quadratic | 1056 // TODO: Hey look ma, it's quadratic |
1031 for _, port := range ports { | 1057 for _, port := range ports { |
1032 for _, p := range (*group).Rules { | 1058 for _, p := range (*group).Rules { |
1033 if p.IPProtocol == nil || *p.IPProtocol != port.Protocol
|| | 1059 if p.IPProtocol == nil || *p.IPProtocol != port.Protocol
|| |
1034 p.FromPort == nil || *p.FromPort != port.Number
|| | 1060 p.FromPort == nil || *p.FromPort != port.Number
|| |
1035 p.ToPort == nil || *p.ToPort != port.Number { | 1061 p.ToPort == nil || *p.ToPort != port.Number { |
1036 continue | 1062 continue |
1037 } | 1063 } |
1038 err := novaclient.DeleteSecurityGroupRule(p.Id) | 1064 err := novaclient.DeleteSecurityGroupRule(p.Id) |
1039 if err != nil { | 1065 if err != nil { |
1040 return err | 1066 return err |
1041 } | 1067 } |
1042 break | 1068 break |
1043 } | 1069 } |
1044 } | 1070 } |
1045 return nil | 1071 return nil |
1046 } | 1072 } |
1047 | 1073 |
1048 func (e *environ) portsInGroup(name string) (ports []params.Port, err error) { | 1074 func (e *environ) portsInGroup(name string) (ports []instance.Port, err error) { |
1049 group, err := e.nova().SecurityGroupByName(name) | 1075 group, err := e.nova().SecurityGroupByName(name) |
1050 if err != nil { | 1076 if err != nil { |
1051 return nil, err | 1077 return nil, err |
1052 } | 1078 } |
1053 for _, p := range (*group).Rules { | 1079 for _, p := range (*group).Rules { |
1054 for i := *p.FromPort; i <= *p.ToPort; i++ { | 1080 for i := *p.FromPort; i <= *p.ToPort; i++ { |
1055 » » » ports = append(ports, params.Port{ | 1081 » » » ports = append(ports, instance.Port{ |
1056 Protocol: *p.IPProtocol, | 1082 Protocol: *p.IPProtocol, |
1057 Number: i, | 1083 Number: i, |
1058 }) | 1084 }) |
1059 } | 1085 } |
1060 } | 1086 } |
1061 state.SortPorts(ports) | 1087 state.SortPorts(ports) |
1062 return ports, nil | 1088 return ports, nil |
1063 } | 1089 } |
1064 | 1090 |
1065 // TODO: following 30 lines nearly verbatim from environs/ec2 | 1091 // TODO: following 30 lines nearly verbatim from environs/ec2 |
1066 | 1092 |
1067 func (e *environ) OpenPorts(ports []params.Port) error { | 1093 func (e *environ) OpenPorts(ports []instance.Port) error { |
1068 if e.Config().FirewallMode() != config.FwGlobal { | 1094 if e.Config().FirewallMode() != config.FwGlobal { |
1069 return fmt.Errorf("invalid firewall mode for opening ports on en
vironment: %q", | 1095 return fmt.Errorf("invalid firewall mode for opening ports on en
vironment: %q", |
1070 e.Config().FirewallMode()) | 1096 e.Config().FirewallMode()) |
1071 } | 1097 } |
1072 if err := e.openPortsInGroup(e.globalGroupName(), ports); err != nil { | 1098 if err := e.openPortsInGroup(e.globalGroupName(), ports); err != nil { |
1073 return err | 1099 return err |
1074 } | 1100 } |
1075 log.Infof("environs/openstack: opened ports in global group: %v", ports) | 1101 log.Infof("environs/openstack: opened ports in global group: %v", ports) |
1076 return nil | 1102 return nil |
1077 } | 1103 } |
1078 | 1104 |
1079 func (e *environ) ClosePorts(ports []params.Port) error { | 1105 func (e *environ) ClosePorts(ports []instance.Port) error { |
1080 if e.Config().FirewallMode() != config.FwGlobal { | 1106 if e.Config().FirewallMode() != config.FwGlobal { |
1081 return fmt.Errorf("invalid firewall mode for closing ports on en
vironment: %q", | 1107 return fmt.Errorf("invalid firewall mode for closing ports on en
vironment: %q", |
1082 e.Config().FirewallMode()) | 1108 e.Config().FirewallMode()) |
1083 } | 1109 } |
1084 if err := e.closePortsInGroup(e.globalGroupName(), ports); err != nil { | 1110 if err := e.closePortsInGroup(e.globalGroupName(), ports); err != nil { |
1085 return err | 1111 return err |
1086 } | 1112 } |
1087 log.Infof("environs/openstack: closed ports in global group: %v", ports) | 1113 log.Infof("environs/openstack: closed ports in global group: %v", ports) |
1088 return nil | 1114 return nil |
1089 } | 1115 } |
1090 | 1116 |
1091 func (e *environ) Ports() ([]params.Port, error) { | 1117 func (e *environ) Ports() ([]instance.Port, error) { |
1092 if e.Config().FirewallMode() != config.FwGlobal { | 1118 if e.Config().FirewallMode() != config.FwGlobal { |
1093 return nil, fmt.Errorf("invalid firewall mode for retrieving por
ts from environment: %q", | 1119 return nil, fmt.Errorf("invalid firewall mode for retrieving por
ts from environment: %q", |
1094 e.Config().FirewallMode()) | 1120 e.Config().FirewallMode()) |
1095 } | 1121 } |
1096 return e.portsInGroup(e.globalGroupName()) | 1122 return e.portsInGroup(e.globalGroupName()) |
1097 } | 1123 } |
1098 | 1124 |
1099 func (e *environ) Provider() environs.EnvironProvider { | 1125 func (e *environ) Provider() environs.EnvironProvider { |
1100 return &providerInstance | 1126 return &providerInstance |
1101 } | 1127 } |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1178 for _, rule := range rules { | 1204 for _, rule := range rules { |
1179 rule.ParentGroupId = group.Id | 1205 rule.ParentGroupId = group.Id |
1180 _, err := novaClient.CreateSecurityGroupRule(rule) | 1206 _, err := novaClient.CreateSecurityGroupRule(rule) |
1181 if err != nil && !gooseerrors.IsDuplicateValue(err) { | 1207 if err != nil && !gooseerrors.IsDuplicateValue(err) { |
1182 return zeroGroup, err | 1208 return zeroGroup, err |
1183 } | 1209 } |
1184 } | 1210 } |
1185 return *group, nil | 1211 return *group, nil |
1186 } | 1212 } |
1187 | 1213 |
1188 func (e *environ) terminateInstances(ids []state.InstanceId) error { | 1214 func (e *environ) terminateInstances(ids []instance.Id) error { |
1189 if len(ids) == 0 { | 1215 if len(ids) == 0 { |
1190 return nil | 1216 return nil |
1191 } | 1217 } |
1192 var firstErr error | 1218 var firstErr error |
1193 novaClient := e.nova() | 1219 novaClient := e.nova() |
1194 for _, id := range ids { | 1220 for _, id := range ids { |
1195 err := novaClient.DeleteServer(string(id)) | 1221 err := novaClient.DeleteServer(string(id)) |
1196 if gooseerrors.IsNotFound(err) { | 1222 if gooseerrors.IsNotFound(err) { |
1197 err = nil | 1223 err = nil |
1198 } | 1224 } |
1199 if err != nil && firstErr == nil { | 1225 if err != nil && firstErr == nil { |
1200 log.Debugf("environs/openstack: error terminating instan
ce %q: %v", id, err) | 1226 log.Debugf("environs/openstack: error terminating instan
ce %q: %v", id, err) |
1201 firstErr = err | 1227 firstErr = err |
1202 } | 1228 } |
1203 } | 1229 } |
1204 return firstErr | 1230 return firstErr |
1205 } | 1231 } |
LEFT | RIGHT |