276 lines
9.5 KiB
Python
276 lines
9.5 KiB
Python
# Copyright 2010 United States Government as represented by the
|
|
# Administrator of the National Aeronautics and Space Administration.
|
|
# All Rights Reserved.
|
|
# Copyright (c) 2010 Citrix Systems, Inc.
|
|
# Copyright (c) 2014 Canonical Ltd.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""
|
|
Nova LXD Driver
|
|
|
|
"""
|
|
|
|
import socket
|
|
import contextlib
|
|
import psutil
|
|
|
|
from oslo.utils import units
|
|
|
|
from oslo.config import cfg
|
|
from oslo.serialization import jsonutils
|
|
|
|
from nova.compute import arch
|
|
from nova.compute import hv_type
|
|
from nova.compute import power_state
|
|
from nova.compute import task_states
|
|
from nova.compute import vm_mode
|
|
from nova.console import type as ctype
|
|
from nova import db
|
|
from nova import exception
|
|
from nova.i18n import _LW
|
|
from nova.openstack.common import log as logging
|
|
from nova import utils
|
|
from nova.virt import diagnostics
|
|
from nova.virt import driver
|
|
from nova.virt import firewall
|
|
from nova.virt import hardware
|
|
from nova.virt import virtapi
|
|
|
|
from . import client
|
|
from . import container
|
|
from . import host_utils
|
|
|
|
lxd_opts = [
|
|
cfg.StrOpt('lxd_client_cert',
|
|
default='/etc/lxd/client.crt',
|
|
help='LXD client certificate'),
|
|
cfg.StrOpt('lxd_client_key',
|
|
default='/etc/lxd/client.key',
|
|
help='LXD client key'),
|
|
cfg.StrOpt('lxd_client_host',
|
|
default='10.5.0.12:8443',
|
|
help='LXD API Server'),
|
|
cfg.StrOpt('lxd_root_dir',
|
|
default='/var/lib/lxd/lxc',
|
|
help='Default LXD directory'),
|
|
cfg.StrOpt('lxd_default_template',
|
|
default='ubuntu-cloud',
|
|
help='Default LXC template'),
|
|
cfg.StrOpt('lxd_template_dir',
|
|
default='/usr/share/lxc/templates',
|
|
help='Default template directory'),
|
|
cfg.StrOpt('lxd_config_dir',
|
|
default='/usr/share/lxc/config',
|
|
help='Default lxc config dir')
|
|
]
|
|
|
|
|
|
CONF = cfg.CONF
|
|
CONF.register_opts(lxd_opts, 'lxd')
|
|
CONF.import_opt('host', 'nova.netconf')
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
class LXDDriver(driver.ComputeDriver):
|
|
capabilities = {
|
|
"has_imagecache": False,
|
|
"supports_recreate": False,
|
|
}
|
|
|
|
|
|
"""LXD hypervisor driver."""
|
|
|
|
def __init__(self, virtapi, read_only=False):
|
|
super(LXDDriver, self).__init__(virtapi)
|
|
|
|
self.client = client.Client(CONF.lxd.lxd_client_host,
|
|
CONF.lxd.lxd_client_cert,
|
|
CONF.lxd.lxd_client_key)
|
|
self.firewall_driver = firewall.load_driver(
|
|
default='nova.virt.firewall.NoopFirewallDriver')
|
|
self.container = container.Container(self.client,
|
|
self.virtapi,
|
|
self.firewall_driver)
|
|
|
|
|
|
def init_host(self, host):
|
|
return self.container.init_container()
|
|
|
|
def list_instances(self):
|
|
return self.client.list()
|
|
|
|
def list_instance_uuids(self):
|
|
return self.client.list()
|
|
|
|
def spawn(self, context, instance, image_meta, injected_files,
|
|
admin_password, network_info=None, block_device_info=None,
|
|
flavor=None):
|
|
self.container.start_container(context, instance, image_meta,
|
|
injected_files, admin_password, network_info,
|
|
block_device_info, flavor)
|
|
|
|
def snapshot(self, context, instance, name, update_task_state):
|
|
raise NotImplemented()
|
|
|
|
def reboot(self, context, instance, network_info, reboot_type,
|
|
block_device_info=None, bad_volumes_callback=None):
|
|
self.client.reboot(instance['uuid'])
|
|
|
|
def rescue(self, context, instance, network_info, image_meta,
|
|
rescue_password):
|
|
raise NotImplemented()
|
|
|
|
def unrescue(self, instance, network_info):
|
|
raise NotImplemented()
|
|
|
|
def poll_rebooting_instances(self, timeout, instances):
|
|
pass
|
|
|
|
def migrate_disk_and_power_off(self, context, instance, dest,
|
|
flavor, network_info,
|
|
block_device_info=None,
|
|
timeout=0, retry_interval=0):
|
|
pass
|
|
|
|
def finish_revert_migration(self, context, instance, network_info,
|
|
block_device_info=None, power_on=True):
|
|
pass
|
|
|
|
def post_live_migration_at_destination(self, context, instance,
|
|
network_info,
|
|
block_migration=False,
|
|
block_device_info=None):
|
|
pass
|
|
|
|
def power_off(self, instance, shutdown_timeout=0, shutdown_attempts=0):
|
|
self.client.stop(instance['uuid'])
|
|
|
|
def power_on(self, context, instance, network_info, block_device_info):
|
|
self.client.start(instance['uuid'])
|
|
|
|
def soft_delete(self, instance):
|
|
pass
|
|
|
|
def restore(self, instance):
|
|
raise NotImplemented()
|
|
|
|
def pause(self, instance):
|
|
self.client.pause(instance['uuid'])
|
|
|
|
def unpause(self, instance):
|
|
self.client.unpause(instance['uuid'])
|
|
|
|
def suspend(self, instance):
|
|
raise NotImplemented()
|
|
|
|
def resume(self, context, instance, network_info, block_device_info=None):
|
|
raise NotImplemented()
|
|
|
|
def destroy(self, context, instance, network_info, block_device_info=None,
|
|
destroy_disks=True, migrate_data=None):
|
|
self.client.destory(instance['uuid'])
|
|
self.cleanup(context, instance, network_info, block_device_info)
|
|
|
|
def cleanup(self, context, instance, network_info, block_device_info=None,
|
|
destroy_disks=True, migrate_data=None, destroy_vifs=True):
|
|
self.container.teardown_network(instance, network_info)
|
|
|
|
def attach_volume(self, context, connection_info, instance, mountpoint,
|
|
disk_bus=None, device_type=None, encryption=None):
|
|
"""Attach the disk to the instance at mountpoint using info."""
|
|
raise NotImplemented()
|
|
|
|
def detach_volume(self, connection_info, instance, mountpoint,
|
|
encryption=None):
|
|
"""Detach the disk attached to the instance."""
|
|
raise NotImplemented()
|
|
|
|
def swap_volume(self, old_connection_info, new_connection_info,
|
|
instance, mountpoint, resize_to):
|
|
"""Replace the disk attached to the instance."""
|
|
raise NotImplemented()
|
|
|
|
def attach_interface(self, instance, image_meta, vif):
|
|
raise NotImplemented()
|
|
|
|
def detach_interface(self, instance, vif):
|
|
raise NotImplemented()
|
|
|
|
def get_info(self, instance):
|
|
if self.client.running(instance['uuid']):
|
|
pstate = power_state.RUNNING
|
|
else:
|
|
pstate = power_state.SHUTDOWN
|
|
return hardware.InstanceInfo(state=pstate,
|
|
max_mem_kb=0,
|
|
mem_kb=0,
|
|
num_cpu=2,
|
|
cpu_time_ns=0)
|
|
|
|
def get_console_output(self, context, instance):
|
|
return self.container.get_console_log(instance)
|
|
|
|
def refresh_security_group_rules(self, security_group_id):
|
|
self.firewall_driver.refresh_security_group_rules(security_group_id)
|
|
|
|
def refresh_security_group_members(self, security_group_id):
|
|
self.firewall_driver.refresh_security_group_members(security_group_id)
|
|
|
|
def refresh_instance_security_rules(self, instance):
|
|
self.firewall_driver.refresh_rules(instance)
|
|
|
|
def refresh_provider_fw_rules(self):
|
|
self.firewall_driver.refresh_provider_fw_rules()
|
|
|
|
def get_available_resource(self, nodename):
|
|
"""Updates compute manager resource info on ComputeNode table.
|
|
|
|
Since we don't have a real hypervisor, pretend we have lots of
|
|
disk and ram.
|
|
"""
|
|
data = {}
|
|
disk = host_utils.get_fs_info(CONF.instances_path)
|
|
memory = host_utils.get_memory_mb_usage()
|
|
|
|
data["supported_instances"] = jsonutils.dumps([
|
|
('i686', 'lxd', 'lxd'),
|
|
('x86_64', 'lxd', 'lxd')])
|
|
data["vcpus"] = 4
|
|
data["memory_mb"] = memory['total'] / units.Mi
|
|
data["local_gb"] = disk['total'] / units.Gi
|
|
data["vcpus_used"] = 1
|
|
data["memory_mb_used"] = memory['used'] / units.Mi
|
|
data["local_gb_used"] = disk['used'] / units.Gi
|
|
data["hypervisor_type"] = "lxd"
|
|
data["hypervisor_version"] = "1.0"
|
|
data["hypervisor_hostname"] = nodename
|
|
data["cpu_info"] = "?"
|
|
data["disk_available_least"] = disk['free'] / units.Gi
|
|
data['numa_topology'] = None
|
|
|
|
return data
|
|
|
|
def ensure_filtering_rules_for_instance(self, instance_ref, network_info):
|
|
self.firewall_driver.setup_basic_filtering(instance, network_info)
|
|
self.firewall_driver.prepare_instance_filter(instance, network_info)
|
|
|
|
def unfilter_instance(self, instance_ref, network_info):
|
|
self.firewall_driver.unfilter_instance(instance, network_info)
|
|
|
|
def get_available_nodes(self, refresh=False):
|
|
hostname = socket.gethostname()
|
|
return [hostname]
|
|
|
|
|