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  }