Adjust changes for nclxd.
Migrate the nclxd to use LXD exclusivily. Signed-off-by: Chuck Short <chuck.short@canonical.com>
This commit is contained in:
parent
7c38728f2b
commit
d035e70270
|
@ -13,8 +13,8 @@ set +o xtrace
|
|||
LXD_DIR=$DEST/nova-compute-lxd
|
||||
LXD_BIN_DIR=$GOPATH/src/github.com/lxc/lxd
|
||||
|
||||
LXC_REPO=${LXC_REPO:-${GIT_BASE}/zulcss/pylxd}
|
||||
LXC_DIR=$DEST/pylxd
|
||||
#LXC_REPO=${LXC_REPO:-${GIT_BASE}/zulcss/pylxd}
|
||||
#LXC_DIR=$DEST/pylxd
|
||||
|
||||
|
||||
function configure_lxd {
|
||||
|
@ -27,8 +27,8 @@ function configure_lxd {
|
|||
}
|
||||
|
||||
function install_lxd {
|
||||
git_clone $LXC_REPO $LXC_DIR
|
||||
setup_develop $LXC_DIR
|
||||
# git_clone $LXC_REPO $LXC_DIR
|
||||
# setup_develop $LXC_DIR
|
||||
|
||||
setup_develop $LXD_DIR
|
||||
mkdir -p /etc/nova/rootwrap.d
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
# Copyright (c) 2015 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.
|
||||
|
||||
import httplib
|
||||
import json
|
||||
import socket
|
||||
|
@ -62,14 +76,36 @@ class Client(object):
|
|||
def container_init(self, config):
|
||||
(status, data) = self._make_request('POST', '/1.0/containers',
|
||||
json.dumps(config))
|
||||
print data
|
||||
return (status, data)
|
||||
|
||||
def container_start(self, name):
|
||||
action = {'action': 'start', 'force': True}
|
||||
(status, data) = self._make_request('PUT', '/1.0/containers/%s/state'
|
||||
% name, json.dumps(action))
|
||||
print data
|
||||
return (status, data)
|
||||
|
||||
def container_restart(self, name):
|
||||
action = {'action': 'restart', 'force': True}
|
||||
(status, data) = self._make_request('PUT', '/1.0/containers/%s/state'
|
||||
% name, json.dumps(action))
|
||||
return (status, data)
|
||||
|
||||
def container_stop(self, name):
|
||||
action = {'action': 'stop', 'force': True}
|
||||
(status, data) = self._make_request('PUT', '/1.0/containers/%s/state'
|
||||
% name, json.dumps(action))
|
||||
return (status, data)
|
||||
|
||||
def container_suspend(self, name):
|
||||
action = {'action': 'freeze', 'force': True}
|
||||
(status, data) = self._make_request('PUT', '/1.0/containers/%s/state'
|
||||
% name, json.dumps(action))
|
||||
return (status, data)
|
||||
|
||||
def container_resume(self, name):
|
||||
ction = {'action': 'unfreeze', 'force': True}
|
||||
(status, data) = self._make_request('PUT', '/1.0/containers/%s/state'
|
||||
% name, json.dumps(action))
|
||||
return (status, data)
|
||||
|
||||
def container_delete(self, name):
|
||||
|
@ -77,6 +113,11 @@ class Client(object):
|
|||
% name)
|
||||
return (status, data)
|
||||
|
||||
def container_update(self, name, config):
|
||||
(status, data) = self._make_request('PUT', '/1.0/containers/%s'
|
||||
% name, json.dumps(config))
|
||||
return (status, data)
|
||||
|
||||
# profiles
|
||||
def profile_list(self):
|
||||
(status, data) = self._make_request('GET', '/1.0/profiles')
|
||||
|
@ -91,7 +132,7 @@ class Client(object):
|
|||
def profile_update(self, name, config):
|
||||
(status, data) = self._make_request('PUT', '/1.0/profiles/%s' % name,
|
||||
json.dumps(config))
|
||||
reutrn (status, data)
|
||||
return (status, data)
|
||||
|
||||
def profile_show(self, name):
|
||||
(status, data) = self._make_request('GET', '/1.0/profiles/%s' % name)
|
||||
|
@ -134,3 +175,13 @@ class Client(object):
|
|||
def alias_delete(self, name):
|
||||
(status, data) = self._make_request('DELETE', '/1.0/images/aliases/%s' % name)
|
||||
return (status, data)
|
||||
|
||||
# operations
|
||||
def operation_list(self):
|
||||
(status, data) = self._make_request('GET', '/1.0/operations')
|
||||
return [operation.split('/1.0/operations/')[-1]
|
||||
for operation in data['metadata']['running']]
|
||||
|
||||
def operation_show(self, oid):
|
||||
(status, data) = self._make_request('GET', '/1.0/operations/%s' % oid)
|
||||
return (status, data)
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
import os
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from nova.i18n import _, _LW, _LE, _LI
|
||||
from nova import utils
|
||||
from nova import exception
|
||||
|
||||
from . import constants
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ContainerConfig(object):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.config = {}
|
||||
self.resources = {}
|
||||
self.devices = {}
|
||||
|
||||
def create_container_config(self, instance, network_info):
|
||||
self.config = { 'name': instance.uuid,
|
||||
'architecture': 'x86_64',
|
||||
'ephemeral': True,
|
||||
'hostname': instance.uuid,
|
||||
'profiles': [],
|
||||
}
|
||||
self.config['source'] = {'type': 'image',
|
||||
'alias': instance.image_ref}
|
||||
|
||||
self.config['devices'] = { 'eth0':
|
||||
self._get_container_devices(network_info)}
|
||||
|
||||
self.config['config'] = {'raw.lxc': 'lxc.console.logfile = %s'
|
||||
% self._get_console_path(instance),
|
||||
'limits.memory': '%s' % self._get_memory_mb(instance),
|
||||
'limits.cpus': '%s' % self._get_vcpus(instance)
|
||||
}
|
||||
|
||||
LOG.debug(_('Creating container configuration'))
|
||||
self._container_init(instance)
|
||||
|
||||
def _container_init(self, instance):
|
||||
try:
|
||||
(status, resp) = self.client.container_init(self.config)
|
||||
if resp.get('status') != 'OK':
|
||||
raise exception.NovaException
|
||||
except Exception as e:
|
||||
LOG.debug(_('Failed to init container: %s') % resp.get('metadata'))
|
||||
msg = _('Cannot init container: {0}')
|
||||
raise exception.NovaException(msg.format(e),
|
||||
instance_id=instance.name)
|
||||
|
||||
|
||||
def _get_container_devices(self, network_info):
|
||||
for vif in network_info:
|
||||
vif_id = vif['id'][:11]
|
||||
vif_type = vif['type']
|
||||
bridge = vif['network']['bridge']
|
||||
mac = vif['address']
|
||||
|
||||
if vif_type == 'ovs':
|
||||
bridge = 'qbr%s' % vif_id
|
||||
|
||||
return {
|
||||
'type': 'nic',
|
||||
'parent': bridge,
|
||||
'hwaddr': mac,
|
||||
}
|
||||
|
||||
def _get_memory_mb(self, instance):
|
||||
if instance.flavor is not None:
|
||||
try:
|
||||
memory_mb = '%sM' % int(instance.flavor.memory_mb)
|
||||
except ValueError:
|
||||
raise Exception('Failed to determine memory for container.')
|
||||
return memory_mb
|
||||
|
||||
def _get_vcpus(self, instance):
|
||||
if instance.flavor is not None:
|
||||
try:
|
||||
vcpus = instance.flavor.vcpus
|
||||
except ValueError:
|
||||
raise Exception('Failed to determine vcpus for container.')
|
||||
return vcpus
|
||||
|
||||
def _get_console_path(self, instance):
|
||||
return os.path.join(CONF.lxd.lxd_root_dir, instance.uuid, 'console.log')
|
|
@ -1,18 +0,0 @@
|
|||
from oslo.utils import units
|
||||
|
||||
from nova.compute import power_state
|
||||
|
||||
MAX_CONSOLE_BYTES = 100 * units.Ki
|
||||
|
||||
LXD_POWER_STATES = {
|
||||
'RUNNING': power_state.RUNNING,
|
||||
'STOPPED': power_state.SHUTDOWN,
|
||||
'STARTING': power_state.BUILDING,
|
||||
'STOPPING': power_state.SHUTDOWN,
|
||||
'ABORTING': power_state.CRASHED,
|
||||
'FREEZING': power_state.PAUSED,
|
||||
'FROZEN': power_state.SUSPENDED,
|
||||
'THAWED': power_state.PAUSED,
|
||||
'PENDING': power_state.BUILDING,
|
||||
'UNKNOWN': power_state.NOSTATE
|
||||
}
|
|
@ -12,10 +12,14 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import pwd
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import units
|
||||
|
||||
from nova.openstack.common import loopingcall
|
||||
|
||||
from nova.i18n import _, _LW, _LE
|
||||
from nova import utils
|
||||
|
@ -25,7 +29,6 @@ from nova.compute import power_state
|
|||
|
||||
from . import vif
|
||||
from . import images
|
||||
from . import config
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt('vif_plugging_timeout', 'nova.virt.driver')
|
||||
|
@ -55,7 +58,6 @@ class Container(object):
|
|||
self.client = client
|
||||
self.virtapi = virtapi
|
||||
self.image = images.ContainerImage(self.client)
|
||||
self.config = config.ContainerConfig(self.client)
|
||||
self.vif_driver = vif.LXDGenericDriver()
|
||||
|
||||
def init_host(self):
|
||||
|
@ -70,8 +72,8 @@ class Container(object):
|
|||
LOG.debug(_('Fetching image from Glance.'))
|
||||
self.image.fetch_image(context, instance, image_meta)
|
||||
|
||||
LOG.debug(_('Writing LXD config'))
|
||||
self.config.create_container_config(instance, network_info)
|
||||
LOG.debug(_('Setting up container profiles'))
|
||||
self.setup_container(instance, network_info)
|
||||
|
||||
LOG.debug(_('Setup Networking'))
|
||||
self._start_network(instance, network_info)
|
||||
|
@ -79,6 +81,103 @@ class Container(object):
|
|||
LOG.debug(_('Start container'))
|
||||
self._start_container(instance, network_info)
|
||||
|
||||
def setup_container(self, instance, network_info):
|
||||
console_log = self._get_console_path(instance)
|
||||
container_log = self._get_container_log(instance)
|
||||
container = {'name': instance.uuid,
|
||||
'source': {'type': 'image','alias': instance.image_ref}}
|
||||
try:
|
||||
(status, resp) = self.client.container_init(container)
|
||||
if resp.get('status') != 'OK':
|
||||
raise exception.NovaException
|
||||
except Exception as e:
|
||||
LOG.debug(_('Failed to init container: %s') % resp)
|
||||
msg = _('Cannot init container: {0}')
|
||||
raise exception.NovaException(msg.format(e),
|
||||
instance_id=instance.name)
|
||||
|
||||
oid = resp.get('operation').split('/')[3]
|
||||
if not oid:
|
||||
msg = _('Unable to determine resource id')
|
||||
raise exception.NovaException(msg)
|
||||
|
||||
timer = loopingcall.FixedIntervalLoopingCall(self._wait_for_start,
|
||||
oid)
|
||||
timer.start(interval=0.5).wait()
|
||||
|
||||
network_type = self._get_container_devices(network_info)
|
||||
container_config = {'config': {'raw.lxc': 'lxc.logfile = %s\nlxc.console.logfile=%s\n'
|
||||
% (container_log,console_log)},
|
||||
'devices': {'eth0': {'nictype': 'bridged',
|
||||
'parent': network_type['parent'],
|
||||
'hwaddr': network_type['hwaddr'],
|
||||
'type': 'nic'}}}
|
||||
try:
|
||||
(status, resp) = self.client.container_update(instance.uuid, container_config)
|
||||
if resp.get('status') != 'OK':
|
||||
raise exception.NovaException
|
||||
except Exception as e:
|
||||
LOG.debug(_('Failed to update container: %s') % resp)
|
||||
msg = _('Cannot update container: {0}')
|
||||
raise exception.NovaException(msg.format(e),
|
||||
instance_id=instance.name)
|
||||
|
||||
def container_restart(self, context, instance, network_info, reboot_type,
|
||||
block_device_info=None, bad_volumes_callback=None):
|
||||
try:
|
||||
(status, resp) = self.client.container_restart(instance.uuid)
|
||||
if resp.get('status') != 'OK':
|
||||
raise exception.NovaException
|
||||
except Exception as e:
|
||||
LOG.debug(_('Failed to restart container: %s') % resp)
|
||||
msg = _('Cannot restart container: {0}')
|
||||
raise exception.NovaException(msg.format(e),
|
||||
instance_id=instance.name)
|
||||
|
||||
def container_power_on(self, instance, shutdown_timeout=0, shutdown_attempts=0):
|
||||
try:
|
||||
(status, resp) = self.client.container_stop(instance.uuid)
|
||||
if resp.get('status') != 'OK':
|
||||
raise exception.NovaException
|
||||
except Exception as e:
|
||||
LOG.debug(_('Failed to power on container: %s') % resp)
|
||||
msg = _('Cannot power on container: {0}')
|
||||
raise exception.NovaException(msg.format(e),
|
||||
instance_id=instance.name)
|
||||
|
||||
def container_power_off(self, context, instance, network_info, block_device_info):
|
||||
try:
|
||||
(status, resp) = self.client.container_stop(instance.uuid)
|
||||
if resp.get('status') != 'OK':
|
||||
raise exception.NovaException
|
||||
except Exception as e:
|
||||
LOG.debug(_('Failed to power off container: %s') % resp)
|
||||
msg = _('Cannot power on container: {0}')
|
||||
raise exception.NovaException(msg.format(e),
|
||||
instance_id=instance.name)
|
||||
|
||||
def container_suspend(self, instance):
|
||||
try:
|
||||
(status, resp) = self.client.container_suspend(instance.uuid)
|
||||
if resp.get('status') != 'OK':
|
||||
raise exception.NovaException
|
||||
except Exception as e:
|
||||
LOG.debug(_('Failed to suspend container: %s') % resp)
|
||||
msg = _('Cannot suspend container: {0}')
|
||||
raise exception.NovaException(msg.format(e),
|
||||
instance_id=instance.name)
|
||||
|
||||
def container_resume(self, context, instance, network_info, block_device_info=None):
|
||||
try:
|
||||
(status, resp) = self.client.container_resume(instance.uuid)
|
||||
if resp.get('status') != 'OK':
|
||||
raise exception.NovaException
|
||||
except Exception as e:
|
||||
LOG.debug(_('Failed to resume container: %s') % resp)
|
||||
msg = _('Cannot suspend container: {0}')
|
||||
raise exception.NovaException(msg.format(e),
|
||||
instance_id=instance.name)
|
||||
|
||||
def container_destroy(self, context, instance, network_info, block_device_info,
|
||||
destroy_disks, migrate_data):
|
||||
try:
|
||||
|
@ -90,6 +189,19 @@ class Container(object):
|
|||
msg = _('Cannot delete container: {0}')
|
||||
raise exception.NovaException(msg.format(e),
|
||||
instance_id=instance.name)
|
||||
def get_console_log(self, instance):
|
||||
console_dir = os.path.join(CONF.lxd.lxd_root_dir, instance.uuid)
|
||||
console_log = self._get_console_path(instance)
|
||||
uid = pwd.getpwuid(os.getuid()).pw_uid
|
||||
utils.execute('chown', '%s:%s' % (uid, uid), console_log, run_as_root=True)
|
||||
utils.execute('chmod', '755', console_dir, run_as_root=True)
|
||||
with open(console_log, 'rb') as fp:
|
||||
log_data, remaining = utils.last_bytes(fp, MAX_CONSOLE_BYTES)
|
||||
if remaining > 0:
|
||||
LOG.info(_('Truncated console log returned, '
|
||||
'%d bytes ignored'),
|
||||
remaining, instance=instance)
|
||||
return log_data
|
||||
|
||||
def container_info(self, instance):
|
||||
try:
|
||||
|
@ -137,6 +249,11 @@ class Container(object):
|
|||
for vif in network_info:
|
||||
self.vif_driver.unplug(instance, vif)
|
||||
|
||||
def _wait_for_start(self, oid):
|
||||
containers = self.client.operation_list()
|
||||
if oid not in containers:
|
||||
raise loopingcall.LoopingCallDone()
|
||||
|
||||
def _get_neutron_events(self, network_info):
|
||||
return [('network-vif-plugged', vif['id'])
|
||||
for vif in network_info if vif.get('active', True) is False]
|
||||
|
@ -146,4 +263,26 @@ class Container(object):
|
|||
'%(event)s for instance %(uuid)s'),
|
||||
{'event': event_name, 'uuid': instance.uuid})
|
||||
if CONF.vif_plugging_is_fatal:
|
||||
raise exception.VirtualInterfaceCreateException()
|
||||
raise exception.VirtualInterfaceCreateException()
|
||||
|
||||
def _get_console_path(self, instance):
|
||||
return os.path.join(CONF.lxd.lxd_root_dir, instance.uuid, 'console.log')
|
||||
|
||||
def _get_container_log(self, instance):
|
||||
return os.path.join(CONF.lxd.lxd_root_dir, instance.uuid, 'container.log')
|
||||
|
||||
def _get_container_devices(self, network_info):
|
||||
for vif in network_info:
|
||||
vif_id = vif['id'][:11]
|
||||
vif_type = vif['type']
|
||||
bridge = vif['network']['bridge']
|
||||
mac = vif['address']
|
||||
|
||||
if vif_type == 'ovs':
|
||||
bridge = 'qbr%s' % vif_id
|
||||
|
||||
return {
|
||||
'type': 'nic',
|
||||
'parent': bridge,
|
||||
'hwaddr': mac,
|
||||
}
|
|
@ -93,7 +93,8 @@ class LXDDriver(driver.ComputeDriver):
|
|||
|
||||
def reboot(self, context, instance, network_info, reboot_type,
|
||||
block_device_info=None, bad_volumes_callback=None):
|
||||
pass
|
||||
self.container.container_restart(context, instance, network_info, reboot_type,
|
||||
block_device_info, bad_volumes_callback)
|
||||
|
||||
def rescue(self, context, instance, network_info, image_meta,
|
||||
rescue_password):
|
||||
|
@ -122,10 +123,10 @@ class LXDDriver(driver.ComputeDriver):
|
|||
raise NotImplemented()
|
||||
|
||||
def power_off(self, instance, shutdown_timeout=0, shutdown_attempts=0):
|
||||
pass
|
||||
self.container.container_power_off(instance, shutdown_timeout, shutdown_attempts)
|
||||
|
||||
def power_on(self, context, instance, network_info, block_device_info):
|
||||
pass
|
||||
self.container.container_power_on(instance, network_info, block_device_info)
|
||||
|
||||
def soft_delete(self, instance):
|
||||
pass
|
||||
|
@ -140,10 +141,10 @@ class LXDDriver(driver.ComputeDriver):
|
|||
pass
|
||||
|
||||
def suspend(self, instance):
|
||||
raise NotImplemented()
|
||||
return self.container.container_suspend(instance)
|
||||
|
||||
def resume(self, context, instance, network_info, block_device_info=None):
|
||||
raise NotImplemented()
|
||||
return self.container.container_resume(context, instance, network_info, block_device_info)
|
||||
|
||||
def destroy(self, context, instance, network_info, block_device_info=None,
|
||||
destroy_disks=True, migrate_data=None):
|
||||
|
|
|
@ -13,14 +13,10 @@
|
|||
# under the License.
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
||||
from nova.i18n import _, _LE
|
||||
from nova.openstack.common import fileutils
|
||||
|
@ -39,7 +35,6 @@ class ContainerImage(object):
|
|||
|
||||
self.base_dir = os.path.join(CONF.instances_path,
|
||||
CONF.image_cache_subdirectory_name)
|
||||
self.workdir = tempfile.mkdtemp()
|
||||
|
||||
def fetch_image(self, context, instance, image_meta):
|
||||
LOG.debug(_('Fetching image from glance'))
|
||||
|
@ -56,8 +51,6 @@ class ContainerImage(object):
|
|||
self._try_fetch_image(context, container_image, instance)
|
||||
|
||||
LOG.debug(_('Upload image to LXD'))
|
||||
self._get_image_metadata(instance, image_meta)
|
||||
self._update_image(instance, image_meta, container_image)
|
||||
if instance.image_ref not in self.client.alias_list():
|
||||
fingerprint = self._create_image(instance, container_image)
|
||||
self._create_alias(instance, fingerprint)
|
||||
|
@ -70,67 +63,7 @@ class ContainerImage(object):
|
|||
except exception.ImageNotFound:
|
||||
LOG.debug("Image %(image_id)s doesn't exist anymore on "
|
||||
"image service, attempting to copy image ",
|
||||
{'image_id': image_id})
|
||||
|
||||
def _get_image_metadata(self, instance, image_meta):
|
||||
''' Generate LXD metadata understands '''
|
||||
LOG.info(_('Generating metadata for LXD image'))
|
||||
|
||||
''' Extract the information from the glance image '''
|
||||
variant = 'Default'
|
||||
img_meta_prop = image_meta.get('properties', {}) if image_meta else {}
|
||||
architecture = img_meta_prop.get('architecture', '')
|
||||
if not architecture:
|
||||
raise exception.NovaException(_('Unable to determine architecture.'))
|
||||
|
||||
|
||||
os_distro = img_meta_prop.get('os_distro')
|
||||
if not os_distro:
|
||||
raise exception.NovaException(_('Unable to distribution.'))
|
||||
|
||||
os_version = img_meta_prop.get('os_version')
|
||||
if not os_version:
|
||||
raise exception.NovaException(_('Unable to determine version.'))
|
||||
|
||||
os_release = img_meta_prop.get('os_release')
|
||||
if not os_release:
|
||||
raise exception.NovaException(_('Unable to determine release '))
|
||||
epoch = time.time()
|
||||
|
||||
self.metadata = {
|
||||
'architecture': architecture,
|
||||
'creation_date': int(epoch),
|
||||
'properties': {
|
||||
'os': os_distro,
|
||||
'release': os_release,
|
||||
'architecture': architecture,
|
||||
'variant': variant,
|
||||
'description': "%s %s %s Default (%s)" %
|
||||
(os_distro,
|
||||
os_release,
|
||||
architecture,
|
||||
os_version),
|
||||
'name': instance['image_ref']
|
||||
},
|
||||
}
|
||||
|
||||
def _update_image(self,instance, image_meta, container_image):
|
||||
rootfs_dir = os.path.join(self.workdir, 'rootfs')
|
||||
|
||||
fileutils.ensure_tree(rootfs_dir)
|
||||
utils.execute('tar', '--anchored', '--numeric-owner', '-zxvf',
|
||||
container_image, '-C', rootfs_dir,
|
||||
run_as_root=True, check_exit_code=[0, 2])
|
||||
|
||||
metadata_yaml = json.dumps(self.metadata, sort_keys=True,
|
||||
indent=4, separators=(',', ': '),
|
||||
ensure_ascii=False).encode('utf-8') + b"\n"
|
||||
metadata_file = os.path.join(self.workdir, 'metadata.yaml')
|
||||
with open(metadata_file, 'w') as fp:
|
||||
fp.write(metadata_yaml)
|
||||
utils.execute('tar', '-C', self.workdir, '-zcvf',
|
||||
container_image, 'metadata.yaml', 'rootfs',
|
||||
run_as_root=True, check_exit_code=[0, 2])
|
||||
{'image_id': instance.image_ref})
|
||||
|
||||
def _create_image(self, instance, container_image):
|
||||
try:
|
||||
|
@ -145,6 +78,7 @@ class ContainerImage(object):
|
|||
msg = _('Cannot create image: {0}')
|
||||
raise exception.NovaException(msg.format(e),
|
||||
instance_id=instance.name)
|
||||
|
||||
def _create_alias(self, instance, fingerprint):
|
||||
try:
|
||||
LOG.debug(_('Creating LXD profile'))
|
||||
|
|
Loading…
Reference in New Issue