github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  
    10  	"github.com/juju/juju/agent"
    11  	"github.com/juju/juju/cloudconfig/instancecfg"
    12  	"github.com/juju/juju/container"
    13  	"github.com/juju/juju/container/kvm"
    14  	"github.com/juju/juju/environs"
    15  	"github.com/juju/juju/instance"
    16  )
    17  
    18  var kvmLogger = loggo.GetLogger("juju.provisioner.kvm")
    19  
    20  var _ environs.InstanceBroker = (*kvmBroker)(nil)
    21  
    22  func NewKvmBroker(
    23  	api APICalls,
    24  	agentConfig agent.Config,
    25  	managerConfig container.ManagerConfig,
    26  	enableNAT bool,
    27  ) (environs.InstanceBroker, error) {
    28  	namespace := maybeGetManagerConfigNamespaces(managerConfig)
    29  	manager, err := kvm.NewContainerManager(managerConfig)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	return &kvmBroker{
    34  		manager:     manager,
    35  		namespace:   namespace,
    36  		api:         api,
    37  		agentConfig: agentConfig,
    38  		enableNAT:   enableNAT,
    39  	}, nil
    40  }
    41  
    42  type kvmBroker struct {
    43  	manager     container.Manager
    44  	namespace   string
    45  	api         APICalls
    46  	agentConfig agent.Config
    47  	enableNAT   bool
    48  }
    49  
    50  // StartInstance is specified in the Broker interface.
    51  func (broker *kvmBroker) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) {
    52  	// TODO: refactor common code out of the container brokers.
    53  	machineId := args.InstanceConfig.MachineId
    54  	kvmLogger.Infof("starting kvm container for machineId: %s", machineId)
    55  
    56  	// TODO: Default to using the host network until we can configure.  Yes,
    57  	// this is using the LxcBridge value, we should put it in the api call for
    58  	// container config.
    59  	bridgeDevice := broker.agentConfig.Value(agent.LxcBridge)
    60  	if bridgeDevice == "" {
    61  		bridgeDevice = container.DefaultKvmBridge
    62  	}
    63  
    64  	config, err := broker.api.ContainerConfig()
    65  	if err != nil {
    66  		kvmLogger.Errorf("failed to get container config: %v", err)
    67  		return nil, err
    68  	}
    69  
    70  	preparedInfo, err := prepareOrGetContainerInterfaceInfo(
    71  		broker.api,
    72  		machineId,
    73  		bridgeDevice,
    74  		true, // allocate if possible, do not maintain existing.
    75  		broker.enableNAT,
    76  		args.NetworkInfo,
    77  		kvmLogger,
    78  		config.ProviderType,
    79  	)
    80  	if err != nil {
    81  		// It's not fatal (yet) if we couldn't pre-allocate addresses for the
    82  		// container.
    83  		logger.Warningf("failed to prepare container %q network config: %v", machineId, err)
    84  	} else {
    85  		args.NetworkInfo = preparedInfo
    86  	}
    87  
    88  	// Unlike with LXC, we don't override the default MTU to use.
    89  	network := container.BridgeNetworkConfig(bridgeDevice, 0, args.NetworkInfo)
    90  
    91  	// The provisioner worker will provide all tools it knows about
    92  	// (after applying explicitly specified constraints), which may
    93  	// include tools for architectures other than the host's.
    94  	//
    95  	// container/kvm only allows running container==host arch, so
    96  	// we constrain the tools to host arch here regardless of the
    97  	// constraints specified.
    98  	archTools, err := matchHostArchTools(args.Tools)
    99  	if err != nil {
   100  		return nil, errors.Trace(err)
   101  	}
   102  
   103  	series := archTools.OneSeries()
   104  	args.InstanceConfig.MachineContainerType = instance.KVM
   105  	if err := args.InstanceConfig.SetTools(archTools); err != nil {
   106  		return nil, errors.Trace(err)
   107  	}
   108  
   109  	if err := instancecfg.PopulateInstanceConfig(
   110  		args.InstanceConfig,
   111  		config.ProviderType,
   112  		config.AuthorizedKeys,
   113  		config.SSLHostnameVerification,
   114  		config.Proxy,
   115  		config.AptProxy,
   116  		config.AptMirror,
   117  		config.PreferIPv6,
   118  		config.EnableOSRefreshUpdate,
   119  		config.EnableOSUpgrade,
   120  	); err != nil {
   121  		kvmLogger.Errorf("failed to populate machine config: %v", err)
   122  		return nil, err
   123  	}
   124  
   125  	storageConfig := &container.StorageConfig{
   126  		AllowMount: true,
   127  	}
   128  	inst, hardware, err := broker.manager.CreateContainer(args.InstanceConfig, series, network, storageConfig, args.StatusCallback)
   129  	if err != nil {
   130  		kvmLogger.Errorf("failed to start container: %v", err)
   131  		return nil, err
   132  	}
   133  	kvmLogger.Infof("started kvm container for machineId: %s, %s, %s", machineId, inst.Id(), hardware.String())
   134  	return &environs.StartInstanceResult{
   135  		Instance:    inst,
   136  		Hardware:    hardware,
   137  		NetworkInfo: network.Interfaces,
   138  	}, nil
   139  }
   140  
   141  // MaintainInstance ensures the container's host has the required iptables and
   142  // routing rules to make the container visible to both the host and other
   143  // machines on the same subnet. This is important mostly when address allocation
   144  // feature flag is enabled, as otherwise we don't create additional iptables
   145  // rules or routes.
   146  func (broker *kvmBroker) MaintainInstance(args environs.StartInstanceParams) error {
   147  	machineID := args.InstanceConfig.MachineId
   148  
   149  	// Default to using the host network until we can configure.
   150  	bridgeDevice := broker.agentConfig.Value(agent.LxcBridge)
   151  	if bridgeDevice == "" {
   152  		bridgeDevice = container.DefaultKvmBridge
   153  	}
   154  
   155  	// There's no InterfaceInfo we expect to get below.
   156  	_, err := prepareOrGetContainerInterfaceInfo(
   157  		broker.api,
   158  		machineID,
   159  		bridgeDevice,
   160  		false, // maintain, do not allocate.
   161  		broker.enableNAT,
   162  		args.NetworkInfo,
   163  		kvmLogger,
   164  		broker.agentConfig.Value(agent.ProviderType),
   165  	)
   166  	return err
   167  }
   168  
   169  // StopInstances shuts down the given instances.
   170  func (broker *kvmBroker) StopInstances(ids ...instance.Id) error {
   171  	// TODO: potentially parallelise.
   172  	for _, id := range ids {
   173  		kvmLogger.Infof("stopping kvm container for instance: %s", id)
   174  		if err := broker.manager.DestroyContainer(id); err != nil {
   175  			kvmLogger.Errorf("container did not stop: %v", err)
   176  			return err
   177  		}
   178  		providerType := broker.agentConfig.Value(agent.ProviderType)
   179  		maybeReleaseContainerAddresses(broker.api, id, broker.namespace, kvmLogger, providerType)
   180  	}
   181  	return nil
   182  }
   183  
   184  // AllInstances only returns running containers.
   185  func (broker *kvmBroker) AllInstances() (result []instance.Instance, err error) {
   186  	return broker.manager.ListContainers()
   187  }