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