0.2 changes
Signed-off-by: Chuck Short <chuck.short@canonical.com>
This commit is contained in:
parent
5c134e6779
commit
243f30539b
|
@ -24,18 +24,7 @@ function configure_lxd {
|
|||
sudo apt-add-repository -y ppa:ubuntu-lxc/lxd-daily
|
||||
apt_get update
|
||||
install_package lxd lxc-dev
|
||||
|
||||
setfacl -m nova:rx /var/lib/lxd/lxd
|
||||
|
||||
if [[ ! -d /var/lib/lxd ]]; then
|
||||
sudo mkdir -p /var/lib/lxd/
|
||||
sudo chown $USER:$USER /var/lib/lxd
|
||||
fi
|
||||
|
||||
run_process lxd "$LXD_BIN_DIR/lxd/lxd --tcp $HOST_IP:8443 --debug"
|
||||
echo "Configure LXD API"
|
||||
}
|
||||
|
||||
|
||||
function install_lxd {
|
||||
git_clone $LXC_REPO $LXC_DIR
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
"""
|
||||
Manage edits to lxc-usernet(5) style file (/etc/lxc/lxc-usernet).
|
||||
File is
|
||||
* comment lines (#)
|
||||
* <username> <type> <bridge> <count>
|
||||
|
||||
# USERNAME TYPE BRIDGE COUNT
|
||||
ubuntu veth br100 128
|
||||
"""
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
|
||||
ETC_LXC_USERNET = "/etc/lxc/lxc-usernet"
|
||||
|
||||
|
||||
class UserNetLine(object):
|
||||
def __init__(self, line):
|
||||
self.error = None
|
||||
cpos = line.find("#")
|
||||
if cpos < 0:
|
||||
payload = line.strip()
|
||||
comment = None
|
||||
else:
|
||||
payload = line[:cpos].strip()
|
||||
comment = line[cpos:]
|
||||
|
||||
if payload:
|
||||
try:
|
||||
user, ntype, brname, count = split(payload)
|
||||
except ValueError:
|
||||
# don't understand this line.
|
||||
user, ntype, brname, count = None
|
||||
self.error = line
|
||||
else:
|
||||
user, ntype, brname, count = None
|
||||
|
||||
self.user = user
|
||||
self.ntype = ntype
|
||||
self.bridge = brname
|
||||
self.comment = comment
|
||||
|
||||
def __str__(self):
|
||||
if self.error is not None:
|
||||
return self.error
|
||||
comment = ""
|
||||
if self.comment is not None:
|
||||
if self.user:
|
||||
comm = " " + self.comment
|
||||
else:
|
||||
comm = self.comment
|
||||
return ' '.join(self.user, self.ntype, self.bridge,
|
||||
self.count + comm)
|
||||
|
||||
|
||||
def update_usernet(user, bridge, op, if_type="veth",
|
||||
count=1, require=True, fname=ETC_LXC_USERNET):
|
||||
|
||||
ops = ("set", "inc", "dec")
|
||||
if op not in ops:
|
||||
raise TypeError("op = '%s'. must be one of %s",
|
||||
(op, ','.join(ops)))
|
||||
|
||||
minfo = "user=%s, bridge=%s, if_type=%s" % (user, bridge, if_type)
|
||||
with lockutils.lock(str(fname)):
|
||||
lines = load_usernet(fname)
|
||||
|
||||
found = []
|
||||
for i, l in enumerate(lines):
|
||||
if (user != l.user or bridge != l.bridge or l.ntype != if_type):
|
||||
continue
|
||||
found.append(i)
|
||||
|
||||
if found:
|
||||
# update the last one (others deleted on write)
|
||||
line = lines(found[-1])
|
||||
if op == "inc":
|
||||
line.count = int(count) + int(line.count)
|
||||
elif op == "dec":
|
||||
line.count = int(count) - int(line.count)
|
||||
elif op == "set":
|
||||
line.count = count
|
||||
|
||||
if require and not found and op != "set":
|
||||
raise ValueError("EntryNotFound: %s" % minfo)
|
||||
elif op == "set":
|
||||
mline = UserNetLine("")
|
||||
mline.user = user
|
||||
mline.ntype = if_type
|
||||
mline.bridge = bridge
|
||||
mline.count = int(count)
|
||||
|
||||
tf = None
|
||||
try:
|
||||
tf = tempfile.NamedTemporaryFile(dir=os.path.dirname(fname),
|
||||
delete=False)
|
||||
for i, l in enumerate(lines):
|
||||
if i in found[:-1]:
|
||||
continue
|
||||
else:
|
||||
tf.write(l + "\n")
|
||||
tf.close()
|
||||
os.rename(tf.name, fname)
|
||||
except Exception as e:
|
||||
if tf is not None:
|
||||
os.unlink(tf.name)
|
||||
raise e
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.exit(main())
|
||||
|
|
@ -17,10 +17,15 @@ import socket
|
|||
|
||||
from eventlet.green import httplib
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova.i18n import _
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
class UnixHTTPConnection(httplib.HTTPConnection):
|
||||
|
||||
def __init__(self, path, host='localhost', port=None, strict=None,
|
||||
|
@ -69,14 +74,24 @@ class Client(object):
|
|||
|
||||
def state(self, name):
|
||||
(status, data) = self._make_request('GET', '/1.0/containers/%s/state' % name)
|
||||
if status == 150:
|
||||
return 'PENDING'
|
||||
if status == 500:
|
||||
return 'UNKNOWN'
|
||||
if status == 200:
|
||||
return data['metadata']['state']
|
||||
|
||||
def list(self):
|
||||
(status, data) = self._make_request('GET', '/1.0/list')
|
||||
(status, data) = self._make_request('GET', '/1.0')
|
||||
if status != 200:
|
||||
return []
|
||||
return [container.split('/1.0/list')[-1] for container in data['metadata']]
|
||||
return [container.split('/1.0')[-1] for container in data['metadata']]
|
||||
|
||||
def update_container(self, instance, config):
|
||||
container_update = False
|
||||
(status, data) = self._make_request('PUT', '/1.0/containers/%s' % instance, json.dumps(config))
|
||||
if status != 200:
|
||||
raise Exception('Failed to update configuration')
|
||||
|
||||
def start(self, name):
|
||||
container_start = False
|
||||
|
@ -134,14 +149,29 @@ class Client(object):
|
|||
(status, data) = self._make_request('GET', '/1.0/images')
|
||||
return [image.split('/1.0/images')[-1] for image in data['metadata']]
|
||||
|
||||
def upload_image(self, path, filename):
|
||||
(status, data) = self._make_request('POST', '/1.0/images', open(path, 'rb'))
|
||||
|
||||
if status != 200:
|
||||
raise Exception('Failed to upload image')
|
||||
|
||||
def remove_image(self, fingerprint):
|
||||
(status, data) = self._make_request('DELETE', '/1.0/images/%s' % fingerprint)
|
||||
if status != 200:
|
||||
raise Exception('Failed to delete image')
|
||||
|
||||
def list_aliases(self):
|
||||
status, data = self._make_request('/1.0/images/aliases')
|
||||
return [alias.split('/1.0/aliases')[-1] for alias in data['metadata']]
|
||||
(status, data) = self._make_request('GET', '/1.0/images/aliases')
|
||||
return [alias.split('/1.0/images/aliases')[-1] for alias in data['metadata']]
|
||||
|
||||
def create_alias(self, alias, fingerprint):
|
||||
container_alias = False
|
||||
action = {'target': fingerprint,
|
||||
'name': alias}
|
||||
(status, data) = self._make_request('POST','/1.0/images/aliases', json.dumps(action))
|
||||
return data
|
||||
def alias_create(self, name, target):
|
||||
payload = {'target': target,
|
||||
'name': name}
|
||||
(status, data) = self._make_request('POST', '/1.0/images/aliases', json.dumps(payload))
|
||||
if status != 200:
|
||||
raise Exception('Alias create failed')
|
||||
|
||||
def alias_delete(self, name):
|
||||
(status, data) = self._make_request('DELETE', '/1.0/images/aliases/%s' % name)
|
||||
if status != 200:
|
||||
raise Exception('Failed to delete alias')
|
|
@ -12,118 +12,54 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import os
|
||||
|
||||
import jinja2
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo_log import log as logging
|
||||
from nova.i18n import _LW, _
|
||||
|
||||
from nova import exception
|
||||
from nova import utils
|
||||
|
||||
from . import utils as container_utils
|
||||
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
def get_container_console(instance):
|
||||
return os.path.join(CONF.lxd.lxd_root_dir, instance['uuid'],
|
||||
'container.console')
|
||||
|
||||
class LXDSetConfig(object):
|
||||
def __init__(self, container, instance, image_meta, network_info):
|
||||
self.container = container
|
||||
def __init__(self, config, instance, image_meta, network_info):
|
||||
self.instance = instance
|
||||
self.config = config
|
||||
self.image_meta = image_meta
|
||||
self.network_info = network_info
|
||||
|
||||
self.config = {}
|
||||
self.image = {}
|
||||
self.raw_lxc = {}
|
||||
|
||||
def write_config(self):
|
||||
lxc_template = self.get_lxd_template()
|
||||
if lxc_template:
|
||||
net = self.config_lxd_network()
|
||||
(user, uoffset) = container_utils.parse_subfile(CONF.lxd.lxd_default_user,
|
||||
'/etc/subuid')
|
||||
(group, goffset) = container_utils.parse_subfile(CONF.lxd.lxd_default_user,
|
||||
'/etc/subgid')
|
||||
self.config = {
|
||||
'lxd_common_config': '%s/%s.common.conf' % (CONF.lxd.lxd_config_dir,
|
||||
lxc_template),
|
||||
'lxd_userns_config': '%s/%s.userns.conf' % (CONF.lxd.lxd_config_dir,
|
||||
lxc_template),
|
||||
'lxd_rootfs': self.config_lxd_rootfs(),
|
||||
'lxd_name': self.config_lxd_name(),
|
||||
'lxd_logfile': self.config_lxd_logging(),
|
||||
'lxd_console_file': self.config_lxd_console(),
|
||||
'lxd_mac_addr': net['mac'],
|
||||
'lxd_network_link': net['link'],
|
||||
'lxd_user': user,
|
||||
'lxd_uoffset': uoffset,
|
||||
'lxd_group': group,
|
||||
'lxd_goffset': goffset
|
||||
}
|
||||
self._get_lxd_config()
|
||||
self.config['config'] = self.raw_lxc
|
||||
self.config['source'] = self.image
|
||||
|
||||
tmpl_path, tmpl_file = os.path.split(CONF.lxd.lxd_config_template)
|
||||
env = jinja2.Environment(loader=jinja2.FileSystemLoader(tmpl_path),
|
||||
trim_blocks=True)
|
||||
template = env.get_template(tmpl_file)
|
||||
tmpl = template.render(self.config)
|
||||
config_file = container_utils.get_container_config(self.instance)
|
||||
f = open(config_file, 'w')
|
||||
f.write(tmpl)
|
||||
f.close()
|
||||
return self.config
|
||||
|
||||
def config_lxd_name(self):
|
||||
if self.instance:
|
||||
return self.instance['uuid']
|
||||
def _get_lxd_config(self):
|
||||
# Specify the console
|
||||
console_log = 'lxc.console.logfile = %s\n' % get_container_console(self.instance)
|
||||
self.raw_lxc['lxc.raw'] = console_log
|
||||
|
||||
def config_lxd_rootfs(self):
|
||||
container_rootfs = container_utils.get_container_rootfs(self.instance)
|
||||
if not os.path.exists(container_rootfs):
|
||||
msg = _('Container rootfs not found')
|
||||
raise exception.InstanceNotReady(msg)
|
||||
|
||||
return container_rootfs
|
||||
|
||||
def config_lxd_logging(self):
|
||||
return container_utils.get_container_logfile(self.instance)
|
||||
|
||||
def config_lxd_network(self):
|
||||
net = {}
|
||||
if self.network_info:
|
||||
# NOTE(jamespage) this does not deal with multiple nics.
|
||||
for vif in self.network_info:
|
||||
vif_id = vif['id'][:11]
|
||||
vif_type = vif['type']
|
||||
bridge = vif['network']['bridge']
|
||||
mac = vif['address']
|
||||
# Specify the network
|
||||
for vif in self.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
|
||||
|
||||
net = {'mac': mac,
|
||||
'link': bridge}
|
||||
return net
|
||||
self.raw_lxc['lxc.raw'] += 'lxc.network.type = veth\n'
|
||||
self.raw_lxc['lxc.raw'] += 'lxc.network.addr = %s\n' % mac
|
||||
self.raw_lxc['lxc.raw'] += 'lxc.network.link = %s\n' % bridge
|
||||
|
||||
def config_lxd_console(self):
|
||||
return container_utils.get_container_console(self.instance)
|
||||
|
||||
def config_lxd_limits(self):
|
||||
pass
|
||||
|
||||
def get_lxd_template(self):
|
||||
LOG.debug('Fetching LXC template')
|
||||
|
||||
templates = []
|
||||
if (self.image_meta and
|
||||
self.image_meta.get('properties', {}).get('template')):
|
||||
lxc_template = self.image_meta['properties'].get('template')
|
||||
else:
|
||||
lxc_template = CONF.lxd.lxd_default_template
|
||||
path = os.listdir(CONF.lxd.lxd_template_dir)
|
||||
for line in path:
|
||||
templates.append(line.replace('lxc-', ''))
|
||||
if lxc_template in templates:
|
||||
return lxc_template
|
||||
self.image = {'type': 'image', 'alias': self.instance['image_ref']}
|
||||
|
|
|
@ -14,11 +14,9 @@
|
|||
|
||||
import os
|
||||
|
||||
import lxc
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo.utils import units, excutils
|
||||
from oslo.utils import units
|
||||
|
||||
from nova.i18n import _, _LW, _LE, _LI
|
||||
from nova import utils
|
||||
|
@ -26,7 +24,6 @@ from nova import exception
|
|||
from nova.compute import power_state
|
||||
|
||||
from . import config
|
||||
from . import utils as container_utils
|
||||
from . import vif
|
||||
from . import images
|
||||
|
||||
|
@ -34,6 +31,7 @@ CONF = cfg.CONF
|
|||
CONF.import_opt('use_cow_images', 'nova.virt.driver')
|
||||
CONF.import_opt('vif_plugging_timeout', 'nova.virt.driver')
|
||||
CONF.import_opt('vif_plugging_is_fatal', 'nova.virt.driver')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MAX_CONSOLE_BYTES = 100 * units.Ki
|
||||
|
@ -47,9 +45,11 @@ LXD_POWER_STATES = {
|
|||
'FREEZING': power_state.PAUSED,
|
||||
'FROZEN': power_state.SUSPENDED,
|
||||
'THAWED': power_state.PAUSED,
|
||||
'NONE': power_state.NOSTATE
|
||||
'PENDING': power_state.BUILDING,
|
||||
'UNKNOWN': power_state.NOSTATE
|
||||
}
|
||||
|
||||
|
||||
class Container(object):
|
||||
|
||||
def __init__(self, client, virtapi, firewall):
|
||||
|
@ -57,7 +57,9 @@ class Container(object):
|
|||
self.virtapi = virtapi
|
||||
self.firewall_driver = firewall
|
||||
|
||||
|
||||
self.vif_driver = vif.LXDGenericDriver()
|
||||
self.config = {}
|
||||
|
||||
def init_container(self):
|
||||
if not os.path.exists(CONF.lxd.lxd_socket):
|
||||
|
@ -68,8 +70,7 @@ class Container(object):
|
|||
console_log = os.path.join(CONF.lxd.lxd_root_dir,
|
||||
instance['uuid'],
|
||||
'container.console')
|
||||
user = os.getuid()
|
||||
utils.execute('chown', user, console_log, run_as_root=True)
|
||||
|
||||
with open(console_log, 'rb') as fp:
|
||||
log_data, remaining = utils.last_bytes(fp, MAX_CONSOLE_BYTES)
|
||||
if remaining > 0:
|
||||
|
@ -82,24 +83,15 @@ class Container(object):
|
|||
admin_password, network_info, block_device_info, flavor):
|
||||
LOG.info(_LI('Starting new instance'), instance=instance)
|
||||
|
||||
try:
|
||||
# Setup the LXC instance
|
||||
instance_name = instance['uuid']
|
||||
container = lxc.Container(instance_name)
|
||||
container.set_config_path(CONF.lxd.lxd_root_dir)
|
||||
# Setup the LXC instance
|
||||
''' Fetch the image from glance '''
|
||||
self._fetch_image(context, instance, image_meta)
|
||||
|
||||
''' Fetch the image from glance '''
|
||||
self._fetch_image(context, instance, image_meta)
|
||||
''' Set up the configuration file '''
|
||||
self._write_config(instance, network_info, image_meta)
|
||||
|
||||
''' Set up the configuration file '''
|
||||
#self._write_config(container, instance, network_info, image_meta)
|
||||
|
||||
''' Start the container '''
|
||||
#self._start_container(context, instance, network_info, image_meta)
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.destroy_container(context, instance, network_info,
|
||||
block_device_info)
|
||||
''' Start the container '''
|
||||
#self._start_container(context, instance, network_info, image_meta)
|
||||
|
||||
def destroy_container(self, context, instance, network_info, block_device_info,
|
||||
destroy_disks=None, migrate_data=None):
|
||||
|
@ -108,29 +100,19 @@ class Container(object):
|
|||
self.teardown_network(instance, network_info)
|
||||
|
||||
def get_container_info(self, instance):
|
||||
instance_name = instance['uuid']
|
||||
container = lxc.Container(instance_name)
|
||||
container.set_config_path(CONF.lxd.lxd_root_dir)
|
||||
|
||||
try:
|
||||
mem = int(container.get_cgroup_item('memory.usage_in_bytes')) / units.Mi
|
||||
except KeyError:
|
||||
mem = 0
|
||||
|
||||
|
||||
container_state = self.client.state(instance_name)
|
||||
container_state = self.client.state(instance['uuid'])
|
||||
if container_state is None:
|
||||
container_state = 'NONE'
|
||||
state = power_state.CRASHED
|
||||
else:
|
||||
state = LXD_POWER_STATES[container_state]
|
||||
|
||||
LOG.info(_('!!! %s') % container_state)
|
||||
return {'state': LXD_POWER_STATES[container_state],
|
||||
'mem': mem,
|
||||
return {'state': state,
|
||||
'mem': 0,
|
||||
'cpu': 1}
|
||||
|
||||
|
||||
def _fetch_image(self, context, instance, image_meta):
|
||||
image = images.ContainerImage(context, instance, image_meta, self.client)
|
||||
image.create_container()
|
||||
image.upload_image()
|
||||
|
||||
def _start_container(self, context, instance, network_info, image_meta):
|
||||
timeout = CONF.vif_plugging_timeout
|
||||
|
@ -153,10 +135,10 @@ class Container(object):
|
|||
|
||||
self.client.start(instance['uuid'])
|
||||
|
||||
def _write_config(self, container, instance, network_info, image_meta):
|
||||
self.config = config.LXDSetConfig(container, instance,
|
||||
image_meta, network_info)
|
||||
self.config.write_config()
|
||||
def _write_config(self, instance, network_info, image_meta):
|
||||
cconfig = config.LXDSetConfig(self.config, instance, image_meta, network_info)
|
||||
self.config = cconfig.write_config()
|
||||
self.client.update_container(instance['uuid'], self.config)
|
||||
|
||||
def _start_network(self, instance, network_info):
|
||||
for vif in network_info:
|
||||
|
|
|
@ -57,22 +57,7 @@ lxd_opts = [
|
|||
help='Default LXD unix socket'),
|
||||
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_config_template',
|
||||
default='/etc/lxd/lxd.template',
|
||||
help='container config 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'),
|
||||
cfg.StrOpt('lxd_default_user',
|
||||
default='root',
|
||||
help='Default LXD user')
|
||||
help='Default LXD directory')
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -15,14 +15,11 @@
|
|||
import os
|
||||
import hashlib
|
||||
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo.utils import units, excutils
|
||||
from oslo_utils import excutils
|
||||
|
||||
|
||||
from nova import utils
|
||||
from nova.i18n import _, _LI
|
||||
from nova.i18n import _, _LE
|
||||
from nova.openstack.common import fileutils
|
||||
from nova.virt import images
|
||||
from nova import exception
|
||||
|
@ -48,7 +45,7 @@ class ContainerImage(object):
|
|||
if not os.path.exists(self.base_dir):
|
||||
fileutils.ensure_tree(self.base_dir)
|
||||
|
||||
def create_container(self):
|
||||
def upload_image(self):
|
||||
LOG.info(_('Downloading image from glance'))
|
||||
|
||||
disk_format = self.image_meta.get('disk_format')
|
||||
|
@ -56,33 +53,47 @@ class ContainerImage(object):
|
|||
msg = _('Unable to determine disk format for image.')
|
||||
raise exception.InvalidImageRef(msg)
|
||||
|
||||
if not os.path.exists(self.container_image):
|
||||
LOG.info(_('Fetching Image from Glance'))
|
||||
try:
|
||||
images.fetch_to_raw(self.context, self.instance['image_ref'], self.container_image,
|
||||
self.instance['user_id'], self.instance['project_id'],
|
||||
max_size=self.max_size)
|
||||
fingerprint = self._get_fingerprint()
|
||||
|
||||
if fingerprint in self.client.list_images():
|
||||
msg = _('Image already exists in LXD store')
|
||||
raise exception.InvalidImageRef(msg)
|
||||
|
||||
self.client.upload_image(self.container_image, self.container_image.split('/')[-1])
|
||||
|
||||
if self.instance['image_ref'] in self.client.list_aliases():
|
||||
msg = _('Alias already exists in LXD store')
|
||||
raise exception.InvalidImageRef(msg)
|
||||
|
||||
self.client.alias_create(self.instance['image_ref'], fingerprint)
|
||||
os.unlink(self.container_image)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Erorr uploading image to image store for %(image).'),
|
||||
{'image': self.instance['image_ref']})
|
||||
self.cleanup_image()
|
||||
|
||||
def cleanup_image(self):
|
||||
LOG.info(_('Cleaning up image'))
|
||||
|
||||
if os.path.exists(self.container_image):
|
||||
return
|
||||
fingerprint = self._get_fingerprint()
|
||||
os.unlink(self.container_image)
|
||||
|
||||
LOG.info(_('Fetching Image from Glance'))
|
||||
images.fetch_to_raw(self.context, self.instance['image_ref'], self.container_image,
|
||||
self.instance['user_id'], self.instance['project_id'],
|
||||
max_size=self.max_size)
|
||||
if self.instance['image_ref'] in self.client.lists_aliases():
|
||||
self.client.alias_delete(self.instance['image_ref'])
|
||||
|
||||
fingerprint = self._get_fingerprint(self.container_image)
|
||||
if fingerprint is not None:
|
||||
self.client.remove_image(fingerprint)
|
||||
|
||||
if fingerprint in self.client.list_images():
|
||||
msg = _('Image already exists in image store')
|
||||
raise exception.InvalidImageRef(msg)
|
||||
|
||||
alias = 'glance/%s' % self.instance['image_ref']
|
||||
if alias in self.client.list_aliases():
|
||||
msg = _('Alias already exists')
|
||||
raise exception.ImageUnacceptable(msg)
|
||||
|
||||
data = self.client.create_alias(alias, fingerprint)
|
||||
LOG.info(_('!!! %s') % data)
|
||||
|
||||
os.unlink(self.container_image)
|
||||
|
||||
def _get_fingerprint(self, filename):
|
||||
m = hashlib.sha256()
|
||||
with open(filename, 'rb') as f:
|
||||
for chunk in iter(lambda: f.read(128 * m.block_size), b''):
|
||||
m.update(chunk)
|
||||
return m.hexdigest()
|
||||
def _get_fingerprint(self):
|
||||
with open(self.container_image, 'rb') as fp:
|
||||
fingerprint = hashlib.sha256(fp.read()).hexdigest()
|
||||
return fingerprint
|
|
@ -1,219 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""
|
||||
Manage edits to lxc-usernet(5) style file (/etc/lxc/lxc-usernet).
|
||||
File is
|
||||
* comment lines (#)
|
||||
* <username> <type> <bridge> <count>
|
||||
|
||||
example:
|
||||
# USERNAME TYPE BRIDGE COUNT
|
||||
ubuntu veth br100 128
|
||||
"""
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import tempfile
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
|
||||
ETC_LXC_USERNET = "/etc/lxc/lxc-usernet"
|
||||
|
||||
|
||||
class UserNetLine(object):
|
||||
|
||||
def __init__(self, line):
|
||||
self.error = None
|
||||
line = line.rstrip("\n")
|
||||
cpos = line.find("#")
|
||||
user = None
|
||||
ntype = None
|
||||
brname = None
|
||||
count = None
|
||||
if cpos < 0:
|
||||
payload = line.strip()
|
||||
comment = None
|
||||
else:
|
||||
payload = line[:cpos].strip()
|
||||
comment = line[cpos:]
|
||||
|
||||
if payload:
|
||||
try:
|
||||
user, ntype, brname, count = payload.split()
|
||||
except ValueError:
|
||||
# don't understand this line.
|
||||
self.error = line
|
||||
else:
|
||||
comment = line
|
||||
|
||||
self.user = user
|
||||
self.bridge = brname
|
||||
self.ntype = ntype
|
||||
self.count = count
|
||||
self.comment = comment
|
||||
|
||||
def __repr__(self):
|
||||
return(self.__str__())
|
||||
|
||||
def __str__(self):
|
||||
if self.error is not None:
|
||||
return self.error
|
||||
|
||||
if self.user:
|
||||
comm = ""
|
||||
if self.comment:
|
||||
comm = " " + self.comment
|
||||
return ("%s %s %s %s" % (self.user, self.ntype, self.bridge,
|
||||
str(self.count) + comm))
|
||||
return self.comment
|
||||
|
||||
|
||||
def load_usernet(fname):
|
||||
lines = []
|
||||
with open(fname, "r") as fp:
|
||||
for line in fp:
|
||||
lines.append(UserNetLine(line))
|
||||
return lines
|
||||
|
||||
|
||||
def write_usernet(fname, lines, drop=None):
|
||||
if drop:
|
||||
nlines = []
|
||||
for i, l in enumerate(lines):
|
||||
if i not in drop:
|
||||
nlines.append(l)
|
||||
lines = nlines
|
||||
tf = None
|
||||
try:
|
||||
tf = tempfile.NamedTemporaryFile(dir=os.path.dirname(fname),
|
||||
delete=False)
|
||||
for l in lines:
|
||||
tf.write(str(l) + "\n")
|
||||
tf.close()
|
||||
|
||||
if os.path.isfile(fname):
|
||||
statl = os.stat(fname)
|
||||
os.chmod(tf.name, statl.st_mode)
|
||||
os.chown(tf.name, statl.st_uid, statl.st_gid)
|
||||
else:
|
||||
os.chmod(tf.name, 0o644)
|
||||
|
||||
os.rename(tf.name, fname)
|
||||
finally:
|
||||
if tf is not None and os.path.isfile(tf.name):
|
||||
os.unlink(tf.name)
|
||||
|
||||
|
||||
def update_usernet(user, bridge, op, count=1, ntype="veth",
|
||||
strict=False, fname=ETC_LXC_USERNET):
|
||||
|
||||
ops = ("set", "inc", "dec")
|
||||
if op not in ops:
|
||||
raise TypeError("op = '%s'. must be one of %s",
|
||||
(op, ','.join(ops)))
|
||||
|
||||
minfo = "user=%s, bridge=%s, ntype=%s" % (user, bridge, ntype)
|
||||
lines = load_usernet(fname)
|
||||
|
||||
found = []
|
||||
for i, l in enumerate(lines):
|
||||
if (user != l.user or bridge != l.bridge or l.ntype != ntype):
|
||||
continue
|
||||
found.append(i)
|
||||
|
||||
if strict and not found and op != "set":
|
||||
raise ValueError("EntryNotFound: %s" % minfo)
|
||||
|
||||
if not found:
|
||||
if op == "dec":
|
||||
# decrement non-existing, assume zero
|
||||
return
|
||||
newline = UserNetLine("")
|
||||
newline.user = user
|
||||
newline.ntype = ntype
|
||||
newline.bridge = bridge
|
||||
newline.count = int(count)
|
||||
lines.append(newline)
|
||||
else:
|
||||
# update the last one (others deleted on write)
|
||||
line = lines[found[-1]]
|
||||
if op == "inc":
|
||||
line.count = int(line.count) + int(count)
|
||||
elif op == "dec":
|
||||
line.count = int(line.count) - int(count)
|
||||
elif op == "set":
|
||||
if len(found) == 1 and line.count == count and count != 0:
|
||||
return
|
||||
line.count = count
|
||||
if line.count == 0:
|
||||
# set or dec to '0'. add this line to found, for delete
|
||||
found.append(found[-1])
|
||||
|
||||
write_usernet(fname, lines, found[:-1])
|
||||
|
||||
|
||||
def lfilter(fname, user=None, bridge=None, count=None, ntype="veth"):
|
||||
ret = []
|
||||
for f in load_usernet(fname):
|
||||
if user is not None and f.user != user:
|
||||
continue
|
||||
if bridge is not None and f.bridge != bridge:
|
||||
continue
|
||||
if count is not None and str(f.count) != str(count):
|
||||
continue
|
||||
if ntype is not None and f.ntype != ntype:
|
||||
continue
|
||||
ret.append(f)
|
||||
return ret
|
||||
|
||||
|
||||
def manage_main():
|
||||
fname = ETC_LXC_USERNET
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--type", "-t", help="nic type (default: 'veth')",
|
||||
default="veth", dest="ntype")
|
||||
parser.add_argument(
|
||||
'operation',
|
||||
choices=(
|
||||
"set",
|
||||
"inc",
|
||||
"dec",
|
||||
"del",
|
||||
"get"))
|
||||
parser.add_argument('user', help="username")
|
||||
parser.add_argument('bridge', help="bridge")
|
||||
parser.add_argument('count', nargs="?", help="number to operate with.",
|
||||
default=None, const=int)
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.operation == "del":
|
||||
args.operation = "set"
|
||||
args.count = 0
|
||||
elif args.operation in ("set", "inc", "dec") and args.count is None:
|
||||
args.count = 1
|
||||
|
||||
if args.operation == "get":
|
||||
if args.bridge == "*":
|
||||
args.bridge = None
|
||||
matching = lfilter(fname, user=args.user, bridge=args.bridge,
|
||||
count=args.count, ntype=args.ntype)
|
||||
for l in matching:
|
||||
print(str(l))
|
||||
return 0
|
||||
|
||||
with lockutils.lock(str(fname)):
|
||||
update_usernet(user=args.user, bridge=args.bridge, op=args.operation,
|
||||
count=args.count, ntype=args.ntype, fname=fname)
|
|
@ -1,69 +0,0 @@
|
|||
# Copyright (c) 2014 Canonical ltd
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 grp
|
||||
import getpass
|
||||
import pwd
|
||||
import os
|
||||
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from nova.i18n import _
|
||||
|
||||
|
||||
from nova import context as nova_context
|
||||
from nova import objects
|
||||
from nova import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def write_lxc_usernet(instance, bridge, user=None, count=1):
|
||||
if user is None:
|
||||
user = CONF.lxd.lxd_default_user
|
||||
utils.execute('lxc-usernet-manage', 'set', user, bridge, str(count),
|
||||
run_as_root=True, check_exit_code=[0])
|
||||
|
||||
def parse_subfile(name, fname):
|
||||
line = None
|
||||
with open(fname, "r") as fp:
|
||||
for cline in fp:
|
||||
if cline.startswith(name + ":"):
|
||||
line = cline
|
||||
break
|
||||
if line is None:
|
||||
raise ValueError("%s not found in %s" % (name, fname))
|
||||
toks = line.split(":")
|
||||
return (toks[1], toks[2])
|
||||
|
||||
def get_container_config(instance):
|
||||
return os.path.join(CONF.lxd.lxd_root_dir, instance['uuid'], 'config')
|
||||
|
||||
|
||||
def get_container_rootfs(instance):
|
||||
return os.path.join(CONF.lxd.lxd_root_dir,instance['uuid'], 'rootfs')
|
||||
|
||||
def get_container_logfile(instance):
|
||||
return os.path.join(CONF.lxd.lxd_root_dir, instance['uuid'],
|
||||
'container.logfile')
|
||||
|
||||
def get_container_console(instance):
|
||||
return os.path.join(CONF.lxd.lxd_root_dir, instance['uuid'],
|
||||
'container.console')
|
|
@ -24,12 +24,9 @@ from oslo_concurrency import processutils
|
|||
from nova.i18n import _LW
|
||||
from nova import exception
|
||||
from nova import utils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.network import linux_net
|
||||
from nova.network import model as network_model
|
||||
|
||||
import utils as container_utils
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
@ -84,8 +81,6 @@ class LXDOpenVswitchDriver(object):
|
|||
v2_name, iface_id, vif['address'],
|
||||
instance['uuid'])
|
||||
|
||||
container_utils.write_lxc_usernet(instance, br_name)
|
||||
|
||||
def unplug(self, instance, vif):
|
||||
try:
|
||||
br_name = self._get_br_name(vif['id'])
|
||||
|
|
|
@ -10,4 +10,4 @@ oslo.concurrency>=1.4.1 # Apache-2.0
|
|||
oslo.utils>=1.2.0 # Apache-2.0
|
||||
oslo.i18n>=1.3.0 # Apache-2.0
|
||||
oslo.log
|
||||
python-glanceclient>=0.15.0
|
||||
eventlet
|
||||
|
|
Loading…
Reference in New Issue