github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/provisioner/provisioner.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/version" 9 "gopkg.in/juju/names.v2" 10 11 "github.com/juju/juju/api/base" 12 "github.com/juju/juju/api/common" 13 apiwatcher "github.com/juju/juju/api/watcher" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/core/watcher" 16 "github.com/juju/juju/network" 17 "github.com/juju/juju/tools" 18 ) 19 20 // State provides access to the Machiner API facade. 21 type State struct { 22 *common.ModelWatcher 23 *common.APIAddresser 24 *common.ControllerConfigAPI 25 26 facade base.FacadeCaller 27 } 28 29 // MachineResult provides a found Machine and any Error related to 30 // finding it. 31 type MachineResult struct { 32 Machine MachineProvisioner 33 Err *params.Error 34 } 35 36 // MachineStatusResult provides a found Machine and Status Results 37 // for it. 38 type MachineStatusResult struct { 39 Machine MachineProvisioner 40 Status params.StatusResult 41 } 42 43 // DistributionGroupResult provides a slice of machine.Ids in the 44 // distribution group and any Error related to finding it. 45 type DistributionGroupResult struct { 46 MachineIds []string 47 Err *params.Error 48 } 49 50 // LXDProfileResult provides a charm.LXDProfile, adding the name. 51 type LXDProfileResult struct { 52 Config map[string]string `json:"config" yaml:"config"` 53 Description string `json:"description" yaml:"description"` 54 Devices map[string]map[string]string `json:"devices" yaml:"devices"` 55 Name string `json:"name" yaml:"name"` 56 } 57 58 const provisionerFacade = "Provisioner" 59 60 // NewState creates a new provisioner facade using the input caller. 61 func NewState(caller base.APICaller) *State { 62 facadeCaller := base.NewFacadeCaller(caller, provisionerFacade) 63 return NewStateFromFacade(facadeCaller) 64 } 65 66 // NewStateFromFacade creates a new provisioner facade using the input 67 // facade caller. 68 func NewStateFromFacade(facadeCaller base.FacadeCaller) *State { 69 return &State{ 70 ModelWatcher: common.NewModelWatcher(facadeCaller), 71 APIAddresser: common.NewAPIAddresser(facadeCaller), 72 ControllerConfigAPI: common.NewControllerConfig(facadeCaller), 73 facade: facadeCaller, 74 } 75 } 76 77 // machineLife requests the lifecycle of the given machine from the server. 78 func (st *State) machineLife(tag names.MachineTag) (params.Life, error) { 79 return common.OneLife(st.facade, tag) 80 } 81 82 // Machine provides access to methods of a state.Machine through the facade 83 // for the given tags. 84 func (st *State) Machines(tags ...names.MachineTag) ([]MachineResult, error) { 85 lenTags := len(tags) 86 genericTags := make([]names.Tag, lenTags) 87 for i, t := range tags { 88 genericTags[i] = t 89 } 90 result, err := common.Life(st.facade, genericTags) 91 if err != nil { 92 return []MachineResult{}, err 93 } 94 machines := make([]MachineResult, lenTags) 95 for i, r := range result { 96 if r.Error == nil { 97 machines[i].Machine = &Machine{ 98 tag: tags[i], 99 life: r.Life, 100 st: st, 101 } 102 } else { 103 machines[i].Err = r.Error 104 } 105 } 106 return machines, nil 107 } 108 109 // WatchModelMachines returns a StringsWatcher that notifies of 110 // changes to the lifecycles of the machines (but not containers) in 111 // the current model. 112 func (st *State) WatchModelMachines() (watcher.StringsWatcher, error) { 113 var result params.StringsWatchResult 114 err := st.facade.FacadeCall("WatchModelMachines", nil, &result) 115 if err != nil { 116 return nil, err 117 } 118 if err := result.Error; err != nil { 119 return nil, result.Error 120 } 121 w := apiwatcher.NewStringsWatcher(st.facade.RawAPICaller(), result) 122 return w, nil 123 } 124 125 func (st *State) WatchMachineErrorRetry() (watcher.NotifyWatcher, error) { 126 var result params.NotifyWatchResult 127 err := st.facade.FacadeCall("WatchMachineErrorRetry", nil, &result) 128 if err != nil { 129 return nil, err 130 } 131 if err := result.Error; err != nil { 132 return nil, result.Error 133 } 134 w := apiwatcher.NewNotifyWatcher(st.facade.RawAPICaller(), result) 135 return w, nil 136 } 137 138 // WatchModelMachinesCharmProfiles returns a StringsWatcher that notifies of 139 // changes to the upgrade charm profile charm url for all non container machines 140 // in the current model. 141 func (st *State) WatchModelMachinesCharmProfiles() (watcher.StringsWatcher, error) { 142 var result params.StringsWatchResult 143 err := st.facade.FacadeCall("WatchModelMachinesCharmProfiles", nil, &result) 144 if err != nil { 145 return nil, err 146 } 147 if err := result.Error; err != nil { 148 return nil, result.Error 149 } 150 w := apiwatcher.NewStringsWatcher(st.facade.RawAPICaller(), result) 151 return w, nil 152 } 153 154 // StateAddresses returns the list of addresses used to connect to the state. 155 func (st *State) StateAddresses() ([]string, error) { 156 var result params.StringsResult 157 err := st.facade.FacadeCall("StateAddresses", nil, &result) 158 if err != nil { 159 return nil, err 160 } 161 return result.Result, nil 162 } 163 164 // ContainerManagerConfig returns information from the model config that is 165 // needed for configuring the container manager. 166 func (st *State) ContainerManagerConfig(args params.ContainerManagerConfigParams) (result params.ContainerManagerConfig, err error) { 167 err = st.facade.FacadeCall("ContainerManagerConfig", args, &result) 168 return result, err 169 } 170 171 // ContainerConfig returns information from the model config that is 172 // needed for container cloud-init. 173 func (st *State) ContainerConfig() (result params.ContainerConfig, err error) { 174 if st.facade.BestAPIVersion() < 6 { 175 return st.containerConfigV5() 176 } 177 err = st.facade.FacadeCall("ContainerConfig", nil, &result) 178 return result, err 179 } 180 181 func (st *State) containerConfigV5() (params.ContainerConfig, error) { 182 var result params.ContainerConfigV5 183 if err := st.facade.FacadeCall("ContainerConfig", nil, &result); err != nil { 184 return params.ContainerConfig{}, err 185 } 186 return params.ContainerConfig{ 187 ProviderType: result.ProviderType, 188 AuthorizedKeys: result.AuthorizedKeys, 189 SSLHostnameVerification: result.SSLHostnameVerification, 190 LegacyProxy: result.Proxy, 191 AptProxy: result.AptProxy, 192 // JujuProxy is zero value. 193 // SnapProxy is zero value, 194 AptMirror: result.AptMirror, 195 CloudInitUserData: result.CloudInitUserData, 196 ContainerInheritProperties: result.ContainerInheritProperties, 197 UpdateBehavior: result.UpdateBehavior, 198 }, nil 199 } 200 201 // MachinesWithTransientErrors returns a slice of machines and corresponding status information 202 // for those machines which have transient provisioning errors. 203 func (st *State) MachinesWithTransientErrors() ([]MachineStatusResult, error) { 204 var results params.StatusResults 205 err := st.facade.FacadeCall("MachinesWithTransientErrors", nil, &results) 206 if err != nil { 207 return []MachineStatusResult{}, err 208 } 209 machines := make([]MachineStatusResult, len(results.Results)) 210 for i, status := range results.Results { 211 if status.Error != nil { 212 continue 213 } 214 machines[i].Machine = &Machine{ 215 tag: names.NewMachineTag(status.Id), 216 life: status.Life, 217 st: st, 218 } 219 machines[i].Status = status 220 } 221 return machines, nil 222 } 223 224 // FindTools returns al ist of tools matching the specified version number and 225 // series, and, arch. If arch is blank, a default will be used. 226 func (st *State) FindTools(v version.Number, series string, arch string) (tools.List, error) { 227 args := params.FindToolsParams{ 228 Number: v, 229 Series: series, 230 MajorVersion: -1, 231 MinorVersion: -1, 232 } 233 if arch != "" { 234 args.Arch = arch 235 } 236 var result params.FindToolsResult 237 if err := st.facade.FacadeCall("FindTools", args, &result); err != nil { 238 return nil, err 239 } 240 if result.Error != nil { 241 return nil, result.Error 242 } 243 return result.List, nil 244 } 245 246 // ReleaseContainerAddresses releases a static IP address allocated to a 247 // container. 248 func (st *State) ReleaseContainerAddresses(containerTag names.MachineTag) (err error) { 249 defer errors.DeferredAnnotatef(&err, "cannot release static addresses for %q", containerTag.Id()) 250 var result params.ErrorResults 251 args := params.Entities{ 252 Entities: []params.Entity{{Tag: containerTag.String()}}, 253 } 254 if err := st.facade.FacadeCall("ReleaseContainerAddresses", args, &result); err != nil { 255 return err 256 } 257 return result.OneError() 258 } 259 260 // PrepareContainerInterfaceInfo allocates an address and returns information to 261 // configure networking for a container. It accepts container tags as arguments. 262 func (st *State) PrepareContainerInterfaceInfo(containerTag names.MachineTag) ([]network.InterfaceInfo, error) { 263 return st.prepareOrGetContainerInterfaceInfo(containerTag, true) 264 } 265 266 // GetContainerInterfaceInfo returns information to configure networking 267 // for a container. It accepts container tags as arguments. 268 func (st *State) GetContainerInterfaceInfo(containerTag names.MachineTag) ([]network.InterfaceInfo, error) { 269 return st.prepareOrGetContainerInterfaceInfo(containerTag, false) 270 } 271 272 // prepareOrGetContainerInterfaceInfo returns the necessary information to 273 // configure network interfaces of a container with allocated static 274 // IP addresses. 275 // 276 // TODO(dimitern): Before we start using this, we need to rename both 277 // the method and the network.InterfaceInfo type to be called 278 // InterfaceConfig. 279 func (st *State) prepareOrGetContainerInterfaceInfo( 280 containerTag names.MachineTag, allocateNewAddress bool) ( 281 []network.InterfaceInfo, error) { 282 var result params.MachineNetworkConfigResults 283 args := params.Entities{ 284 Entities: []params.Entity{{Tag: containerTag.String()}}, 285 } 286 methodName := "" 287 if allocateNewAddress { 288 methodName = "PrepareContainerInterfaceInfo" 289 } else { 290 methodName = "GetContainerInterfaceInfo" 291 } 292 if err := st.facade.FacadeCall(methodName, args, &result); err != nil { 293 return nil, err 294 } 295 if len(result.Results) != 1 { 296 return nil, errors.Errorf("expected 1 result, got %d", len(result.Results)) 297 } 298 if err := result.Results[0].Error; err != nil { 299 return nil, err 300 } 301 machineConf := result.Results[0] 302 ifaceInfo := make([]network.InterfaceInfo, len(machineConf.Config)) 303 for i, cfg := range machineConf.Config { 304 routes := make([]network.Route, len(cfg.Routes)) 305 for j, route := range cfg.Routes { 306 routes[j] = network.Route{ 307 DestinationCIDR: route.DestinationCIDR, 308 GatewayIP: route.GatewayIP, 309 Metric: route.Metric, 310 } 311 } 312 ifaceInfo[i] = network.InterfaceInfo{ 313 DeviceIndex: cfg.DeviceIndex, 314 MACAddress: cfg.MACAddress, 315 CIDR: cfg.CIDR, 316 MTU: cfg.MTU, 317 ProviderId: network.Id(cfg.ProviderId), 318 ProviderSubnetId: network.Id(cfg.ProviderSubnetId), 319 ProviderSpaceId: network.Id(cfg.ProviderSpaceId), 320 ProviderVLANId: network.Id(cfg.ProviderVLANId), 321 ProviderAddressId: network.Id(cfg.ProviderAddressId), 322 VLANTag: cfg.VLANTag, 323 InterfaceName: cfg.InterfaceName, 324 ParentInterfaceName: cfg.ParentInterfaceName, 325 InterfaceType: network.InterfaceType(cfg.InterfaceType), 326 Disabled: cfg.Disabled, 327 NoAutoStart: cfg.NoAutoStart, 328 ConfigType: network.InterfaceConfigType(cfg.ConfigType), 329 Address: network.NewAddress(cfg.Address), 330 DNSServers: network.NewAddresses(cfg.DNSServers...), 331 DNSSearchDomains: cfg.DNSSearchDomains, 332 GatewayAddress: network.NewAddress(cfg.GatewayAddress), 333 Routes: routes, 334 } 335 } 336 return ifaceInfo, nil 337 } 338 339 // SetHostMachineNetworkConfig sets the network configuration of the 340 // machine with netConfig 341 func (st *State) SetHostMachineNetworkConfig(hostMachineTag names.MachineTag, netConfig []params.NetworkConfig) error { 342 args := params.SetMachineNetworkConfig{ 343 Tag: hostMachineTag.String(), 344 Config: netConfig, 345 } 346 err := st.facade.FacadeCall("SetHostMachineNetworkConfig", args, nil) 347 if err != nil { 348 return errors.Trace(err) 349 } 350 return nil 351 } 352 353 func (st *State) HostChangesForContainer(containerTag names.MachineTag) ([]network.DeviceToBridge, int, error) { 354 var result params.HostNetworkChangeResults 355 args := params.Entities{ 356 Entities: []params.Entity{{Tag: containerTag.String()}}, 357 } 358 if err := st.facade.FacadeCall("HostChangesForContainers", args, &result); err != nil { 359 return nil, 0, err 360 } 361 if len(result.Results) != 1 { 362 return nil, 0, errors.Errorf("expected 1 result, got %d", len(result.Results)) 363 } 364 if err := result.Results[0].Error; err != nil { 365 return nil, 0, err 366 } 367 newBridges := result.Results[0].NewBridges 368 res := make([]network.DeviceToBridge, len(newBridges)) 369 for i, bridgeInfo := range newBridges { 370 res[i].BridgeName = bridgeInfo.BridgeName 371 res[i].DeviceName = bridgeInfo.HostDeviceName 372 res[i].MACAddress = bridgeInfo.MACAddress 373 } 374 return res, result.Results[0].ReconfigureDelay, nil 375 } 376 377 // DistributionGroupByMachineId returns a slice of machine.Ids 378 // that belong to the same distribution group as the given 379 // Machine. The provisioner may use this information 380 // to distribute instances for high availability. 381 func (st *State) DistributionGroupByMachineId(tags ...names.MachineTag) ([]DistributionGroupResult, error) { 382 var stringResults params.StringsResults 383 entities := make([]params.Entity, len(tags)) 384 for i, t := range tags { 385 entities[i] = params.Entity{Tag: t.String()} 386 } 387 err := st.facade.FacadeCall("DistributionGroupByMachineId", params.Entities{Entities: entities}, &stringResults) 388 if err != nil { 389 return []DistributionGroupResult{}, err 390 } 391 results := make([]DistributionGroupResult, len(tags)) 392 for i, stringResult := range stringResults.Results { 393 results[i] = DistributionGroupResult{MachineIds: stringResult.Result, Err: stringResult.Error} 394 } 395 return results, nil 396 } 397 398 // CACert returns the certificate used to validate the API and state connections. 399 func (a *State) CACert() (string, error) { 400 var result params.BytesResult 401 err := a.facade.FacadeCall("CACert", nil, &result) 402 if err != nil { 403 return "", err 404 } 405 return string(result.Result), nil 406 } 407 408 // GetContainerProfileInfo returns a slice of ContainerLXDProfile, 1 for each unit's charm 409 // which contains an lxd-profile.yaml. 410 func (st *State) GetContainerProfileInfo(containerTag names.MachineTag) ([]*LXDProfileResult, error) { 411 var result params.ContainerProfileResults 412 args := params.Entities{ 413 Entities: []params.Entity{{Tag: containerTag.String()}}, 414 } 415 if err := st.facade.FacadeCall("GetContainerProfileInfo", args, &result); err != nil { 416 return nil, err 417 } 418 if len(result.Results) != 1 { 419 return nil, errors.Errorf("expected 1 result, got %d", len(result.Results)) 420 } 421 if err := result.Results[0].Error; err != nil { 422 return nil, err 423 } 424 profiles := result.Results[0].LXDProfiles 425 var res []*LXDProfileResult 426 for _, p := range profiles { 427 if p == nil { 428 continue 429 } 430 res = append(res, &LXDProfileResult{ 431 Config: p.Profile.Config, 432 Description: p.Profile.Description, 433 Devices: p.Profile.Devices, 434 Name: p.Name, 435 }) 436 } 437 return res, nil 438 }