github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/provisioner/kvm-broker.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/loggo" 9 "gopkg.in/juju/names.v2" 10 11 "github.com/juju/juju/agent" 12 "github.com/juju/juju/cloudconfig/instancecfg" 13 "github.com/juju/juju/container" 14 "github.com/juju/juju/core/instance" 15 "github.com/juju/juju/environs" 16 "github.com/juju/juju/environs/context" 17 "github.com/juju/juju/environs/instances" 18 "github.com/juju/juju/network" 19 ) 20 21 var kvmLogger = loggo.GetLogger("juju.provisioner.kvm") 22 23 // NewKVMBroker creates a Broker that can be used to start KVM guests in a 24 // similar fashion to normal StartInstance requests. 25 // prepareHost is a callback that will be called when a new container is about 26 // to be started. It provides the intersection point where the host can update 27 // itself to be ready for whatever changes are necessary to have a functioning 28 // container. (such as bridging host devices.) 29 // manager is the infrastructure to actually launch the container. 30 // agentConfig is currently only used to find out the 'default' bridge to use 31 // when a specific network device is not specified in StartInstanceParams. This 32 // should be deprecated. And hopefully removed in the future. 33 func NewKVMBroker( 34 prepareHost PrepareHostFunc, 35 api APICalls, 36 manager container.Manager, 37 agentConfig agent.Config, 38 ) (environs.InstanceBroker, error) { 39 return &kvmBroker{ 40 prepareHost: prepareHost, 41 manager: manager, 42 api: api, 43 agentConfig: agentConfig, 44 }, nil 45 } 46 47 type kvmBroker struct { 48 prepareHost PrepareHostFunc 49 manager container.Manager 50 api APICalls 51 agentConfig agent.Config 52 } 53 54 // StartInstance is specified in the Broker interface. 55 func (broker *kvmBroker) StartInstance(ctx context.ProviderCallContext, args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { 56 // TODO: refactor common code out of the container brokers. 57 containerMachineID := args.InstanceConfig.MachineId 58 kvmLogger.Infof("starting kvm container for containerMachineID: %s", containerMachineID) 59 60 // TODO: Default to using the host network until we can configure. Yes, 61 // this is using the LxcBridge value, we should put it in the api call for 62 // container config. 63 bridgeDevice := broker.agentConfig.Value(agent.LxcBridge) 64 if bridgeDevice == "" { 65 bridgeDevice = network.DefaultKVMBridge 66 } 67 68 config, err := broker.api.ContainerConfig() 69 if err != nil { 70 kvmLogger.Errorf("failed to get container config: %v", err) 71 return nil, err 72 } 73 74 err = broker.prepareHost(names.NewMachineTag(containerMachineID), kvmLogger, args.Abort) 75 if err != nil { 76 return nil, errors.Trace(err) 77 } 78 79 preparedInfo, err := prepareOrGetContainerInterfaceInfo( 80 broker.api, 81 containerMachineID, 82 true, // allocate if possible, do not maintain existing. 83 kvmLogger, 84 ) 85 if err != nil { 86 return nil, errors.Trace(err) 87 } 88 89 // Something to fallback to if there are no devices given in args.NetworkInfo 90 // TODO(jam): 2017-02-07, this feels like something that should never need 91 // to be invoked, because either StartInstance or 92 // prepareOrGetContainerInterfaceInfo should always return a value. The 93 // test suite currently doesn't think so, and I'm hesitant to munge it too 94 // much. 95 interfaces, err := finishNetworkConfig(bridgeDevice, preparedInfo) 96 if err != nil { 97 return nil, errors.Trace(err) 98 } 99 network := container.BridgeNetworkConfig(bridgeDevice, 0, interfaces) 100 101 // The provisioner worker will provide all tools it knows about 102 // (after applying explicitly specified constraints), which may 103 // include tools for architectures other than the host's. 104 // 105 // container/kvm only allows running container==host arch, so 106 // we constrain the tools to host arch here regardless of the 107 // constraints specified. 108 archTools, err := matchHostArchTools(args.Tools) 109 if err != nil { 110 return nil, errors.Trace(err) 111 } 112 113 series := archTools.OneSeries() 114 args.InstanceConfig.MachineContainerType = instance.KVM 115 if err := args.InstanceConfig.SetTools(archTools); err != nil { 116 return nil, errors.Trace(err) 117 } 118 119 cloudInitUserData, err := combinedCloudInitData( 120 config.CloudInitUserData, 121 config.ContainerInheritProperties, 122 series, kvmLogger) 123 if err != nil { 124 return nil, errors.Trace(err) 125 } 126 127 if err := instancecfg.PopulateInstanceConfig( 128 args.InstanceConfig, 129 config.ProviderType, 130 config.AuthorizedKeys, 131 config.SSLHostnameVerification, 132 config.LegacyProxy, 133 config.JujuProxy, 134 config.AptProxy, 135 config.AptMirror, 136 config.EnableOSRefreshUpdate, 137 config.EnableOSUpgrade, 138 cloudInitUserData, 139 nil, 140 ); err != nil { 141 kvmLogger.Errorf("failed to populate machine config: %v", err) 142 return nil, err 143 } 144 145 storageConfig := &container.StorageConfig{ 146 AllowMount: true, 147 } 148 inst, hardware, err := broker.manager.CreateContainer( 149 args.InstanceConfig, args.Constraints, 150 series, network, storageConfig, args.StatusCallback, 151 ) 152 if err != nil { 153 kvmLogger.Errorf("failed to start container: %v", err) 154 return nil, err 155 } 156 kvmLogger.Infof("started kvm container for containerMachineID: %s, %s, %s", containerMachineID, inst.Id(), hardware.String()) 157 return &environs.StartInstanceResult{ 158 Instance: inst, 159 Hardware: hardware, 160 NetworkInfo: interfaces, 161 }, nil 162 } 163 164 // MaintainInstance ensures the container's host has the required iptables and 165 // routing rules to make the container visible to both the host and other 166 // machines on the same subnet. 167 func (broker *kvmBroker) MaintainInstance(ctx context.ProviderCallContext, args environs.StartInstanceParams) error { 168 machineID := args.InstanceConfig.MachineId 169 170 // There's no InterfaceInfo we expect to get below. 171 _, err := prepareOrGetContainerInterfaceInfo( 172 broker.api, 173 machineID, 174 false, // maintain, do not allocate. 175 kvmLogger, 176 ) 177 return err 178 } 179 180 // StopInstances shuts down the given instances. 181 func (broker *kvmBroker) StopInstances(ctx context.ProviderCallContext, ids ...instance.Id) error { 182 // TODO: potentially parallelise. 183 for _, id := range ids { 184 kvmLogger.Infof("stopping kvm container for instance: %s", id) 185 if err := broker.manager.DestroyContainer(id); err != nil { 186 kvmLogger.Errorf("container did not stop: %v", err) 187 return err 188 } 189 releaseContainerAddresses(broker.api, id, broker.manager.Namespace(), kvmLogger) 190 } 191 return nil 192 } 193 194 // AllInstances only returns running containers. 195 func (broker *kvmBroker) AllInstances(ctx context.ProviderCallContext) (result []instances.Instance, err error) { 196 return broker.manager.ListContainers() 197 }