github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/apiserver/provisioner/provisioner.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner 5 6 import ( 7 "fmt" 8 "math/rand" 9 "sort" 10 "strings" 11 12 "github.com/juju/errors" 13 "github.com/juju/loggo" 14 "github.com/juju/names" 15 "github.com/juju/utils/set" 16 17 "github.com/juju/juju/apiserver/common" 18 "github.com/juju/juju/apiserver/common/storagecommon" 19 "github.com/juju/juju/apiserver/params" 20 "github.com/juju/juju/cloudconfig/instancecfg" 21 "github.com/juju/juju/constraints" 22 "github.com/juju/juju/container" 23 "github.com/juju/juju/environs" 24 "github.com/juju/juju/environs/tags" 25 "github.com/juju/juju/instance" 26 "github.com/juju/juju/network" 27 "github.com/juju/juju/provider" 28 "github.com/juju/juju/state" 29 "github.com/juju/juju/state/multiwatcher" 30 "github.com/juju/juju/state/watcher" 31 "github.com/juju/juju/storage" 32 "github.com/juju/juju/storage/poolmanager" 33 "github.com/juju/juju/storage/provider/registry" 34 ) 35 36 var logger = loggo.GetLogger("juju.apiserver.provisioner") 37 38 func init() { 39 common.RegisterStandardFacade("Provisioner", 0, NewProvisionerAPI) 40 41 // Version 1 has the same set of methods as 0, with the same 42 // signatures, but its ProvisioningInfo returns additional 43 // information. Clients may require version 1 so that they 44 // receive this additional information; otherwise they are 45 // compatible. 46 common.RegisterStandardFacade("Provisioner", 1, NewProvisionerAPI) 47 } 48 49 // ProvisionerAPI provides access to the Provisioner API facade. 50 type ProvisionerAPI struct { 51 *common.Remover 52 *common.StatusSetter 53 *common.StatusGetter 54 *common.DeadEnsurer 55 *common.PasswordChanger 56 *common.LifeGetter 57 *common.StateAddresser 58 *common.APIAddresser 59 *common.EnvironWatcher 60 *common.EnvironMachinesWatcher 61 *common.InstanceIdGetter 62 *common.ToolsFinder 63 *common.ToolsGetter 64 65 st *state.State 66 resources *common.Resources 67 authorizer common.Authorizer 68 getAuthFunc common.GetAuthFunc 69 } 70 71 // NewProvisionerAPI creates a new server-side ProvisionerAPI facade. 72 func NewProvisionerAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*ProvisionerAPI, error) { 73 if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() { 74 return nil, common.ErrPerm 75 } 76 getAuthFunc := func() (common.AuthFunc, error) { 77 isEnvironManager := authorizer.AuthEnvironManager() 78 isMachineAgent := authorizer.AuthMachineAgent() 79 authEntityTag := authorizer.GetAuthTag() 80 81 return func(tag names.Tag) bool { 82 if isMachineAgent && tag == authEntityTag { 83 // A machine agent can always access its own machine. 84 return true 85 } 86 switch tag := tag.(type) { 87 case names.MachineTag: 88 parentId := state.ParentId(tag.Id()) 89 if parentId == "" { 90 // All top-level machines are accessible by the 91 // environment manager. 92 return isEnvironManager 93 } 94 // All containers with the authenticated machine as a 95 // parent are accessible by it. 96 // TODO(dfc) sometimes authEntity tag is nil, which is fine because nil is 97 // only equal to nil, but it suggests someone is passing an authorizer 98 // with a nil tag. 99 return isMachineAgent && names.NewMachineTag(parentId) == authEntityTag 100 default: 101 return false 102 } 103 }, nil 104 } 105 getAuthOwner := func() (common.AuthFunc, error) { 106 return authorizer.AuthOwner, nil 107 } 108 env, err := st.Environment() 109 if err != nil { 110 return nil, err 111 } 112 urlGetter := common.NewToolsURLGetter(env.UUID(), st) 113 return &ProvisionerAPI{ 114 Remover: common.NewRemover(st, false, getAuthFunc), 115 StatusSetter: common.NewStatusSetter(st, getAuthFunc), 116 StatusGetter: common.NewStatusGetter(st, getAuthFunc), 117 DeadEnsurer: common.NewDeadEnsurer(st, getAuthFunc), 118 PasswordChanger: common.NewPasswordChanger(st, getAuthFunc), 119 LifeGetter: common.NewLifeGetter(st, getAuthFunc), 120 StateAddresser: common.NewStateAddresser(st), 121 APIAddresser: common.NewAPIAddresser(st, resources), 122 EnvironWatcher: common.NewEnvironWatcher(st, resources, authorizer), 123 EnvironMachinesWatcher: common.NewEnvironMachinesWatcher(st, resources, authorizer), 124 InstanceIdGetter: common.NewInstanceIdGetter(st, getAuthFunc), 125 ToolsFinder: common.NewToolsFinder(st, st, urlGetter), 126 ToolsGetter: common.NewToolsGetter(st, st, st, urlGetter, getAuthOwner), 127 st: st, 128 resources: resources, 129 authorizer: authorizer, 130 getAuthFunc: getAuthFunc, 131 }, nil 132 } 133 134 func (p *ProvisionerAPI) getMachine(canAccess common.AuthFunc, tag names.MachineTag) (*state.Machine, error) { 135 if !canAccess(tag) { 136 return nil, common.ErrPerm 137 } 138 entity, err := p.st.FindEntity(tag) 139 if err != nil { 140 return nil, err 141 } 142 // The authorization function guarantees that the tag represents a 143 // machine. 144 return entity.(*state.Machine), nil 145 } 146 147 func (p *ProvisionerAPI) watchOneMachineContainers(arg params.WatchContainer) (params.StringsWatchResult, error) { 148 nothing := params.StringsWatchResult{} 149 canAccess, err := p.getAuthFunc() 150 if err != nil { 151 return nothing, common.ErrPerm 152 } 153 tag, err := names.ParseMachineTag(arg.MachineTag) 154 if err != nil { 155 return nothing, common.ErrPerm 156 } 157 if !canAccess(tag) { 158 return nothing, common.ErrPerm 159 } 160 machine, err := p.st.Machine(tag.Id()) 161 if err != nil { 162 return nothing, err 163 } 164 var watch state.StringsWatcher 165 if arg.ContainerType != "" { 166 watch = machine.WatchContainers(instance.ContainerType(arg.ContainerType)) 167 } else { 168 watch = machine.WatchAllContainers() 169 } 170 // Consume the initial event and forward it to the result. 171 if changes, ok := <-watch.Changes(); ok { 172 return params.StringsWatchResult{ 173 StringsWatcherId: p.resources.Register(watch), 174 Changes: changes, 175 }, nil 176 } 177 return nothing, watcher.EnsureErr(watch) 178 } 179 180 // WatchContainers starts a StringsWatcher to watch containers deployed to 181 // any machine passed in args. 182 func (p *ProvisionerAPI) WatchContainers(args params.WatchContainers) (params.StringsWatchResults, error) { 183 result := params.StringsWatchResults{ 184 Results: make([]params.StringsWatchResult, len(args.Params)), 185 } 186 for i, arg := range args.Params { 187 watcherResult, err := p.watchOneMachineContainers(arg) 188 result.Results[i] = watcherResult 189 result.Results[i].Error = common.ServerError(err) 190 } 191 return result, nil 192 } 193 194 // WatchAllContainers starts a StringsWatcher to watch all containers deployed to 195 // any machine passed in args. 196 func (p *ProvisionerAPI) WatchAllContainers(args params.WatchContainers) (params.StringsWatchResults, error) { 197 return p.WatchContainers(args) 198 } 199 200 // SetSupportedContainers updates the list of containers supported by the machines passed in args. 201 func (p *ProvisionerAPI) SetSupportedContainers(args params.MachineContainersParams) (params.ErrorResults, error) { 202 result := params.ErrorResults{ 203 Results: make([]params.ErrorResult, len(args.Params)), 204 } 205 206 canAccess, err := p.getAuthFunc() 207 if err != nil { 208 return result, err 209 } 210 for i, arg := range args.Params { 211 tag, err := names.ParseMachineTag(arg.MachineTag) 212 if err != nil { 213 result.Results[i].Error = common.ServerError(common.ErrPerm) 214 continue 215 } 216 machine, err := p.getMachine(canAccess, tag) 217 if err != nil { 218 result.Results[i].Error = common.ServerError(err) 219 continue 220 } 221 if len(arg.ContainerTypes) == 0 { 222 err = machine.SupportsNoContainers() 223 } else { 224 err = machine.SetSupportedContainers(arg.ContainerTypes) 225 } 226 if err != nil { 227 result.Results[i].Error = common.ServerError(err) 228 } 229 } 230 return result, nil 231 } 232 233 // ContainerManagerConfig returns information from the environment config that is 234 // needed for configuring the container manager. 235 func (p *ProvisionerAPI) ContainerManagerConfig(args params.ContainerManagerConfigParams) (params.ContainerManagerConfig, error) { 236 var result params.ContainerManagerConfig 237 config, err := p.st.EnvironConfig() 238 if err != nil { 239 return result, err 240 } 241 cfg := make(map[string]string) 242 cfg[container.ConfigName] = container.DefaultNamespace 243 244 switch args.Type { 245 case instance.LXC: 246 if useLxcClone, ok := config.LXCUseClone(); ok { 247 cfg["use-clone"] = fmt.Sprint(useLxcClone) 248 } 249 if useLxcCloneAufs, ok := config.LXCUseCloneAUFS(); ok { 250 cfg["use-aufs"] = fmt.Sprint(useLxcCloneAufs) 251 } 252 if lxcDefaultMTU, ok := config.LXCDefaultMTU(); ok { 253 logger.Debugf("using default MTU %v for all LXC containers NICs", lxcDefaultMTU) 254 cfg[container.ConfigLXCDefaultMTU] = fmt.Sprintf("%d", lxcDefaultMTU) 255 } 256 } 257 258 if !environs.AddressAllocationEnabled() { 259 // No need to even try checking the environ for support. 260 logger.Debugf("address allocation feature flag not enabled") 261 result.ManagerConfig = cfg 262 return result, nil 263 } 264 265 // Create an environment to verify networking support. 266 env, err := environs.New(config) 267 if err != nil { 268 return result, err 269 } 270 if netEnv, ok := environs.SupportsNetworking(env); ok { 271 // Passing network.AnySubnet below should be interpreted by 272 // the provider as "does ANY subnet support this". 273 supported, err := netEnv.SupportsAddressAllocation(network.AnySubnet) 274 if err == nil && supported { 275 cfg[container.ConfigIPForwarding] = "true" 276 } else if err != nil { 277 // We log the error, but it's safe to ignore as it's not 278 // critical. 279 logger.Debugf("address allocation not supported (%v)", err) 280 } 281 // AWS requires NAT in place in order for hosted containers to 282 // reach outside. 283 if config.Type() == provider.EC2 { 284 cfg[container.ConfigEnableNAT] = "true" 285 } 286 } 287 288 result.ManagerConfig = cfg 289 return result, nil 290 } 291 292 // ContainerConfig returns information from the environment config that is 293 // needed for container cloud-init. 294 func (p *ProvisionerAPI) ContainerConfig() (params.ContainerConfig, error) { 295 result := params.ContainerConfig{} 296 config, err := p.st.EnvironConfig() 297 if err != nil { 298 return result, err 299 } 300 301 result.UpdateBehavior = ¶ms.UpdateBehavior{ 302 config.EnableOSRefreshUpdate(), 303 config.EnableOSUpgrade(), 304 } 305 result.ProviderType = config.Type() 306 result.AuthorizedKeys = config.AuthorizedKeys() 307 result.SSLHostnameVerification = config.SSLHostnameVerification() 308 result.Proxy = config.ProxySettings() 309 result.AptProxy = config.AptProxySettings() 310 result.PreferIPv6 = config.PreferIPv6() 311 result.AllowLXCLoopMounts, _ = config.AllowLXCLoopMounts() 312 313 return result, nil 314 } 315 316 // MachinesWithTransientErrors returns status data for machines with provisioning 317 // errors which are transient. 318 func (p *ProvisionerAPI) MachinesWithTransientErrors() (params.StatusResults, error) { 319 var results params.StatusResults 320 canAccessFunc, err := p.getAuthFunc() 321 if err != nil { 322 return results, err 323 } 324 // TODO (wallyworld) - add state.State API for more efficient machines query 325 machines, err := p.st.AllMachines() 326 if err != nil { 327 return results, err 328 } 329 for _, machine := range machines { 330 if !canAccessFunc(machine.Tag()) { 331 continue 332 } 333 if _, provisionedErr := machine.InstanceId(); provisionedErr == nil { 334 // Machine may have been provisioned but machiner hasn't set the 335 // status to Started yet. 336 continue 337 } 338 var result params.StatusResult 339 statusInfo, err := machine.Status() 340 if err != nil { 341 continue 342 } 343 result.Status = params.Status(statusInfo.Status) 344 result.Info = statusInfo.Message 345 result.Data = statusInfo.Data 346 if result.Status != params.StatusError { 347 continue 348 } 349 // Transient errors are marked as such in the status data. 350 if transient, ok := result.Data["transient"].(bool); !ok || !transient { 351 continue 352 } 353 result.Id = machine.Id() 354 result.Life = params.Life(machine.Life().String()) 355 results.Results = append(results.Results, result) 356 } 357 return results, nil 358 } 359 360 // Series returns the deployed series for each given machine entity. 361 func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) { 362 result := params.StringResults{ 363 Results: make([]params.StringResult, len(args.Entities)), 364 } 365 canAccess, err := p.getAuthFunc() 366 if err != nil { 367 return result, err 368 } 369 for i, entity := range args.Entities { 370 tag, err := names.ParseMachineTag(entity.Tag) 371 if err != nil { 372 result.Results[i].Error = common.ServerError(common.ErrPerm) 373 continue 374 } 375 machine, err := p.getMachine(canAccess, tag) 376 if err == nil { 377 result.Results[i].Result = machine.Series() 378 } 379 result.Results[i].Error = common.ServerError(err) 380 } 381 return result, nil 382 } 383 384 // ProvisioningInfo returns the provisioning information for each given machine entity. 385 func (p *ProvisionerAPI) ProvisioningInfo(args params.Entities) (params.ProvisioningInfoResults, error) { 386 result := params.ProvisioningInfoResults{ 387 Results: make([]params.ProvisioningInfoResult, len(args.Entities)), 388 } 389 canAccess, err := p.getAuthFunc() 390 if err != nil { 391 return result, err 392 } 393 for i, entity := range args.Entities { 394 tag, err := names.ParseMachineTag(entity.Tag) 395 if err != nil { 396 result.Results[i].Error = common.ServerError(common.ErrPerm) 397 continue 398 } 399 machine, err := p.getMachine(canAccess, tag) 400 if err == nil { 401 result.Results[i].Result, err = p.getProvisioningInfo(machine) 402 } 403 result.Results[i].Error = common.ServerError(err) 404 } 405 return result, nil 406 } 407 408 func (p *ProvisionerAPI) getProvisioningInfo(m *state.Machine) (*params.ProvisioningInfo, error) { 409 cons, err := m.Constraints() 410 if err != nil { 411 return nil, err 412 } 413 volumes, err := p.machineVolumeParams(m) 414 if err != nil { 415 return nil, errors.Trace(err) 416 } 417 // TODO(dimitern) Drop this once we only use spaces for 418 // deployments. 419 networks, err := m.RequestedNetworks() 420 if err != nil { 421 return nil, err 422 } 423 var jobs []multiwatcher.MachineJob 424 for _, job := range m.Jobs() { 425 jobs = append(jobs, job.ToParams()) 426 } 427 tags, err := p.machineTags(m, jobs) 428 if err != nil { 429 return nil, errors.Trace(err) 430 } 431 subnetsToZones, err := p.machineSubnetsAndZones(m) 432 if err != nil { 433 return nil, errors.Annotate(err, "cannot match subnets to zones") 434 } 435 return ¶ms.ProvisioningInfo{ 436 Constraints: cons, 437 Series: m.Series(), 438 Placement: m.Placement(), 439 Networks: networks, 440 Jobs: jobs, 441 Volumes: volumes, 442 Tags: tags, 443 SubnetsToZones: subnetsToZones, 444 }, nil 445 } 446 447 // DistributionGroup returns, for each given machine entity, 448 // a slice of instance.Ids that belong to the same distribution 449 // group as that machine. This information may be used to 450 // distribute instances for high availability. 451 func (p *ProvisionerAPI) DistributionGroup(args params.Entities) (params.DistributionGroupResults, error) { 452 result := params.DistributionGroupResults{ 453 Results: make([]params.DistributionGroupResult, len(args.Entities)), 454 } 455 canAccess, err := p.getAuthFunc() 456 if err != nil { 457 return result, err 458 } 459 for i, entity := range args.Entities { 460 tag, err := names.ParseMachineTag(entity.Tag) 461 if err != nil { 462 result.Results[i].Error = common.ServerError(common.ErrPerm) 463 continue 464 } 465 machine, err := p.getMachine(canAccess, tag) 466 if err == nil { 467 // If the machine is an environment manager, return 468 // environment manager instances. Otherwise, return 469 // instances with services in common with the machine 470 // being provisioned. 471 if machine.IsManager() { 472 result.Results[i].Result, err = environManagerInstances(p.st) 473 } else { 474 result.Results[i].Result, err = commonServiceInstances(p.st, machine) 475 } 476 } 477 result.Results[i].Error = common.ServerError(err) 478 } 479 return result, nil 480 } 481 482 // environManagerInstances returns all environ manager instances. 483 func environManagerInstances(st *state.State) ([]instance.Id, error) { 484 info, err := st.StateServerInfo() 485 if err != nil { 486 return nil, err 487 } 488 instances := make([]instance.Id, 0, len(info.MachineIds)) 489 for _, id := range info.MachineIds { 490 machine, err := st.Machine(id) 491 if err != nil { 492 return nil, err 493 } 494 instanceId, err := machine.InstanceId() 495 if err == nil { 496 instances = append(instances, instanceId) 497 } else if !errors.IsNotProvisioned(err) { 498 return nil, err 499 } 500 } 501 return instances, nil 502 } 503 504 // commonServiceInstances returns instances with 505 // services in common with the specified machine. 506 func commonServiceInstances(st *state.State, m *state.Machine) ([]instance.Id, error) { 507 units, err := m.Units() 508 if err != nil { 509 return nil, err 510 } 511 instanceIdSet := make(set.Strings) 512 for _, unit := range units { 513 if !unit.IsPrincipal() { 514 continue 515 } 516 instanceIds, err := state.ServiceInstances(st, unit.ServiceName()) 517 if err != nil { 518 return nil, err 519 } 520 for _, instanceId := range instanceIds { 521 instanceIdSet.Add(string(instanceId)) 522 } 523 } 524 instanceIds := make([]instance.Id, instanceIdSet.Size()) 525 // Sort values to simplify testing. 526 for i, instanceId := range instanceIdSet.SortedValues() { 527 instanceIds[i] = instance.Id(instanceId) 528 } 529 return instanceIds, nil 530 } 531 532 // Constraints returns the constraints for each given machine entity. 533 func (p *ProvisionerAPI) Constraints(args params.Entities) (params.ConstraintsResults, error) { 534 result := params.ConstraintsResults{ 535 Results: make([]params.ConstraintsResult, len(args.Entities)), 536 } 537 canAccess, err := p.getAuthFunc() 538 if err != nil { 539 return result, err 540 } 541 for i, entity := range args.Entities { 542 tag, err := names.ParseMachineTag(entity.Tag) 543 if err != nil { 544 result.Results[i].Error = common.ServerError(common.ErrPerm) 545 continue 546 } 547 machine, err := p.getMachine(canAccess, tag) 548 if err == nil { 549 var cons constraints.Value 550 cons, err = machine.Constraints() 551 if err == nil { 552 result.Results[i].Constraints = cons 553 } 554 } 555 result.Results[i].Error = common.ServerError(err) 556 } 557 return result, nil 558 } 559 560 // machineVolumeParams retrieves VolumeParams for the volumes that should be 561 // provisioned with, and attached to, the machine. The client should ignore 562 // parameters that it does not know how to handle. 563 func (p *ProvisionerAPI) machineVolumeParams(m *state.Machine) ([]params.VolumeParams, error) { 564 volumeAttachments, err := m.VolumeAttachments() 565 if err != nil { 566 return nil, err 567 } 568 if len(volumeAttachments) == 0 { 569 return nil, nil 570 } 571 envConfig, err := p.st.EnvironConfig() 572 if err != nil { 573 return nil, err 574 } 575 poolManager := poolmanager.New(state.NewStateSettings(p.st)) 576 allVolumeParams := make([]params.VolumeParams, 0, len(volumeAttachments)) 577 for _, volumeAttachment := range volumeAttachments { 578 volumeTag := volumeAttachment.Volume() 579 volume, err := p.st.Volume(volumeTag) 580 if err != nil { 581 return nil, errors.Annotatef(err, "getting volume %q", volumeTag.Id()) 582 } 583 storageInstance, err := storagecommon.MaybeAssignedStorageInstance( 584 volume.StorageInstance, p.st.StorageInstance, 585 ) 586 if err != nil { 587 return nil, errors.Annotatef(err, "getting volume %q storage instance", volumeTag.Id()) 588 } 589 volumeParams, err := storagecommon.VolumeParams(volume, storageInstance, envConfig, poolManager) 590 if err != nil { 591 return nil, errors.Annotatef(err, "getting volume %q parameters", volumeTag.Id()) 592 } 593 provider, err := registry.StorageProvider(storage.ProviderType(volumeParams.Provider)) 594 if err != nil { 595 return nil, errors.Annotate(err, "getting storage provider") 596 } 597 if provider.Dynamic() { 598 // Leave dynamic storage to the storage provisioner. 599 continue 600 } 601 volumeAttachmentParams, ok := volumeAttachment.Params() 602 if !ok { 603 // Attachment is already provisioned; this is an insane 604 // state, so we should not proceed with the volume. 605 return nil, errors.Errorf( 606 "volume %s already attached to machine %s", 607 volumeTag.Id(), m.Id(), 608 ) 609 } 610 // Not provisioned yet, so ask the cloud provisioner do it. 611 volumeParams.Attachment = ¶ms.VolumeAttachmentParams{ 612 volumeTag.String(), 613 m.Tag().String(), 614 "", // we're creating the volume, so it has no volume ID. 615 "", // we're creating the machine, so it has no instance ID. 616 volumeParams.Provider, 617 volumeAttachmentParams.ReadOnly, 618 } 619 allVolumeParams = append(allVolumeParams, volumeParams) 620 } 621 return allVolumeParams, nil 622 } 623 624 // storageConfig returns the provider type and config attributes for the 625 // specified poolName. If no such pool exists, we check to see if poolName is 626 // actually a provider type, in which case config will be empty. 627 func storageConfig(st *state.State, poolName string) (storage.ProviderType, map[string]interface{}, error) { 628 pm := poolmanager.New(state.NewStateSettings(st)) 629 p, err := pm.Get(poolName) 630 // If not a storage pool, then maybe a provider type. 631 if errors.IsNotFound(err) { 632 providerType := storage.ProviderType(poolName) 633 if _, err1 := registry.StorageProvider(providerType); err1 != nil { 634 return "", nil, errors.Trace(err) 635 } 636 return providerType, nil, nil 637 } 638 if err != nil { 639 return "", nil, errors.Trace(err) 640 } 641 return p.Provider(), p.Attrs(), nil 642 } 643 644 // volumeAttachmentsToState converts a slice of storage.VolumeAttachment to a 645 // mapping of volume names to state.VolumeAttachmentInfo. 646 func volumeAttachmentsToState(in []params.VolumeAttachment) (map[names.VolumeTag]state.VolumeAttachmentInfo, error) { 647 m := make(map[names.VolumeTag]state.VolumeAttachmentInfo) 648 for _, v := range in { 649 if v.VolumeTag == "" { 650 return nil, errors.New("Tag is empty") 651 } 652 volumeTag, err := names.ParseVolumeTag(v.VolumeTag) 653 if err != nil { 654 return nil, errors.Trace(err) 655 } 656 m[volumeTag] = state.VolumeAttachmentInfo{ 657 v.Info.DeviceName, 658 v.Info.DeviceLink, 659 v.Info.BusAddress, 660 v.Info.ReadOnly, 661 } 662 } 663 return m, nil 664 } 665 666 func networkParamsToStateParams(networks []params.Network, ifaces []params.NetworkInterface) ( 667 []state.NetworkInfo, []state.NetworkInterfaceInfo, error, 668 ) { 669 stateNetworks := make([]state.NetworkInfo, len(networks)) 670 for i, net := range networks { 671 tag, err := names.ParseNetworkTag(net.Tag) 672 if err != nil { 673 return nil, nil, err 674 } 675 stateNetworks[i] = state.NetworkInfo{ 676 Name: tag.Id(), 677 ProviderId: network.Id(net.ProviderId), 678 CIDR: net.CIDR, 679 VLANTag: net.VLANTag, 680 } 681 } 682 stateInterfaces := make([]state.NetworkInterfaceInfo, len(ifaces)) 683 for i, iface := range ifaces { 684 tag, err := names.ParseNetworkTag(iface.NetworkTag) 685 if err != nil { 686 return nil, nil, err 687 } 688 stateInterfaces[i] = state.NetworkInterfaceInfo{ 689 MACAddress: iface.MACAddress, 690 NetworkName: tag.Id(), 691 InterfaceName: iface.InterfaceName, 692 IsVirtual: iface.IsVirtual, 693 Disabled: iface.Disabled, 694 } 695 } 696 return stateNetworks, stateInterfaces, nil 697 } 698 699 // RequestedNetworks returns the requested networks for each given 700 // machine entity. Each entry in both lists is returned with its 701 // provider specific id. 702 func (p *ProvisionerAPI) RequestedNetworks(args params.Entities) (params.RequestedNetworksResults, error) { 703 result := params.RequestedNetworksResults{ 704 Results: make([]params.RequestedNetworkResult, len(args.Entities)), 705 } 706 canAccess, err := p.getAuthFunc() 707 if err != nil { 708 return result, err 709 } 710 for i, entity := range args.Entities { 711 tag, err := names.ParseMachineTag(entity.Tag) 712 if err != nil { 713 result.Results[i].Error = common.ServerError(common.ErrPerm) 714 continue 715 } 716 machine, err := p.getMachine(canAccess, tag) 717 if err == nil { 718 var networks []string 719 networks, err = machine.RequestedNetworks() 720 if err == nil { 721 // TODO(dimitern) For now, since network names and 722 // provider ids are the same, we return what we got 723 // from state. In the future, when networks can be 724 // added before provisioning, we should convert both 725 // slices from juju network names to provider-specific 726 // ids before returning them. 727 result.Results[i].Networks = networks 728 } 729 } 730 result.Results[i].Error = common.ServerError(err) 731 } 732 return result, nil 733 } 734 735 // SetProvisioned sets the provider specific instance id, nonce and 736 // metadata for each given machine. Once set, the instance id cannot 737 // be changed. 738 // 739 // TODO(dimitern) This is not used anymore (as of 1.19.0) and is 740 // retained only for backwards-compatibility. It should be removed as 741 // deprecated. SetInstanceInfo is used instead. 742 func (p *ProvisionerAPI) SetProvisioned(args params.SetProvisioned) (params.ErrorResults, error) { 743 result := params.ErrorResults{ 744 Results: make([]params.ErrorResult, len(args.Machines)), 745 } 746 canAccess, err := p.getAuthFunc() 747 if err != nil { 748 return result, err 749 } 750 for i, arg := range args.Machines { 751 tag, err := names.ParseMachineTag(arg.Tag) 752 if err != nil { 753 result.Results[i].Error = common.ServerError(common.ErrPerm) 754 continue 755 } 756 machine, err := p.getMachine(canAccess, tag) 757 if err == nil { 758 err = machine.SetProvisioned(arg.InstanceId, arg.Nonce, arg.Characteristics) 759 } 760 result.Results[i].Error = common.ServerError(err) 761 } 762 return result, nil 763 } 764 765 // SetInstanceInfo sets the provider specific machine id, nonce, 766 // metadata and network info for each given machine. Once set, the 767 // instance id cannot be changed. 768 func (p *ProvisionerAPI) SetInstanceInfo(args params.InstancesInfo) (params.ErrorResults, error) { 769 result := params.ErrorResults{ 770 Results: make([]params.ErrorResult, len(args.Machines)), 771 } 772 canAccess, err := p.getAuthFunc() 773 if err != nil { 774 return result, err 775 } 776 setInstanceInfo := func(arg params.InstanceInfo) error { 777 tag, err := names.ParseMachineTag(arg.Tag) 778 if err != nil { 779 return common.ErrPerm 780 } 781 machine, err := p.getMachine(canAccess, tag) 782 if err != nil { 783 return err 784 } 785 networks, interfaces, err := networkParamsToStateParams(arg.Networks, arg.Interfaces) 786 if err != nil { 787 return err 788 } 789 volumes, err := storagecommon.VolumesToState(arg.Volumes) 790 if err != nil { 791 return err 792 } 793 volumeAttachments, err := storagecommon.VolumeAttachmentInfosToState(arg.VolumeAttachments) 794 if err != nil { 795 return err 796 } 797 if err = machine.SetInstanceInfo( 798 arg.InstanceId, arg.Nonce, arg.Characteristics, 799 networks, interfaces, volumes, volumeAttachments); err != nil { 800 return errors.Annotatef( 801 err, 802 "cannot record provisioning info for %q", 803 arg.InstanceId, 804 ) 805 } 806 return nil 807 } 808 for i, arg := range args.Machines { 809 err := setInstanceInfo(arg) 810 result.Results[i].Error = common.ServerError(err) 811 } 812 return result, nil 813 } 814 815 // WatchMachineErrorRetry returns a NotifyWatcher that notifies when 816 // the provisioner should retry provisioning machines with transient errors. 817 func (p *ProvisionerAPI) WatchMachineErrorRetry() (params.NotifyWatchResult, error) { 818 result := params.NotifyWatchResult{} 819 if !p.authorizer.AuthEnvironManager() { 820 return result, common.ErrPerm 821 } 822 watch := newWatchMachineErrorRetry() 823 // Consume any initial event and forward it to the result. 824 if _, ok := <-watch.Changes(); ok { 825 result.NotifyWatcherId = p.resources.Register(watch) 826 } else { 827 return result, watcher.EnsureErr(watch) 828 } 829 return result, nil 830 } 831 832 // ReleaseContainerAddresses finds addresses allocated to a container 833 // and marks them as Dead, to be released and removed. It accepts 834 // container tags as arguments. If address allocation feature flag is 835 // not enabled, it will return a NotSupported error. 836 func (p *ProvisionerAPI) ReleaseContainerAddresses(args params.Entities) (params.ErrorResults, error) { 837 result := params.ErrorResults{ 838 Results: make([]params.ErrorResult, len(args.Entities)), 839 } 840 841 if !environs.AddressAllocationEnabled() { 842 return result, errors.NotSupportedf("address allocation") 843 } 844 845 canAccess, err := p.getAuthFunc() 846 if err != nil { 847 logger.Errorf("failed to get an authorisation function: %v", err) 848 return result, errors.Trace(err) 849 } 850 // Loop over the passed container tags. 851 for i, entity := range args.Entities { 852 tag, err := names.ParseMachineTag(entity.Tag) 853 if err != nil { 854 logger.Warningf("failed to parse machine tag %q: %v", entity.Tag, err) 855 result.Results[i].Error = common.ServerError(common.ErrPerm) 856 continue 857 } 858 859 // The auth function (canAccess) checks that the machine is a 860 // top level machine (we filter those out next) or that the 861 // machine has the host as a parent. 862 container, err := p.getMachine(canAccess, tag) 863 if err != nil { 864 logger.Warningf("failed to get machine %q: %v", tag, err) 865 result.Results[i].Error = common.ServerError(err) 866 continue 867 } else if !container.IsContainer() { 868 err = errors.Errorf("cannot mark addresses for removal for %q: not a container", tag) 869 result.Results[i].Error = common.ServerError(err) 870 continue 871 } 872 873 id := container.Id() 874 addresses, err := p.st.AllocatedIPAddresses(id) 875 if err != nil { 876 logger.Warningf("failed to get Id for container %q: %v", tag, err) 877 result.Results[i].Error = common.ServerError(err) 878 continue 879 } 880 881 deadErrors := []error{} 882 logger.Debugf("for container %q found addresses %v", tag, addresses) 883 for _, addr := range addresses { 884 err = addr.EnsureDead() 885 if err != nil { 886 deadErrors = append(deadErrors, err) 887 continue 888 } 889 } 890 if len(deadErrors) != 0 { 891 err = errors.Errorf("failed to mark all addresses for removal for %q: %v", tag, deadErrors) 892 result.Results[i].Error = common.ServerError(err) 893 } 894 } 895 896 return result, nil 897 } 898 899 // PrepareContainerInterfaceInfo allocates an address and returns 900 // information to configure networking for a container. It accepts 901 // container tags as arguments. If the address allocation feature flag 902 // is not enabled, it returns a NotSupported error. 903 func (p *ProvisionerAPI) PrepareContainerInterfaceInfo(args params.Entities) ( 904 params.MachineNetworkConfigResults, error) { 905 return p.prepareOrGetContainerInterfaceInfo(args, true) 906 } 907 908 // GetContainerInterfaceInfo returns information to configure networking 909 // for a container. It accepts container tags as arguments. If the address 910 // allocation feature flag is not enabled, it returns a NotSupported error. 911 func (p *ProvisionerAPI) GetContainerInterfaceInfo(args params.Entities) ( 912 params.MachineNetworkConfigResults, error) { 913 return p.prepareOrGetContainerInterfaceInfo(args, false) 914 } 915 916 // MACAddressTemplate is used to generate a unique MAC address for a 917 // container. Every '%x' is replaced by a random hexadecimal digit, 918 // while the rest is kept as-is. 919 const MACAddressTemplate = "00:16:3e:%02x:%02x:%02x" 920 921 // generateMACAddress creates a random MAC address within the space defined by 922 // MACAddressTemplate above. 923 func generateMACAddress() string { 924 digits := make([]interface{}, 3) 925 for i := range digits { 926 digits[i] = rand.Intn(256) 927 } 928 return fmt.Sprintf(MACAddressTemplate, digits...) 929 } 930 931 // prepareOrGetContainerInterfaceInfo optionally allocates an address and returns information 932 // for configuring networking on a container. It accepts container tags as arguments. 933 func (p *ProvisionerAPI) prepareOrGetContainerInterfaceInfo( 934 args params.Entities, provisionContainer bool) ( 935 params.MachineNetworkConfigResults, error) { 936 result := params.MachineNetworkConfigResults{ 937 Results: make([]params.MachineNetworkConfigResult, len(args.Entities)), 938 } 939 940 if !environs.AddressAllocationEnabled() { 941 return result, errors.NotSupportedf("address allocation") 942 } 943 944 // Some preparations first. 945 environ, host, canAccess, err := p.prepareContainerAccessEnvironment() 946 if err != nil { 947 return result, errors.Trace(err) 948 } 949 instId, err := host.InstanceId() 950 if err != nil && errors.IsNotProvisioned(err) { 951 // If the host machine is not provisioned yet, we have nothing 952 // to do. NotProvisionedf will append " not provisioned" to 953 // the message. 954 err = errors.NotProvisionedf("cannot allocate addresses: host machine %q", host) 955 return result, err 956 } 957 subnet, subnetInfo, interfaceInfo, err := p.prepareAllocationNetwork(environ, host, instId) 958 if err != nil { 959 return result, errors.Annotate(err, "cannot allocate addresses") 960 } 961 962 // Loop over the passed container tags. 963 for i, entity := range args.Entities { 964 tag, err := names.ParseMachineTag(entity.Tag) 965 if err != nil { 966 result.Results[i].Error = common.ServerError(err) 967 continue 968 } 969 970 // The auth function (canAccess) checks that the machine is a 971 // top level machine (we filter those out next) or that the 972 // machine has the host as a parent. 973 container, err := p.getMachine(canAccess, tag) 974 if err != nil { 975 result.Results[i].Error = common.ServerError(err) 976 continue 977 } else if !container.IsContainer() { 978 err = errors.Errorf("cannot allocate address for %q: not a container", tag) 979 result.Results[i].Error = common.ServerError(err) 980 continue 981 } else if ciid, cerr := container.InstanceId(); provisionContainer == true && cerr == nil { 982 // Since we want to configure and create NICs on the 983 // container before it starts, it must also be not 984 // provisioned yet. 985 err = errors.Errorf("container %q already provisioned as %q", container, ciid) 986 result.Results[i].Error = common.ServerError(err) 987 continue 988 } else if cerr != nil && !errors.IsNotProvisioned(cerr) { 989 // Any other error needs to be reported. 990 result.Results[i].Error = common.ServerError(cerr) 991 continue 992 } 993 994 var macAddress string 995 var address *state.IPAddress 996 if provisionContainer { 997 // Allocate and set address. 998 macAddress = generateMACAddress() 999 address, err = p.allocateAddress(environ, subnet, host, container, instId, macAddress) 1000 if err != nil { 1001 err = errors.Annotatef(err, "failed to allocate an address for %q", container) 1002 result.Results[i].Error = common.ServerError(err) 1003 continue 1004 } 1005 } else { 1006 id := container.Id() 1007 addresses, err := p.st.AllocatedIPAddresses(id) 1008 if err != nil { 1009 logger.Warningf("failed to get Id for container %q: %v", tag, err) 1010 result.Results[i].Error = common.ServerError(err) 1011 continue 1012 } 1013 // TODO(dooferlad): if we get more than 1 address back, we ignore everything after 1014 // the first. The calling function expects exactly one result though, 1015 // so we don't appear to have a way of allocating >1 address to a 1016 // container... 1017 if len(addresses) != 1 { 1018 logger.Warningf("got %d addresses for container %q - expected 1: %v", len(addresses), tag, err) 1019 result.Results[i].Error = common.ServerError(err) 1020 continue 1021 } 1022 address = addresses[0] 1023 macAddress = address.MACAddress() 1024 } 1025 // Store it on the machine, construct and set an interface result. 1026 dnsServers := make([]string, len(interfaceInfo.DNSServers)) 1027 for i, dns := range interfaceInfo.DNSServers { 1028 dnsServers[i] = dns.Value 1029 } 1030 1031 if macAddress == "" { 1032 macAddress = interfaceInfo.MACAddress 1033 } 1034 // TODO(dimitern): Support allocating one address per NIC on 1035 // the host, effectively creating the same number of NICs in 1036 // the container. 1037 result.Results[i] = params.MachineNetworkConfigResult{ 1038 Config: []params.NetworkConfig{{ 1039 DeviceIndex: interfaceInfo.DeviceIndex, 1040 MACAddress: macAddress, 1041 CIDR: subnetInfo.CIDR, 1042 NetworkName: interfaceInfo.NetworkName, 1043 ProviderId: string(interfaceInfo.ProviderId), 1044 ProviderSubnetId: string(subnetInfo.ProviderId), 1045 VLANTag: interfaceInfo.VLANTag, 1046 InterfaceName: interfaceInfo.InterfaceName, 1047 Disabled: interfaceInfo.Disabled, 1048 NoAutoStart: interfaceInfo.NoAutoStart, 1049 DNSServers: dnsServers, 1050 ConfigType: string(network.ConfigStatic), 1051 Address: address.Value(), 1052 // container's gateway is the host's primary NIC's IP. 1053 GatewayAddress: interfaceInfo.Address.Value, 1054 ExtraConfig: interfaceInfo.ExtraConfig, 1055 }}, 1056 } 1057 } 1058 return result, nil 1059 } 1060 1061 // prepareContainerAccessEnvironment retrieves the environment, host machine, and access 1062 // for working with containers. 1063 func (p *ProvisionerAPI) prepareContainerAccessEnvironment() (environs.NetworkingEnviron, *state.Machine, common.AuthFunc, error) { 1064 cfg, err := p.st.EnvironConfig() 1065 if err != nil { 1066 return nil, nil, nil, errors.Annotate(err, "failed to get environment config") 1067 } 1068 environ, err := environs.New(cfg) 1069 if err != nil { 1070 return nil, nil, nil, errors.Annotate(err, "failed to construct an environment from config") 1071 } 1072 netEnviron, supported := environs.SupportsNetworking(environ) 1073 if !supported { 1074 // " not supported" will be appended to the message below. 1075 return nil, nil, nil, errors.NotSupportedf("environment %q networking", cfg.Name()) 1076 } 1077 1078 canAccess, err := p.getAuthFunc() 1079 if err != nil { 1080 return nil, nil, nil, errors.Annotate(err, "cannot authenticate request") 1081 } 1082 hostAuthTag := p.authorizer.GetAuthTag() 1083 if hostAuthTag == nil { 1084 return nil, nil, nil, errors.Errorf("authenticated entity tag is nil") 1085 } 1086 hostTag, err := names.ParseMachineTag(hostAuthTag.String()) 1087 if err != nil { 1088 return nil, nil, nil, errors.Trace(err) 1089 } 1090 host, err := p.getMachine(canAccess, hostTag) 1091 if err != nil { 1092 return nil, nil, nil, errors.Trace(err) 1093 } 1094 return netEnviron, host, canAccess, nil 1095 } 1096 1097 // prepareAllocationNetwork retrieves the subnet, its info, and the interface info 1098 // for the allocations. 1099 func (p *ProvisionerAPI) prepareAllocationNetwork( 1100 environ environs.NetworkingEnviron, 1101 host *state.Machine, 1102 instId instance.Id, 1103 ) ( 1104 *state.Subnet, 1105 network.SubnetInfo, 1106 network.InterfaceInfo, 1107 error, 1108 ) { 1109 var subnetInfo network.SubnetInfo 1110 var interfaceInfo network.InterfaceInfo 1111 1112 interfaces, err := environ.NetworkInterfaces(instId) 1113 if err != nil { 1114 return nil, subnetInfo, interfaceInfo, errors.Trace(err) 1115 } else if len(interfaces) == 0 { 1116 return nil, subnetInfo, interfaceInfo, errors.Errorf("no interfaces available") 1117 } 1118 logger.Tracef("interfaces for instance %q: %v", instId, interfaces) 1119 1120 subnetSet := make(set.Strings) 1121 subnetIds := []network.Id{} 1122 subnetIdToInterface := make(map[network.Id]network.InterfaceInfo) 1123 for _, iface := range interfaces { 1124 if iface.ProviderSubnetId == "" { 1125 logger.Debugf("no subnet associated with interface %#v (skipping)", iface) 1126 continue 1127 } else if iface.Disabled { 1128 logger.Debugf("interface %#v disabled (skipping)", iface) 1129 continue 1130 } 1131 if !subnetSet.Contains(string(iface.ProviderSubnetId)) { 1132 subnetIds = append(subnetIds, iface.ProviderSubnetId) 1133 subnetSet.Add(string(iface.ProviderSubnetId)) 1134 1135 // This means that multiple interfaces on the same subnet will 1136 // only appear once. 1137 subnetIdToInterface[iface.ProviderSubnetId] = iface 1138 } 1139 } 1140 subnets, err := environ.Subnets(instId, subnetIds) 1141 if err != nil { 1142 return nil, subnetInfo, interfaceInfo, errors.Trace(err) 1143 } else if len(subnets) == 0 { 1144 return nil, subnetInfo, interfaceInfo, errors.Errorf("no subnets available") 1145 } 1146 logger.Tracef("subnets for instance %q: %v", instId, subnets) 1147 1148 // TODO(mfoord): we need a better strategy for picking a subnet to 1149 // allocate an address on. (dimitern): Right now we just pick the 1150 // first subnet with allocatable range set. Instead, we should 1151 // allocate an address per interface, assuming each interface is 1152 // on a subnet with allocatable range set, and skipping those 1153 // which do not have a range set. 1154 var success bool 1155 for _, sub := range subnets { 1156 logger.Tracef("trying to allocate a static IP on subnet %q", sub.ProviderId) 1157 if sub.AllocatableIPHigh == nil { 1158 logger.Tracef("ignoring subnet %q - no allocatable range set", sub.ProviderId) 1159 // this subnet has no allocatable IPs 1160 continue 1161 } 1162 ok, err := environ.SupportsAddressAllocation(sub.ProviderId) 1163 if err == nil && ok { 1164 subnetInfo = sub 1165 interfaceInfo = subnetIdToInterface[sub.ProviderId] 1166 success = true 1167 break 1168 } 1169 logger.Tracef( 1170 "subnet %q supports address allocation: %v (error: %v)", 1171 sub.ProviderId, ok, err, 1172 ) 1173 } 1174 if !success { 1175 // " not supported" will be appended to the message below. 1176 return nil, subnetInfo, interfaceInfo, errors.NotSupportedf( 1177 "address allocation on any available subnets is", 1178 ) 1179 } 1180 subnet, err := p.createOrFetchStateSubnet(subnetInfo) 1181 1182 return subnet, subnetInfo, interfaceInfo, nil 1183 } 1184 1185 // These are defined like this to allow mocking in tests. 1186 var ( 1187 allocateAddrTo = func(a *state.IPAddress, m *state.Machine, macAddress string) error { 1188 // TODO(mfoord): populate proper interface ID (in state). 1189 return a.AllocateTo(m.Id(), "", macAddress) 1190 } 1191 setAddrsTo = func(a *state.IPAddress, m *state.Machine) error { 1192 return m.SetProviderAddresses(a.Address()) 1193 } 1194 setAddrState = func(a *state.IPAddress, st state.AddressState) error { 1195 return a.SetState(st) 1196 } 1197 ) 1198 1199 // allocateAddress tries to pick an address out of the given subnet and 1200 // allocates it to the container. 1201 func (p *ProvisionerAPI) allocateAddress( 1202 environ environs.NetworkingEnviron, 1203 subnet *state.Subnet, 1204 host, container *state.Machine, 1205 instId instance.Id, 1206 macAddress string, 1207 ) (*state.IPAddress, error) { 1208 1209 subnetId := network.Id(subnet.ProviderId()) 1210 name := names.NewMachineTag(container.Id()).String() 1211 for { 1212 addr, err := subnet.PickNewAddress() 1213 if err != nil { 1214 return nil, err 1215 } 1216 logger.Tracef("picked new address %q on subnet %q", addr.String(), subnetId) 1217 // Attempt to allocate with environ. 1218 err = environ.AllocateAddress(instId, subnetId, addr.Address(), macAddress, name) 1219 if err != nil { 1220 logger.Warningf( 1221 "allocating address %q on instance %q and subnet %q failed: %v (retrying)", 1222 addr.String(), instId, subnetId, err, 1223 ) 1224 // It's as good as unavailable for us, so mark it as 1225 // such. 1226 err = setAddrState(addr, state.AddressStateUnavailable) 1227 if err != nil { 1228 logger.Warningf( 1229 "cannot set address %q to %q: %v (ignoring and retrying)", 1230 addr.String(), state.AddressStateUnavailable, err, 1231 ) 1232 continue 1233 } 1234 logger.Tracef( 1235 "setting address %q to %q and retrying", 1236 addr.String(), state.AddressStateUnavailable, 1237 ) 1238 continue 1239 } 1240 logger.Infof( 1241 "allocated address %q on instance %q and subnet %q", 1242 addr.String(), instId, subnetId, 1243 ) 1244 err = p.setAllocatedOrRelease(addr, environ, instId, container, subnetId, macAddress) 1245 if err != nil { 1246 // Something went wrong - retry. 1247 continue 1248 } 1249 return addr, nil 1250 } 1251 } 1252 1253 // setAllocatedOrRelease tries to associate the newly allocated 1254 // address addr with the container. On failure it makes the best 1255 // effort to cleanup and release addr, logging issues along the way. 1256 func (p *ProvisionerAPI) setAllocatedOrRelease( 1257 addr *state.IPAddress, 1258 environ environs.NetworkingEnviron, 1259 instId instance.Id, 1260 container *state.Machine, 1261 subnetId network.Id, 1262 macAddress string, 1263 ) (err error) { 1264 defer func() { 1265 if errors.Cause(err) == nil { 1266 // Success! 1267 return 1268 } 1269 logger.Warningf( 1270 "failed to mark address %q as %q to container %q: %v (releasing and retrying)", 1271 addr.String(), state.AddressStateAllocated, container, err, 1272 ) 1273 // It's as good as unavailable for us, so mark it as 1274 // such. 1275 err = setAddrState(addr, state.AddressStateUnavailable) 1276 if err != nil { 1277 logger.Warningf( 1278 "cannot set address %q to %q: %v (ignoring and releasing)", 1279 addr.String(), state.AddressStateUnavailable, err, 1280 ) 1281 } 1282 err = environ.ReleaseAddress(instId, subnetId, addr.Address(), addr.MACAddress()) 1283 if err == nil { 1284 logger.Infof("address %q released; trying to allocate new", addr.String()) 1285 return 1286 } 1287 logger.Warningf( 1288 "failed to release address %q on instance %q and subnet %q: %v (ignoring and retrying)", 1289 addr.String(), instId, subnetId, err, 1290 ) 1291 }() 1292 1293 // Any errors returned below will trigger the release/cleanup 1294 // steps above. 1295 if err = allocateAddrTo(addr, container, macAddress); err != nil { 1296 return errors.Trace(err) 1297 } 1298 if err = setAddrsTo(addr, container); err != nil { 1299 return errors.Trace(err) 1300 } 1301 1302 logger.Infof("assigned address %q to container %q", addr.String(), container) 1303 return nil 1304 } 1305 1306 func (p *ProvisionerAPI) createOrFetchStateSubnet(subnetInfo network.SubnetInfo) (*state.Subnet, error) { 1307 stateSubnetInfo := state.SubnetInfo{ 1308 ProviderId: string(subnetInfo.ProviderId), 1309 CIDR: subnetInfo.CIDR, 1310 VLANTag: subnetInfo.VLANTag, 1311 AllocatableIPHigh: subnetInfo.AllocatableIPHigh.String(), 1312 AllocatableIPLow: subnetInfo.AllocatableIPLow.String(), 1313 } 1314 subnet, err := p.st.AddSubnet(stateSubnetInfo) 1315 if err != nil { 1316 if errors.IsAlreadyExists(err) { 1317 subnet, err = p.st.Subnet(subnetInfo.CIDR) 1318 } 1319 if err != nil { 1320 return subnet, errors.Trace(err) 1321 } 1322 } 1323 return subnet, nil 1324 } 1325 1326 // machineTags returns machine-specific tags to set on the instance. 1327 func (p *ProvisionerAPI) machineTags(m *state.Machine, jobs []multiwatcher.MachineJob) (map[string]string, error) { 1328 // Names of all units deployed to the machine. 1329 // 1330 // TODO(axw) 2015-06-02 #1461358 1331 // We need a worker that periodically updates 1332 // instance tags with current deployment info. 1333 units, err := m.Units() 1334 if err != nil { 1335 return nil, errors.Trace(err) 1336 } 1337 unitNames := make([]string, 0, len(units)) 1338 for _, unit := range units { 1339 if !unit.IsPrincipal() { 1340 continue 1341 } 1342 unitNames = append(unitNames, unit.Name()) 1343 } 1344 sort.Strings(unitNames) 1345 1346 cfg, err := p.st.EnvironConfig() 1347 if err != nil { 1348 return nil, errors.Trace(err) 1349 } 1350 machineTags := instancecfg.InstanceTags(cfg, jobs) 1351 if len(unitNames) > 0 { 1352 machineTags[tags.JujuUnitsDeployed] = strings.Join(unitNames, " ") 1353 } 1354 return machineTags, nil 1355 } 1356 1357 // machineSubnetsAndZones returns a map of subnet provider-specific id 1358 // to list of availability zone names for that subnet. The result can 1359 // be empty if there are no spaces constraints specified for the 1360 // machine, or there's an error fetching them. 1361 func (p *ProvisionerAPI) machineSubnetsAndZones(m *state.Machine) (map[string][]string, error) { 1362 mcons, err := m.Constraints() 1363 if err != nil { 1364 return nil, errors.Annotate(err, "cannot get machine constraints") 1365 } 1366 includeSpaces := mcons.IncludeSpaces() 1367 if len(includeSpaces) < 1 { 1368 // Nothing to do. 1369 return nil, nil 1370 } 1371 // TODO(dimitern): For the network model MVP we only use the first 1372 // included space and ignore the rest. 1373 spaceName := includeSpaces[0] 1374 if len(includeSpaces) > 1 { 1375 logger.Debugf( 1376 "using space %q from constraints for machine %q (ignoring remaining: %v)", 1377 spaceName, m.Id(), includeSpaces[1:], 1378 ) 1379 } 1380 space, err := p.st.Space(spaceName) 1381 if err != nil { 1382 return nil, errors.Trace(err) 1383 } 1384 subnets, err := space.Subnets() 1385 if err != nil { 1386 return nil, errors.Trace(err) 1387 } 1388 subnetsToZones := make(map[string][]string, len(subnets)) 1389 for _, subnet := range subnets { 1390 warningPrefix := fmt.Sprintf( 1391 "not using subnet %q in space %q for machine %q provisioning: ", 1392 subnet.CIDR(), spaceName, m.Id(), 1393 ) 1394 // TODO(dimitern): state.Subnet.ProviderId needs to be of type 1395 // network.Id. 1396 providerId := subnet.ProviderId() 1397 if providerId == "" { 1398 logger.Warningf(warningPrefix + "no ProviderId set") 1399 continue 1400 } 1401 // TODO(dimitern): Once state.Subnet supports multiple zones, 1402 // use all of them below. 1403 zone := subnet.AvailabilityZone() 1404 if zone == "" { 1405 logger.Warningf(warningPrefix + "no availability zone(s) set") 1406 continue 1407 } 1408 subnetsToZones[providerId] = []string{zone} 1409 } 1410 return subnetsToZones, nil 1411 }