merged in migration code
Change-Id: Id526ba721ba3c531f75768f2d5552189d7e1e3c5
This commit is contained in:
4
debian/changelog
vendored
4
debian/changelog
vendored
@@ -1,6 +1,8 @@
|
||||
openvz-nova-driver (1.0-3) precise; urgency=low
|
||||
openvz-nova-driver (1.1-02) precise; urgency=low
|
||||
|
||||
* Released for known working distributions. (Closes: #XXXXXX)
|
||||
* Releasing as noarch
|
||||
* Released Migrations
|
||||
* Fixed broken copyright headers
|
||||
|
||||
-- Daniel Salinas <imsplitbit@gmail.com> Mon, 07 May 2013 15:51:03 -0500
|
||||
|
||||
4
debian/rules
vendored
4
debian/rules
vendored
@@ -16,9 +16,11 @@ clean:
|
||||
binary-arch: binary-indep
|
||||
|
||||
binary-indep:
|
||||
dh_installdirs /usr/lib/python$(PYVERS)/dist-packages/nova/virt /etc/nova/rootwrap.d
|
||||
dh_installdirs /usr/lib/python$(PYVERS)/dist-packages/nova/virt /usr/lib/python$(PYVERS)/dist-packages/nova/tests /etc/nova/rootwrap.d
|
||||
cp -R $(CURDIR)/nova/virt/openvz $(CURDIR)/debian/openvz-nova-driver/usr/lib/python$(PYVERS)/dist-packages/nova/virt
|
||||
cp -R $(CURDIR)/nova/tests/openvz $(CURDIR)/debian/openvz-nova-driver/usr/lib/python$(PYVERS)/dist-packages/nova/tests
|
||||
install -d -m 755 $(CURDIR)/debian/openvz-nova-driver/usr/lib/python$(PYVERS)/dist-packages/nova/virt
|
||||
install -d -m 755 $(CURDIR)/debian/openvz-nova-driver/usr/lib/python$(PYVERS)/dist-packages/nova/tests
|
||||
install -D -m 0400 $(CURDIR)/etc/nova/rootwrap.d/openvz.filters $(CURDIR)/debian/openvz-nova-driver/etc/nova/rootwrap.d/
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# nova-rootwrap command filters for openvz nodes
|
||||
# This file should be owned by (and only-writeable by) the root user
|
||||
|
||||
[Filters]
|
||||
# nova/virt/openvz/utils.py: 'mount', '-o', 'defaults' ...
|
||||
mount: CommandFilter, /bin/mount, root
|
||||
|
||||
@@ -70,3 +71,15 @@ sfdisk: CommandFilter, /sbin/sfdisk, root
|
||||
|
||||
# nova/virt/openvz/volume.py
|
||||
mknod: CommandFilter, /bin/mknod, root
|
||||
|
||||
# nova/virt/openvz/driver.py
|
||||
vzmigrate: CommandFilter, /usr/sbin/vzmigrate, root
|
||||
|
||||
# nova/virt/openvz/migrate.py
|
||||
cp: CommandFilter, /bin/cp, root
|
||||
|
||||
# nova/virt/openvz/utils.py
|
||||
tar: CommandFilter, /bin/tar, root
|
||||
|
||||
# nova/virt/openvz/migration_drivers/rsync.py
|
||||
rsync: CommandFilter, /usr/bin/rsync, root
|
||||
|
||||
16
nova/tests/openvz/__init__.py
Normal file
16
nova/tests/openvz/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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.
|
||||
892
nova/tests/openvz/fakes.py
Normal file
892
nova/tests/openvz/fakes.py
Normal file
@@ -0,0 +1,892 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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.
|
||||
|
||||
from Cheetah import Template
|
||||
import json
|
||||
from nova.compute import power_state
|
||||
from nova import exception
|
||||
from nova.virt.openvz import utils as ovz_utils
|
||||
import os
|
||||
from oslo.config import cfg
|
||||
import random
|
||||
import socket
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
global _ovz_tc_available_ids
|
||||
global _ovz_tc_inflight_ids
|
||||
_ovz_tc_available_ids = None
|
||||
_ovz_tc_inflight_ids = None
|
||||
|
||||
|
||||
class FakeOVZExtStorage(object):
|
||||
def __init__(self, instance_id):
|
||||
self.instance_id = instance_id
|
||||
self._volumes = {}
|
||||
|
||||
def add_volume(self, mount_point, connection_info):
|
||||
self._volumes[mount_point] = connection_info
|
||||
|
||||
def remove_volume(self, mount_point):
|
||||
self._volumes.pop(mount_point)
|
||||
|
||||
|
||||
class FakeOVZTcRules(object):
|
||||
if not _ovz_tc_available_ids:
|
||||
_ovz_tc_available_ids = list()
|
||||
|
||||
if not _ovz_tc_inflight_ids:
|
||||
_ovz_tc_inflight_ids = list()
|
||||
|
||||
def __init__(self):
|
||||
if not len(FakeOVZTcRules._ovz_tc_available_ids):
|
||||
FakeOVZTcRules._ovz_tc_available_ids = [
|
||||
i for i in range(1, CONF.ovz_tc_id_max)
|
||||
]
|
||||
self.instance_type = {'memory_mb': 512}
|
||||
self._remove_used_ids()
|
||||
|
||||
def instance_info(self, instance_id, address, vz_iface):
|
||||
if not instance_id:
|
||||
self.instance_type = dict()
|
||||
self.instance_type['memory_mb'] = 2048
|
||||
|
||||
self.address = address
|
||||
self.vz_iface = vz_iface
|
||||
self.bandwidth = int(
|
||||
round(self.instance_type['memory_mb'] /
|
||||
CONF.ovz_memory_unit_size)) * CONF.ovz_tc_mbit_per_unit
|
||||
self.tc_id = self._get_instance_tc_id()
|
||||
if not self.tc_id:
|
||||
self.tc_id = self.get_id()
|
||||
|
||||
self._save_instance_tc_id()
|
||||
|
||||
def get_id(self):
|
||||
self._remove_used_ids()
|
||||
tc_id = self._pull_id()
|
||||
while tc_id in FakeOVZTcRules._ovz_tc_inflight_ids:
|
||||
tc_id = self._pull_id()
|
||||
self._reserve_id(tc_id)
|
||||
return id
|
||||
|
||||
def container_start(self):
|
||||
template = self._load_template('tc_container_start.template')
|
||||
search_list = {
|
||||
'prio': self.tc_id,
|
||||
'host_iface': CONF.ovz_tc_host_slave_device,
|
||||
'vz_iface': self.vz_iface,
|
||||
'bandwidth': self.bandwidth,
|
||||
'vz_address': self.address,
|
||||
'line_speed': CONF.ovz_tc_max_line_speed
|
||||
}
|
||||
return self._fill_template(template, search_list).splitlines()
|
||||
|
||||
def container_stop(self):
|
||||
template = self._load_template('tc_container_stop.template')
|
||||
search_list = {
|
||||
'prio': self.tc_id,
|
||||
'host_iface': CONF.ovz_tc_host_slave_device,
|
||||
'vz_iface': self.vz_iface,
|
||||
'bandwidth': self.bandwidth,
|
||||
'vz_address': self.address
|
||||
}
|
||||
return self._fill_template(template, search_list).splitlines()
|
||||
|
||||
def host_start(self):
|
||||
template = self._load_template('tc_host_start.template')
|
||||
search_list = {
|
||||
'host_iface': CONF.ovz_tc_host_slave_device,
|
||||
'line_speed': CONF.ovz_tc_max_line_speed
|
||||
}
|
||||
return self._fill_template(template, search_list).splitlines()
|
||||
|
||||
def host_stop(self):
|
||||
template = self._load_template('tc_host_stop.template')
|
||||
search_list = {
|
||||
'host_iface': CONF.ovz_tc_host_slave_device
|
||||
}
|
||||
return self._fill_template(template, search_list).splitlines()
|
||||
|
||||
def _load_template(self, template_name):
|
||||
full_template_path = '%s/%s' % (
|
||||
CONF.ovz_tc_template_dir, template_name)
|
||||
full_template_path = os.path.abspath(full_template_path)
|
||||
try:
|
||||
template_file = open(full_template_path).read()
|
||||
return template_file
|
||||
except Exception as err:
|
||||
raise exception.FileNotFound(err)
|
||||
|
||||
def _fill_template(self, template, search_list):
|
||||
return str(Template.Template(template, searchList=[search_list]))
|
||||
|
||||
def _pull_id(self):
|
||||
return _ovz_tc_available_ids[random.randint(
|
||||
0, len(_ovz_tc_available_ids) - 1)]
|
||||
|
||||
def _list_existing_ids(self):
|
||||
return [1, 3, 6]
|
||||
|
||||
def _reserve_id(self, tc_id):
|
||||
FakeOVZTcRules._ovz_tc_inflight_ids.append(tc_id)
|
||||
FakeOVZTcRules._ovz_tc_available_ids.remove(tc_id)
|
||||
|
||||
def _remove_used_ids(self):
|
||||
used_ids = self._list_existing_ids()
|
||||
for tc_id in used_ids:
|
||||
if tc_id in FakeOVZTcRules._ovz_tc_available_ids:
|
||||
FakeOVZTcRules._ovz_tc_available_ids.remove(tc_id)
|
||||
|
||||
def _save_instance_tc_id(self):
|
||||
return
|
||||
|
||||
def _get_instance_tc_id(self):
|
||||
return 1
|
||||
|
||||
|
||||
class Context(object):
|
||||
def __init__(self):
|
||||
self.is_admin = False
|
||||
self.read_deleted = "yes"
|
||||
|
||||
|
||||
class AdminContext(Context):
|
||||
def __init__(self):
|
||||
super(AdminContext, self).__init__()
|
||||
self.is_admin = True
|
||||
|
||||
|
||||
# Stubs for faked file operations to allow unittests to test code paths
|
||||
# without actually leaving file turds around the test box.
|
||||
class FakeOvzFile(object):
|
||||
def __init__(self, filename, perms):
|
||||
self.filename = filename
|
||||
self.permissions = perms
|
||||
self.contents = []
|
||||
|
||||
def __enter__(self):
|
||||
"""
|
||||
This may feel dirty but we need to be able to read and write files
|
||||
as a non priviledged user so we need to change the permissions to
|
||||
be more open for our file operations and then secure them once we
|
||||
are done.
|
||||
"""
|
||||
if not self.exists():
|
||||
self.make_path()
|
||||
self.touch()
|
||||
|
||||
self.set_permissions(666)
|
||||
|
||||
def __exit__(self, _type, value, tb):
|
||||
if self.exists():
|
||||
self.set_permissions(self.permissions)
|
||||
|
||||
def exists(self):
|
||||
return True
|
||||
|
||||
def make_path(self, path=None):
|
||||
return
|
||||
|
||||
def touch(self):
|
||||
return
|
||||
|
||||
def set_permissions(self, perms):
|
||||
return
|
||||
|
||||
def read(self):
|
||||
return
|
||||
|
||||
def run_contents(self, raise_on_error=True):
|
||||
return
|
||||
|
||||
def set_contents(self, contents):
|
||||
self.contents = contents
|
||||
|
||||
def make_proper_script(self):
|
||||
return
|
||||
|
||||
def append(self, contents):
|
||||
if not isinstance(contents, list):
|
||||
contents = [str(contents)]
|
||||
self.contents = self.contents + contents
|
||||
|
||||
def prepend(self, contents):
|
||||
if not isinstance(contents, list):
|
||||
contents = [str(contents)]
|
||||
self.contents = contents + self.contents
|
||||
|
||||
def write(self):
|
||||
return
|
||||
|
||||
|
||||
class FakeOVZShutdownFile(FakeOvzFile):
|
||||
def __init__(self, instance_id, permissions):
|
||||
filename = "%s/%s.shutdown" % (CONF.ovz_config_dir, instance_id)
|
||||
filename = os.path.abspath(filename)
|
||||
super(FakeOVZShutdownFile, self).__init__(filename, permissions)
|
||||
|
||||
|
||||
class FakeOVZBootFile(FakeOvzFile):
|
||||
def __init__(self, instance_id, permissions):
|
||||
filename = "%s/%s.shutdown" % (CONF.ovz_config_dir, instance_id)
|
||||
filename = os.path.abspath(filename)
|
||||
super(FakeOVZBootFile, self).__init__(filename, permissions)
|
||||
|
||||
|
||||
class FakeOVZNetworkFile(FakeOvzFile):
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
super(FakeOVZNetworkFile, self).__init__(filename, 644)
|
||||
|
||||
|
||||
class FakeOVZInstanceVolumeOps(object):
|
||||
def __init__(self, instance):
|
||||
self.instance = instance
|
||||
|
||||
def attach_all_volumes(self):
|
||||
return
|
||||
|
||||
def detach_all_volumes(self):
|
||||
return
|
||||
|
||||
|
||||
class FakeOVZISCSIStorageDriver(object):
|
||||
def init_iscsi_device(self):
|
||||
return
|
||||
|
||||
def disconnect_iscsi_volume(self):
|
||||
return
|
||||
|
||||
def discover_volume(self):
|
||||
return
|
||||
|
||||
def setup(self):
|
||||
return
|
||||
|
||||
def prepare_filesystem(self):
|
||||
return
|
||||
|
||||
def attach(self):
|
||||
return
|
||||
|
||||
def detach(self, container_is_running=True):
|
||||
return
|
||||
|
||||
def write_and_close(self):
|
||||
return
|
||||
|
||||
|
||||
class FakeAggregate(object):
|
||||
def __init__(self):
|
||||
self.id = 1
|
||||
|
||||
|
||||
class FakePosixStatVFSResult(object):
|
||||
def __init__(self):
|
||||
self.f_frsize = 4096
|
||||
self.f_blocks = 61005206
|
||||
self.f_bavail = 58342965
|
||||
self.f_bfree = 58342965
|
||||
|
||||
|
||||
METAKEY = 'tc_id'
|
||||
METAVALUE = '1002'
|
||||
METADATA = {METAKEY: METAVALUE}
|
||||
|
||||
STATVFSRESULT = FakePosixStatVFSResult()
|
||||
|
||||
AGGREGATE = FakeAggregate()
|
||||
|
||||
ROOTPASS = '2s3cUr3'
|
||||
|
||||
USER = {'user': 'admin', 'role': 'admin', 'id': 1}
|
||||
|
||||
PROJECT = {'name': 'test', 'id': 2}
|
||||
|
||||
ADMINCONTEXT = AdminContext()
|
||||
|
||||
CONTEXT = Context()
|
||||
|
||||
DESTINATION = '127.0.7.1'
|
||||
|
||||
BDM = {
|
||||
'block_device_mapping': [
|
||||
{
|
||||
'connection_info': {
|
||||
'data': {
|
||||
'volume_id': 'c49a7247-731e-4135-8420-7a3c67002582'
|
||||
},
|
||||
'driver_volume_type': 'iscsi',
|
||||
'mount_device': '/dev/sdgg'
|
||||
},
|
||||
'mount_device': '/dev/sdgg'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
INSTANCETYPE = {
|
||||
'id': 1,
|
||||
'vcpus': 1,
|
||||
'name': 'm1.small',
|
||||
'memory_mb': 2048,
|
||||
'swap': 0,
|
||||
'root_gb': 20
|
||||
}
|
||||
|
||||
INSTANCE = {
|
||||
"image_ref": 1,
|
||||
"name": "instance-00001002",
|
||||
"instance_type_id": 1,
|
||||
"id": 1002,
|
||||
"uuid": "07fd1fc9-eb75-4375-88d5-6247ce2fb7e4",
|
||||
"hostname": "test.foo.com",
|
||||
"power_state": power_state.RUNNING,
|
||||
"admin_pass": ROOTPASS,
|
||||
"user_id": USER['id'],
|
||||
"project_id": PROJECT['id'],
|
||||
"memory_mb": INSTANCETYPE['memory_mb'],
|
||||
"block_device_mapping": BDM,
|
||||
"system_metadata": [
|
||||
{'key': METAKEY, 'value': METAVALUE},
|
||||
{'key': 'instance_type_name', 'value': INSTANCETYPE['name']},
|
||||
{'key': 'instance_type_root_gb', 'value': INSTANCETYPE['root_gb']},
|
||||
{'key': 'instance_type_memory_mb', 'value': INSTANCETYPE['memory_mb']},
|
||||
{'key': 'instance_type_vcpus', 'value': INSTANCETYPE['vcpus']}
|
||||
]
|
||||
}
|
||||
|
||||
BLKID = '0670a412-bba5-4ef4-a954-7fec8f2a06aa\n'
|
||||
|
||||
IMAGEPATH = '%s/%s.tar.gz' % \
|
||||
(CONF.ovz_image_template_dir, INSTANCE['image_ref'])
|
||||
|
||||
INSTANCES = [INSTANCE, INSTANCE]
|
||||
|
||||
RES_PERCENT = .50
|
||||
|
||||
RES_OVER_PERCENT = 1.50
|
||||
|
||||
VZLIST = "\t1001\n\t%d\n\t1003\n\t1004\n" % (INSTANCE['id'],)
|
||||
|
||||
VZLISTDETAIL = " %d running %s" \
|
||||
% (INSTANCE['id'], INSTANCE['hostname'])
|
||||
|
||||
FINDBYNAME = VZLISTDETAIL.split()
|
||||
FINDBYNAME = {'name': FINDBYNAME[2], 'id': int(FINDBYNAME[0]),
|
||||
'state': FINDBYNAME[1]}
|
||||
FINDBYNAMENOSTATE = VZLISTDETAIL.split()
|
||||
FINDBYNAMENOSTATE = {
|
||||
'name': FINDBYNAMENOSTATE[2], 'id': int(FINDBYNAMENOSTATE[0]),
|
||||
'state': '-'}
|
||||
FINDBYNAMESHUTDOWN = VZLISTDETAIL.split()
|
||||
FINDBYNAMESHUTDOWN = {
|
||||
'name': FINDBYNAMESHUTDOWN[2], 'id': int(FINDBYNAMESHUTDOWN[0]),
|
||||
'state': 'stopped'}
|
||||
|
||||
VZNAME = """\tinstance-00001001\n"""
|
||||
|
||||
VZNAMES = """\tinstance-00001001\n\t%s
|
||||
\tinstance-00001003\n\tinstance-00001004\n""" % (
|
||||
INSTANCE['name'],)
|
||||
|
||||
GOODSTATUS = {
|
||||
'state': power_state.RUNNING,
|
||||
'max_mem': 0,
|
||||
'mem': 0,
|
||||
'num_cpu': 0,
|
||||
'cpu_time': 0
|
||||
}
|
||||
|
||||
NOSTATUS = {
|
||||
'state': power_state.NOSTATE,
|
||||
'max_mem': 0,
|
||||
'mem': 0,
|
||||
'num_cpu': 0,
|
||||
'cpu_time': 0
|
||||
}
|
||||
|
||||
SHUTDOWNSTATUS = {
|
||||
'state': power_state.SHUTDOWN,
|
||||
'max_mem': 0,
|
||||
'mem': 0,
|
||||
'num_cpu': 0,
|
||||
'cpu_time': 0
|
||||
}
|
||||
|
||||
ERRORMSG = "vz command ran but output something to stderr"
|
||||
|
||||
MEMINFO = """MemTotal: 506128 kB
|
||||
MemFree: 291992 kB
|
||||
Buffers: 44512 kB
|
||||
Cached: 64708 kB
|
||||
SwapCached: 0 kB
|
||||
Active: 106496 kB
|
||||
Inactive: 62948 kB
|
||||
Active(anon): 62108 kB
|
||||
Inactive(anon): 496 kB
|
||||
Active(file): 44388 kB
|
||||
Inactive(file): 62452 kB
|
||||
Unevictable: 2648 kB
|
||||
Mlocked: 2648 kB
|
||||
SwapTotal: 1477624 kB
|
||||
SwapFree: 1477624 kB
|
||||
Dirty: 0 kB
|
||||
Writeback: 0 kB
|
||||
AnonPages: 62908 kB
|
||||
Mapped: 14832 kB
|
||||
Shmem: 552 kB
|
||||
Slab: 27988 kB
|
||||
SReclaimable: 17280 kB
|
||||
SUnreclaim: 10708 kB
|
||||
KernelStack: 1448 kB
|
||||
PageTables: 3092 kB
|
||||
NFS_Unstable: 0 kB
|
||||
Bounce: 0 kB
|
||||
WritebackTmp: 0 kB
|
||||
CommitLimit: 1730688 kB
|
||||
Committed_AS: 654760 kB
|
||||
VmallocTotal: 34359738367 kB
|
||||
VmallocUsed: 24124 kB
|
||||
VmallocChunk: 34359711220 kB
|
||||
HardwareCorrupted: 0 kB
|
||||
HugePages_Total: 0
|
||||
HugePages_Free: 0
|
||||
HugePages_Rsvd: 0
|
||||
HugePages_Surp: 0
|
||||
Hugepagesize: 2048 kB
|
||||
DirectMap4k: 8128 kB
|
||||
DirectMap2M: 516096 kB
|
||||
"""
|
||||
|
||||
PROCINFO = """
|
||||
processor : 0
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 58
|
||||
model name : Intel(R) Core(TM) i7-3610QM CPU @ 2.30GHz
|
||||
stepping : 9
|
||||
microcode : 0x13
|
||||
cpu MHz : 1200.000
|
||||
cache size : 6144 KB
|
||||
physical id : 0
|
||||
siblings : 8
|
||||
core id : 0
|
||||
cpu cores : 4
|
||||
apicid : 0
|
||||
initial apicid : 0
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 13
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
|
||||
bogomips : 4589.55
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 36 bits physical, 48 bits virtual
|
||||
power management:
|
||||
|
||||
processor : 1
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 58
|
||||
model name : Intel(R) Core(TM) i7-3610QM CPU @ 2.30GHz
|
||||
stepping : 9
|
||||
microcode : 0x13
|
||||
cpu MHz : 1200.000
|
||||
cache size : 6144 KB
|
||||
physical id : 0
|
||||
siblings : 8
|
||||
core id : 0
|
||||
cpu cores : 4
|
||||
apicid : 1
|
||||
initial apicid : 1
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 13
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
|
||||
bogomips : 4589.36
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 36 bits physical, 48 bits virtual
|
||||
power management:
|
||||
|
||||
processor : 2
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 58
|
||||
model name : Intel(R) Core(TM) i7-3610QM CPU @ 2.30GHz
|
||||
stepping : 9
|
||||
microcode : 0x13
|
||||
cpu MHz : 1200.000
|
||||
cache size : 6144 KB
|
||||
physical id : 0
|
||||
siblings : 8
|
||||
core id : 1
|
||||
cpu cores : 4
|
||||
apicid : 2
|
||||
initial apicid : 2
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 13
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
|
||||
bogomips : 4589.36
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 36 bits physical, 48 bits virtual
|
||||
power management:
|
||||
|
||||
processor : 3
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 58
|
||||
model name : Intel(R) Core(TM) i7-3610QM CPU @ 2.30GHz
|
||||
stepping : 9
|
||||
microcode : 0x13
|
||||
cpu MHz : 1200.000
|
||||
cache size : 6144 KB
|
||||
physical id : 0
|
||||
siblings : 8
|
||||
core id : 1
|
||||
cpu cores : 4
|
||||
apicid : 3
|
||||
initial apicid : 3
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 13
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
|
||||
bogomips : 4589.36
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 36 bits physical, 48 bits virtual
|
||||
power management:
|
||||
|
||||
processor : 4
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 58
|
||||
model name : Intel(R) Core(TM) i7-3610QM CPU @ 2.30GHz
|
||||
stepping : 9
|
||||
microcode : 0x13
|
||||
cpu MHz : 1200.000
|
||||
cache size : 6144 KB
|
||||
physical id : 0
|
||||
siblings : 8
|
||||
core id : 2
|
||||
cpu cores : 4
|
||||
apicid : 4
|
||||
initial apicid : 4
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 13
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
|
||||
bogomips : 4589.37
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 36 bits physical, 48 bits virtual
|
||||
power management:
|
||||
|
||||
processor : 5
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 58
|
||||
model name : Intel(R) Core(TM) i7-3610QM CPU @ 2.30GHz
|
||||
stepping : 9
|
||||
microcode : 0x13
|
||||
cpu MHz : 1200.000
|
||||
cache size : 6144 KB
|
||||
physical id : 0
|
||||
siblings : 8
|
||||
core id : 2
|
||||
cpu cores : 4
|
||||
apicid : 5
|
||||
initial apicid : 5
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 13
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
|
||||
bogomips : 4589.36
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 36 bits physical, 48 bits virtual
|
||||
power management:
|
||||
|
||||
processor : 6
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 58
|
||||
model name : Intel(R) Core(TM) i7-3610QM CPU @ 2.30GHz
|
||||
stepping : 9
|
||||
microcode : 0x13
|
||||
cpu MHz : 1200.000
|
||||
cache size : 6144 KB
|
||||
physical id : 0
|
||||
siblings : 8
|
||||
core id : 3
|
||||
cpu cores : 4
|
||||
apicid : 6
|
||||
initial apicid : 6
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 13
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
|
||||
bogomips : 4589.37
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 36 bits physical, 48 bits virtual
|
||||
power management:
|
||||
|
||||
processor : 7
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 58
|
||||
model name : Intel(R) Core(TM) i7-3610QM CPU @ 2.30GHz
|
||||
stepping : 9
|
||||
microcode : 0x13
|
||||
cpu MHz : 1200.000
|
||||
cache size : 6144 KB
|
||||
physical id : 0
|
||||
siblings : 8
|
||||
core id : 3
|
||||
cpu cores : 4
|
||||
apicid : 7
|
||||
initial apicid : 7
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 13
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
|
||||
bogomips : 4589.37
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 36 bits physical, 48 bits virtual
|
||||
power management:
|
||||
"""
|
||||
|
||||
UTILITY = {
|
||||
'CTIDS': {
|
||||
1: {
|
||||
|
||||
}
|
||||
},
|
||||
'UTILITY': 10000,
|
||||
'TOTAL': 1000,
|
||||
'UNITS': 100000,
|
||||
'MEMORY_MB': 512000,
|
||||
'CPULIMIT': 2400
|
||||
}
|
||||
|
||||
CPUUNITSCAPA = {
|
||||
'total': 500000,
|
||||
'subscribed': 1000
|
||||
}
|
||||
|
||||
CPUCHECKCONT = """VEID CPUUNITS
|
||||
-------------------------
|
||||
0 1000
|
||||
26 25000
|
||||
27 25000
|
||||
Current CPU utilization: 51000
|
||||
Power of the node: 758432
|
||||
"""
|
||||
|
||||
CPUCHECKNOCONT = """Current CPU utilization: 51000
|
||||
Power of the node: 758432
|
||||
"""
|
||||
|
||||
FILECONTENTS = """mount UUID=FEE52433-F693-448E-B6F6-AA6D0124118B /mnt/foo
|
||||
mount --bind /mnt/foo /vz/private/1/mnt/foo
|
||||
"""
|
||||
|
||||
NETWORKINFO = [
|
||||
[
|
||||
{
|
||||
u'bridge': u'br100',
|
||||
u'multi_host': False,
|
||||
u'bridge_interface': u'eth0',
|
||||
u'vlan': None,
|
||||
u'id': 1,
|
||||
u'injected': True,
|
||||
u'cidr': u'10.0.2.0/24',
|
||||
u'cidr_v6': None
|
||||
},
|
||||
{
|
||||
u'should_create_bridge': True,
|
||||
u'dns': [
|
||||
u'192.168.2.1'
|
||||
],
|
||||
u'label': u'usernet',
|
||||
u'broadcast': u'10.0.2.255',
|
||||
u'ips': [
|
||||
{
|
||||
u'ip': u'10.0.2.16',
|
||||
u'netmask': u'255.255.255.0',
|
||||
u'enabled': u'1'
|
||||
}
|
||||
],
|
||||
u'mac': u'02:16:3e:0c:2c:08',
|
||||
u'rxtx_cap': 0,
|
||||
u'should_create_vlan': True,
|
||||
u'dhcp_server': u'10.0.2.2',
|
||||
u'gateway': u'10.0.2.2'
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
u'bridge': u'br200',
|
||||
u'multi_host': False,
|
||||
u'bridge_interface': u'eth1',
|
||||
u'vlan': None,
|
||||
u'id': 2,
|
||||
u'injected': True,
|
||||
u'cidr': u'10.0.4.0/24',
|
||||
u'cidr_v6': None
|
||||
},
|
||||
{
|
||||
u'should_create_bridge': False,
|
||||
u'dns': [
|
||||
u'192.168.2.1'
|
||||
],
|
||||
u'label': u'infranet',
|
||||
u'broadcast': u'10.0.4.255',
|
||||
u'ips': [
|
||||
{
|
||||
u'ip': u'10.0.4.16',
|
||||
u'netmask': u'255.255.255.0',
|
||||
u'enabled': u'1'
|
||||
}
|
||||
],
|
||||
u'mac': u'02:16:3e:40:5e:1b',
|
||||
u'rxtx_cap': 0,
|
||||
u'should_create_vlan': False,
|
||||
u'dhcp_server': u'10.0.2.2',
|
||||
u'gateway': u'10.0.2.2'
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
INTERFACEINFO = [
|
||||
{
|
||||
'id': 1002,
|
||||
'interface_number': 0,
|
||||
'bridge': 'br100',
|
||||
'name': 'eth0',
|
||||
'vz_host_if': 'veth1002.eth0',
|
||||
'mac': '02:16:3e:0c:2c:08',
|
||||
'address': '10.0.2.16',
|
||||
'netmask': '255.255.255.0',
|
||||
'gateway': '10.0.2.2',
|
||||
'broadcast': '10.0.2.255',
|
||||
'dns': '192.168.2.1',
|
||||
'address_v6': None,
|
||||
'gateway_v6': None,
|
||||
'netmask_v6': None
|
||||
},
|
||||
{
|
||||
'id': 1002,
|
||||
'interface_number': 1,
|
||||
'bridge': 'br200',
|
||||
'name': 'eth1',
|
||||
'vz_host_if': 'veth1002.eth1',
|
||||
'mac': '02:16:3e:40:5e:1b',
|
||||
'address': '10.0.4.16',
|
||||
'netmask': '255.255.255.0',
|
||||
'gateway': '10.0.2.2',
|
||||
'broadcast': '10.0.4.255',
|
||||
'dns': '192.168.2.1',
|
||||
'address_v6': None,
|
||||
'gateway_v6': None,
|
||||
'netmask_v6': None
|
||||
}
|
||||
]
|
||||
|
||||
TEMPFILE = '/tmp/foo/file'
|
||||
|
||||
NETTEMPLATE = """
|
||||
# This file describes the network interfaces available on your system
|
||||
# and how to activate them. For more information, see interfaces(5).
|
||||
|
||||
# The loopback network interface
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
#for $ifc in $interfaces
|
||||
auto ${ifc.name}
|
||||
iface ${ifc.name} inet static
|
||||
address ${ifc.address}
|
||||
netmask ${ifc.netmask}
|
||||
broadcast ${ifc.broadcast}
|
||||
gateway ${ifc.gateway}
|
||||
dns-nameservers ${ifc.dns}
|
||||
|
||||
#if $use_ipv6
|
||||
iface ${ifc.name} inet6 static
|
||||
address ${ifc.address_v6}
|
||||
netmask ${ifc.netmask_v6}
|
||||
gateway ${ifc.gateway_v6}
|
||||
#end if
|
||||
|
||||
#end for
|
||||
"""
|
||||
|
||||
HOSTSTATS = {
|
||||
'vcpus': 12,
|
||||
'vcpus_used': 2,
|
||||
'cpu_info': json.dumps(PROCINFO),
|
||||
'memory_mb': 36864,
|
||||
'memory_mb_used': 2048,
|
||||
'host_memory_total': 36864,
|
||||
'host_memory_free': 34816,
|
||||
'disk_total': 232,
|
||||
'disk_used': 10,
|
||||
'disk_available': 222,
|
||||
'local_gb': 232,
|
||||
'local_gb_used': 10,
|
||||
'hypervisor_type': ovz_utils.get_hypervisor_type(),
|
||||
'hypervisor_version': '3.2.0-31-generic',
|
||||
'hypervisor_hostname': socket.gethostname()
|
||||
}
|
||||
|
||||
FILESTOINJECT = [
|
||||
['/tmp/testfile1', FILECONTENTS],
|
||||
['/tmp/testfile2', FILECONTENTS]
|
||||
]
|
||||
|
||||
OSLISTDIR = ['1002.start', '1002.stop']
|
||||
|
||||
INITIATORNAME = 'iqn.1993-08.org.debian:01:f424a54e43'
|
||||
|
||||
ISCSIINITIATOR = """## DO NOT EDIT OR REMOVE THIS FILE!
|
||||
## If you remove this file, the iSCSI daemon will not start.
|
||||
## If you change the InitiatorName, existing access control lists
|
||||
## may reject this initiator. The InitiatorName must be unique
|
||||
## for each iSCSI initiator. Do NOT duplicate iSCSI InitiatorNames.
|
||||
InitiatorName=%s
|
||||
""" % INITIATORNAME
|
||||
|
||||
PRIVVMPAGES_2048 = "%s 524288\n" % INSTANCE['id']
|
||||
PRIVVMPAGES_1024 = "1003 262144\n"
|
||||
PRIVVMPAGES = PRIVVMPAGES_2048 + PRIVVMPAGES_1024
|
||||
|
||||
UNAME = ('Linux', 'imsplitbit-M17xR4', '3.2.0-31-generic',
|
||||
'#50-Ubuntu SMP Fri Sep 7 16:16:45 UTC 2012', 'x86_64', 'x86_64')
|
||||
1987
nova/tests/openvz/test_driver.py
Normal file
1987
nova/tests/openvz/test_driver.py
Normal file
File diff suppressed because it is too large
Load Diff
107
nova/tests/openvz/test_ext_storage.py
Normal file
107
nova/tests/openvz/test_ext_storage.py
Normal file
@@ -0,0 +1,107 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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 json
|
||||
from nova import test
|
||||
from nova.tests.openvz import fakes
|
||||
from nova.virt.openvz.file_ext import ext_storage
|
||||
import os
|
||||
from oslo.config import cfg
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class OpenVzExtStorageTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(OpenVzExtStorageTestCase, self).setUp()
|
||||
self.filename = '%s/%s.ext_storage' % (CONF.ovz_config_dir,
|
||||
fakes.INSTANCE['id'])
|
||||
self.filename = os.path.abspath(self.filename)
|
||||
self.permissions = 600
|
||||
|
||||
def test_new_object(self):
|
||||
self.mox.StubOutWithMock(ext_storage.ovzfile, 'OVZFile')
|
||||
ext_storage.ovzfile.OVZFile(self.filename, self.permissions).AndReturn(
|
||||
fakes.FakeOvzFile(self.filename, self.permissions))
|
||||
self.mox.ReplayAll()
|
||||
ext_str = ext_storage.OVZExtStorage(fakes.INSTANCE['id'])
|
||||
self.assertEqual(ext_str.instance_id, fakes.INSTANCE['id'])
|
||||
self.assertEqual(ext_str.local_store.filename, self.filename)
|
||||
self.assertEqual(ext_str.local_store.permissions, self.permissions)
|
||||
|
||||
def test_load_volumes(self):
|
||||
self.mox.StubOutWithMock(ext_storage.ovzfile, 'OVZFile')
|
||||
ext_storage.ovzfile.OVZFile(self.filename, self.permissions).AndReturn(
|
||||
fakes.FakeOvzFile(self.filename, self.permissions))
|
||||
self.mox.ReplayAll()
|
||||
ext_str = ext_storage.OVZExtStorage(fakes.INSTANCE['id'])
|
||||
self.assertTrue(isinstance(ext_str._volumes, dict))
|
||||
|
||||
def test_add_volume_success(self):
|
||||
BDM = fakes.BDM['block_device_mapping'][0]
|
||||
self.mox.StubOutWithMock(ext_storage.ovzfile, 'OVZFile')
|
||||
ext_storage.ovzfile.OVZFile(self.filename, self.permissions).AndReturn(
|
||||
fakes.FakeOvzFile(self.filename, self.permissions))
|
||||
self.mox.ReplayAll()
|
||||
ext_str = ext_storage.OVZExtStorage(fakes.INSTANCE['id'])
|
||||
for bdm in fakes.BDM['block_device_mapping']:
|
||||
ext_str.add_volume(bdm['mount_device'], bdm['connection_info'])
|
||||
|
||||
self.assertTrue(BDM['mount_device'] in ext_str._volumes)
|
||||
self.assertEqual(
|
||||
ext_str._volumes[BDM['mount_device']], BDM['connection_info'])
|
||||
|
||||
def test_remove_volume_success(self):
|
||||
BDM = fakes.BDM['block_device_mapping'][0]
|
||||
self.mox.StubOutWithMock(ext_storage.ovzfile, 'OVZFile')
|
||||
ext_storage.ovzfile.OVZFile(self.filename, self.permissions).AndReturn(
|
||||
fakes.FakeOvzFile(self.filename, self.permissions))
|
||||
self.mox.ReplayAll()
|
||||
ext_str = ext_storage.OVZExtStorage(fakes.INSTANCE['id'])
|
||||
for bdm in fakes.BDM['block_device_mapping']:
|
||||
ext_str.add_volume(bdm['mount_device'], bdm['connection_info'])
|
||||
|
||||
self.assertTrue(BDM['mount_device'] in ext_str._volumes)
|
||||
self.assertEqual(
|
||||
ext_str._volumes[BDM['mount_device']], BDM['connection_info'])
|
||||
|
||||
ext_str.remove_volume(BDM['mount_device'])
|
||||
|
||||
self.assertFalse(BDM['mount_device'] in ext_str._volumes)
|
||||
|
||||
def test_remove_volume_failure(self):
|
||||
BDM = fakes.BDM['block_device_mapping'][0]
|
||||
self.mox.StubOutWithMock(ext_storage.ovzfile, 'OVZFile')
|
||||
ext_storage.ovzfile.OVZFile(self.filename, self.permissions).AndReturn(
|
||||
fakes.FakeOvzFile(self.filename, self.permissions))
|
||||
self.mox.ReplayAll()
|
||||
ext_str = ext_storage.OVZExtStorage(fakes.INSTANCE['id'])
|
||||
ext_str.remove_volume(BDM['mount_device'])
|
||||
|
||||
def test_save(self):
|
||||
BDM = fakes.BDM['block_device_mapping'][0]
|
||||
self.mox.StubOutWithMock(ext_storage.ovzfile, 'OVZFile')
|
||||
ext_storage.ovzfile.OVZFile(self.filename, self.permissions).AndReturn(
|
||||
fakes.FakeOvzFile(self.filename, self.permissions))
|
||||
self.mox.ReplayAll()
|
||||
ext_str = ext_storage.OVZExtStorage(fakes.INSTANCE['id'])
|
||||
for bdm in fakes.BDM['block_device_mapping']:
|
||||
ext_str.add_volume(bdm['mount_device'], bdm['connection_info'])
|
||||
|
||||
ext_str.save()
|
||||
self.assertEqual(
|
||||
json.dumps(ext_str._volumes), ext_str.local_store.contents)
|
||||
126
nova/tests/openvz/test_file.py
Normal file
126
nova/tests/openvz/test_file.py
Normal file
@@ -0,0 +1,126 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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 __builtin__
|
||||
import mox
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.tests.openvz import fakes
|
||||
from nova.virt.openvz import driver as openvz_conn
|
||||
from nova.virt.openvz import file as ovzfile
|
||||
from nova.virt.openvz import utils as ovz_utils
|
||||
from oslo.config import cfg
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class OpenVzFileTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(OpenVzFileTestCase, self).setUp()
|
||||
self.fake_file = mox.MockAnything()
|
||||
self.fake_file.readlines().AndReturn(fakes.FILECONTENTS.split())
|
||||
self.fake_file.writelines(mox.IgnoreArg())
|
||||
self.fake_file.read().AndReturn(fakes.FILECONTENTS)
|
||||
|
||||
def test_touch_file_success(self):
|
||||
fh = ovzfile.OVZFile(fakes.TEMPFILE, 755)
|
||||
self.mox.StubOutWithMock(fh, 'make_path')
|
||||
fh.make_path()
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'touch', fakes.TEMPFILE, run_as_root=True).AndReturn(
|
||||
('', fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
fh.touch()
|
||||
|
||||
def test_touch_file_failure(self):
|
||||
fh = ovzfile.OVZFile(fakes.TEMPFILE, 755)
|
||||
self.mox.StubOutWithMock(fh, 'make_path')
|
||||
fh.make_path()
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'touch', fakes.TEMPFILE, run_as_root=True).AndRaise(
|
||||
exception.InstanceUnacceptable(fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(exception.InstanceUnacceptable, fh.touch)
|
||||
|
||||
def test_read_file_success(self):
|
||||
self.mox.StubOutWithMock(__builtin__, 'open')
|
||||
__builtin__.open(mox.IgnoreArg(), 'r').AndReturn(self.fake_file)
|
||||
self.mox.ReplayAll()
|
||||
fh = ovzfile.OVZFile(fakes.TEMPFILE, 755)
|
||||
fh.read()
|
||||
|
||||
def test_read_file_failure(self):
|
||||
self.mox.StubOutWithMock(__builtin__, 'open')
|
||||
__builtin__.open(mox.IgnoreArg(), 'r').AndRaise(
|
||||
exception.FileNotFound(fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
fh = ovzfile.OVZFile(fakes.TEMPFILE, 755)
|
||||
self.assertRaises(exception.FileNotFound, fh.read)
|
||||
|
||||
def test_write_to_file_success(self):
|
||||
self.mox.StubOutWithMock(__builtin__, 'open')
|
||||
__builtin__.open(mox.IgnoreArg(), 'w').AndReturn(self.fake_file)
|
||||
self.mox.ReplayAll()
|
||||
fh = ovzfile.OVZFile(fakes.TEMPFILE, 755)
|
||||
fh.write()
|
||||
|
||||
def test_write_to_file_failure(self):
|
||||
self.mox.StubOutWithMock(__builtin__, 'open')
|
||||
__builtin__.open(mox.IgnoreArg(), 'w').AndRaise(
|
||||
exception.FileNotFound(fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
fh = ovzfile.OVZFile(fakes.TEMPFILE, 755)
|
||||
self.assertRaises(exception.FileNotFound, fh.write)
|
||||
|
||||
def test_set_perms_success(self):
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'chmod', 755, fakes.TEMPFILE, run_as_root=True).AndReturn(
|
||||
('', fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
fh = ovzfile.OVZFile(fakes.TEMPFILE, 755)
|
||||
fh.set_permissions(755)
|
||||
|
||||
def test_set_perms_failure(self):
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'chmod', 755, fakes.TEMPFILE, run_as_root=True).AndRaise(
|
||||
exception.InstanceUnacceptable(fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
fh = ovzfile.OVZFile(fakes.TEMPFILE, 755)
|
||||
self.assertRaises(
|
||||
exception.InstanceUnacceptable, fh.set_permissions, 755)
|
||||
|
||||
def test_make_path_and_dir_success(self):
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'mkdir', '-p', mox.IgnoreArg(), run_as_root=True).AndReturn(
|
||||
('', fakes.ERRORMSG))
|
||||
self.mox.StubOutWithMock(openvz_conn.os.path, 'exists')
|
||||
openvz_conn.os.path.exists(mox.IgnoreArg()).AndReturn(False)
|
||||
self.mox.ReplayAll()
|
||||
fh = ovzfile.OVZFile(fakes.TEMPFILE, 755)
|
||||
fh.make_path()
|
||||
|
||||
def test_make_path_and_dir_exists(self):
|
||||
self.mox.StubOutWithMock(openvz_conn.os.path, 'exists')
|
||||
openvz_conn.os.path.exists(mox.IgnoreArg()).AndReturn(True)
|
||||
self.mox.ReplayAll()
|
||||
fh = ovzfile.OVZFile(fakes.TEMPFILE, 755)
|
||||
fh.make_path()
|
||||
214
nova/tests/openvz/test_network.py
Normal file
214
nova/tests/openvz/test_network.py
Normal file
@@ -0,0 +1,214 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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 mox
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.tests.openvz import fakes
|
||||
from nova.virt.openvz import driver as openvz_conn
|
||||
from nova.virt.openvz import network as openvz_net
|
||||
from nova.virt.openvz.network_drivers import network_bridge
|
||||
from oslo.config import cfg
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class OpenVzNetworkTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(OpenVzNetworkTestCase, self).setUp()
|
||||
try:
|
||||
CONF.injected_network_template
|
||||
except AttributeError as err:
|
||||
CONF.register_opt(
|
||||
cfg.StrOpt(
|
||||
'injected_network_template',
|
||||
default='nova/virt/interfaces.template',
|
||||
help='Stub for network template for testing purposes')
|
||||
)
|
||||
CONF.use_ipv6 = False
|
||||
self.fake_file = mox.MockAnything()
|
||||
self.fake_file.readlines().AndReturn(fakes.FILECONTENTS.split())
|
||||
self.fake_file.writelines(mox.IgnoreArg())
|
||||
self.fake_file.read().AndReturn(fakes.FILECONTENTS)
|
||||
|
||||
def test_ovz_network_interfaces_add_success(self):
|
||||
self.mox.StubOutWithMock(openvz_net.ovzshutdown, 'OVZShutdownFile')
|
||||
openvz_net.ovzshutdown.OVZShutdownFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZShutdownFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_net.ovzboot, 'OVZBootFile')
|
||||
openvz_net.ovzboot.OVZBootFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZBootFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_net.ovztc, 'OVZTcRules')
|
||||
openvz_net.ovztc.OVZTcRules().MultipleTimes().AndReturn(
|
||||
fakes.FakeOVZTcRules())
|
||||
self.mox.StubOutWithMock(openvz_net.OVZNetworkInterfaces, '_add_netif')
|
||||
openvz_net.OVZNetworkInterfaces._add_netif(
|
||||
fakes.INTERFACEINFO[0]['id'],
|
||||
mox.IgnoreArg(),
|
||||
mox.IgnoreArg(),
|
||||
mox.IgnoreArg()).MultipleTimes()
|
||||
self.mox.StubOutWithMock(
|
||||
openvz_net.OVZNetworkInterfaces, '_set_nameserver')
|
||||
self.mox.StubOutWithMock(openvz_net, 'OVZNetworkFile')
|
||||
openvz_net.OVZNetworkFile(
|
||||
('/var/lib/vz/private/%s/etc/network/interfaces' %
|
||||
fakes.INSTANCE['id'])).AndReturn(
|
||||
fakes.FakeOVZNetworkFile('/etc/network/interfaces'))
|
||||
openvz_net.OVZNetworkInterfaces._set_nameserver(
|
||||
fakes.INTERFACEINFO[0]['id'], fakes.INTERFACEINFO[0]['dns'])
|
||||
self.mox.ReplayAll()
|
||||
ifaces = openvz_net.OVZNetworkInterfaces(
|
||||
fakes.INTERFACEINFO)
|
||||
ifaces.add()
|
||||
|
||||
def test_ovz_network_interfaces_add_ip_success(self):
|
||||
self.mox.StubOutWithMock(openvz_net.ovzshutdown, 'OVZShutdownFile')
|
||||
openvz_net.ovzshutdown.OVZShutdownFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZShutdownFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_net.ovzboot, 'OVZBootFile')
|
||||
openvz_net.ovzboot.OVZBootFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZBootFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_conn.ovz_utils, 'execute')
|
||||
openvz_conn.ovz_utils.execute(
|
||||
'vzctl', 'set', fakes.INTERFACEINFO[0]['id'], '--save', '--ipadd',
|
||||
fakes.INTERFACEINFO[0]['address'], run_as_root=True).AndReturn(
|
||||
('', fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
ifaces = openvz_net.OVZNetworkInterfaces(fakes.INTERFACEINFO)
|
||||
ifaces._add_ip(
|
||||
fakes.INTERFACEINFO[0]['id'], fakes.INTERFACEINFO[0]['address'])
|
||||
|
||||
def test_ovz_network_interfaces_add_ip_failure(self):
|
||||
self.mox.StubOutWithMock(openvz_net.ovzshutdown, 'OVZShutdownFile')
|
||||
openvz_net.ovzshutdown.OVZShutdownFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZShutdownFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_net.ovzboot, 'OVZBootFile')
|
||||
openvz_net.ovzboot.OVZBootFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZBootFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_conn.ovz_utils, 'execute')
|
||||
openvz_conn.ovz_utils.execute(
|
||||
'vzctl', 'set', fakes.INTERFACEINFO[0]['id'], '--save',
|
||||
'--ipadd', fakes.INTERFACEINFO[0]['address'],
|
||||
run_as_root=True).AndRaise(
|
||||
exception.InstanceUnacceptable(fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
ifaces = openvz_net.OVZNetworkInterfaces(fakes.INTERFACEINFO)
|
||||
self.assertRaises(
|
||||
exception.InstanceUnacceptable, ifaces._add_ip,
|
||||
fakes.INTERFACEINFO[0]['id'], fakes.INTERFACEINFO[0]['address'])
|
||||
|
||||
def test_ovz_network_interfaces_add_netif(self):
|
||||
self.mox.StubOutWithMock(openvz_net.ovzshutdown, 'OVZShutdownFile')
|
||||
openvz_net.ovzshutdown.OVZShutdownFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZShutdownFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_net.ovzboot, 'OVZBootFile')
|
||||
openvz_net.ovzboot.OVZBootFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZBootFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_conn.ovz_utils, 'execute')
|
||||
openvz_conn.ovz_utils.execute(
|
||||
'vzctl', 'set', fakes.INTERFACEINFO[0]['id'], '--save',
|
||||
'--netif_add',
|
||||
'%s,,veth%s.%s,%s,%s' % (
|
||||
fakes.INTERFACEINFO[0]['name'],
|
||||
fakes.INTERFACEINFO[0]['id'],
|
||||
fakes.INTERFACEINFO[0]['name'],
|
||||
fakes.INTERFACEINFO[0]['mac'],
|
||||
fakes.INTERFACEINFO[0]['bridge']),
|
||||
run_as_root=True).AndReturn(('', fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
ifaces = openvz_net.OVZNetworkInterfaces(fakes.INTERFACEINFO)
|
||||
ifaces._add_netif(
|
||||
fakes.INTERFACEINFO[0]['id'],
|
||||
fakes.INTERFACEINFO[0]['name'],
|
||||
fakes.INTERFACEINFO[0]['bridge'],
|
||||
fakes.INTERFACEINFO[0]['mac']
|
||||
)
|
||||
|
||||
def test_filename_factory_debian_variant(self):
|
||||
self.mox.StubOutWithMock(openvz_net.ovzshutdown, 'OVZShutdownFile')
|
||||
openvz_net.ovzshutdown.OVZShutdownFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZShutdownFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_net.ovzboot, 'OVZBootFile')
|
||||
openvz_net.ovzboot.OVZBootFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZBootFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.ReplayAll()
|
||||
ifaces = openvz_net.OVZNetworkInterfaces(fakes.INTERFACEINFO)
|
||||
for filename in ifaces._filename_factory():
|
||||
self.assertFalse('//' in filename)
|
||||
|
||||
def test_set_nameserver_success(self):
|
||||
self.mox.StubOutWithMock(openvz_net.ovzshutdown, 'OVZShutdownFile')
|
||||
openvz_net.ovzshutdown.OVZShutdownFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZShutdownFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_net.ovzboot, 'OVZBootFile')
|
||||
openvz_net.ovzboot.OVZBootFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZBootFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_net.ovz_utils, 'execute')
|
||||
openvz_net.ovz_utils.execute(
|
||||
'vzctl', 'set', fakes.INTERFACEINFO[0]['id'], '--save',
|
||||
'--nameserver', fakes.INTERFACEINFO[0]['dns'],
|
||||
run_as_root=True).AndReturn(('', fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
ifaces = openvz_net.OVZNetworkInterfaces(fakes.INTERFACEINFO)
|
||||
ifaces._set_nameserver(
|
||||
fakes.INTERFACEINFO[0]['id'], fakes.INTERFACEINFO[0]['dns'])
|
||||
|
||||
def test_set_nameserver_failure(self):
|
||||
self.mox.StubOutWithMock(openvz_net.ovzshutdown, 'OVZShutdownFile')
|
||||
openvz_net.ovzshutdown.OVZShutdownFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZShutdownFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_net.ovzboot, 'OVZBootFile')
|
||||
openvz_net.ovzboot.OVZBootFile(
|
||||
fakes.INSTANCE['id'], mox.IgnoreArg()).AndReturn(
|
||||
fakes.FakeOVZBootFile(fakes.INSTANCE['id'], 700))
|
||||
self.mox.StubOutWithMock(openvz_conn.ovz_utils, 'execute')
|
||||
openvz_conn.ovz_utils.execute(
|
||||
'vzctl', 'set', fakes.INTERFACEINFO[0]['id'], '--save',
|
||||
'--nameserver', fakes.INTERFACEINFO[0]['dns'],
|
||||
run_as_root=True).AndRaise(exception.InstanceUnacceptable(
|
||||
fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
ifaces = openvz_net.OVZNetworkInterfaces(fakes.INTERFACEINFO)
|
||||
self.assertRaises(
|
||||
exception.InstanceUnacceptable, ifaces._set_nameserver,
|
||||
fakes.INTERFACEINFO[0]['id'], fakes.INTERFACEINFO[0]['dns'])
|
||||
|
||||
def test_ovz_network_bridge_driver_plug(self):
|
||||
self.mox.StubOutWithMock(
|
||||
openvz_conn.linux_net.LinuxBridgeInterfaceDriver,
|
||||
'ensure_vlan_bridge'
|
||||
)
|
||||
openvz_conn.linux_net.LinuxBridgeInterfaceDriver.ensure_vlan_bridge(
|
||||
mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()
|
||||
)
|
||||
self.mox.ReplayAll()
|
||||
driver = network_bridge.OVZNetworkBridgeDriver()
|
||||
for network, mapping in fakes.NETWORKINFO:
|
||||
driver.plug(fakes.INSTANCE, network, mapping)
|
||||
302
nova/tests/openvz/test_utils.py
Normal file
302
nova/tests/openvz/test_utils.py
Normal file
@@ -0,0 +1,302 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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 mox
|
||||
from nova import exception
|
||||
from nova.openstack.common import processutils
|
||||
from nova import test
|
||||
from nova.tests.openvz import fakes
|
||||
from nova.virt.openvz import utils as ovz_utils
|
||||
from oslo.config import cfg
|
||||
import uuid
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class OpenVzUtilsTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(OpenVzUtilsTestCase, self).setUp()
|
||||
|
||||
def test_execute_process_execution_error(self):
|
||||
self.mox.StubOutWithMock(ovz_utils.utils, 'execute')
|
||||
ovz_utils.utils.execute(
|
||||
'cat', '/proc/cpuinfo', run_as_root=False).AndRaise(
|
||||
processutils.ProcessExecutionError(fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(
|
||||
exception.InstanceUnacceptable, ovz_utils.execute, 'cat',
|
||||
'/proc/cpuinfo', run_as_root=False)
|
||||
|
||||
def test_execute_process_execution_error_no_raise_on_error(self):
|
||||
self.mox.StubOutWithMock(ovz_utils.utils, 'execute')
|
||||
ovz_utils.utils.execute(
|
||||
'cat', '/proc/cpuinfo', run_as_root=False).AndRaise(
|
||||
processutils.ProcessExecutionError)
|
||||
self.mox.ReplayAll()
|
||||
ovz_utils.execute(
|
||||
'cat', '/proc/cpuinfo', run_as_root=False, raise_on_error=False)
|
||||
|
||||
def test_mkfs_uuid(self):
|
||||
fs_uuid = uuid.uuid4()
|
||||
path = '/dev/sdgg'
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'mkfs', '-F', '-t', 'ext3', '-U', fs_uuid, path, run_as_root=True)
|
||||
self.mox.ReplayAll()
|
||||
ovz_utils.mkfs(path, 'ext3', fs_uuid)
|
||||
|
||||
def test_mkfs_label(self):
|
||||
path = '/dev/sdgg'
|
||||
fs_label = 'STORAGE'
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'mkfs', '-F', '-t', 'ext3', '-U', mox.IgnoreArg(), '-L', fs_label,
|
||||
path, run_as_root=True)
|
||||
self.mox.ReplayAll()
|
||||
ovz_utils.mkfs(path, 'ext3', None, fs_label)
|
||||
|
||||
def test_get_fs_uuid_success(self):
|
||||
dev = '/dev/sdgg'
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'blkid', '-o', 'value', '-s', 'UUID', dev,
|
||||
raise_on_error=False, run_as_root=True).AndReturn(fakes.BLKID)
|
||||
self.mox.ReplayAll()
|
||||
fs_uuid = ovz_utils.get_fs_uuid(dev)
|
||||
self.assertEqual(fs_uuid, fakes.BLKID.strip())
|
||||
|
||||
def test_get_fs_uuid_failure(self):
|
||||
dev = '/dev/sdgg'
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'blkid', '-o', 'value', '-s', 'UUID', dev,
|
||||
raise_on_error=False, run_as_root=True).AndReturn('\n')
|
||||
self.mox.ReplayAll()
|
||||
fs_uuid = ovz_utils.get_fs_uuid(dev)
|
||||
self.assertFalse(fs_uuid)
|
||||
|
||||
def test_get_vcpu_total_success(self):
|
||||
self.mox.StubOutWithMock(
|
||||
ovz_utils.multiprocessing, 'cpu_count')
|
||||
ovz_utils.multiprocessing.cpu_count().AndReturn(
|
||||
fakes.HOSTSTATS['vcpus'])
|
||||
self.mox.ReplayAll()
|
||||
result = ovz_utils.get_vcpu_total()
|
||||
self.assertEqual(result, fakes.HOSTSTATS['vcpus'])
|
||||
|
||||
def test_get_vcpu_total_failure(self):
|
||||
self.mox.StubOutWithMock(
|
||||
ovz_utils.multiprocessing, 'cpu_count')
|
||||
ovz_utils.multiprocessing.cpu_count().AndRaise(NotImplementedError)
|
||||
self.mox.ReplayAll()
|
||||
result = ovz_utils.get_vcpu_total()
|
||||
self.assertEqual(result, 0)
|
||||
|
||||
def test_get_cpuinfo_not_running_on_linux(self):
|
||||
self.mox.StubOutWithMock(ovz_utils, 'sys')
|
||||
self.mox.StubOutWithMock(ovz_utils.sys, 'platform')
|
||||
self.mox.StubOutWithMock(ovz_utils.sys.platform, 'upper')
|
||||
ovz_utils.sys.platform.upper().AndReturn('DARWIN')
|
||||
self.mox.ReplayAll()
|
||||
result = ovz_utils.get_cpuinfo()
|
||||
self.assertEqual(result, 0)
|
||||
|
||||
def test_iscsi_initiator(self):
|
||||
self.mox.StubOutWithMock(ovz_utils.utils, 'read_file_as_root')
|
||||
ovz_utils.utils.read_file_as_root(
|
||||
'/etc/iscsi/initiatorname.iscsi').AndReturn(fakes.ISCSIINITIATOR)
|
||||
self.mox.ReplayAll()
|
||||
iscsi_initiator = ovz_utils.get_iscsi_initiator()
|
||||
self.assertEqual(fakes.INITIATORNAME, iscsi_initiator)
|
||||
|
||||
def test_get_cpuunits_capability(self):
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'vzcpucheck', run_as_root=True).AndReturn('')
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(
|
||||
exception.InvalidCPUInfo, ovz_utils.get_cpuunits_capability)
|
||||
|
||||
def test_get_vcpu_used(self):
|
||||
self.mox.StubOutWithMock(ovz_utils, 'get_cpuunits_capability')
|
||||
ovz_utils.get_cpuunits_capability().AndReturn(fakes.CPUUNITSCAPA)
|
||||
self.mox.StubOutWithMock(ovz_utils, 'get_vcpu_total')
|
||||
ovz_utils.get_vcpu_total().AndReturn(fakes.HOSTSTATS['vcpus'])
|
||||
self.mox.ReplayAll()
|
||||
used = int(fakes.HOSTSTATS['vcpus'] *
|
||||
(float(fakes.CPUUNITSCAPA['subscribed']) /
|
||||
fakes.CPUUNITSCAPA['total']))
|
||||
result = ovz_utils.get_vcpu_used()
|
||||
self.assertEqual(result, used)
|
||||
|
||||
def test_get_memory_mb_total_not_running_on_linux(self):
|
||||
self.mox.StubOutWithMock(ovz_utils, 'sys')
|
||||
self.mox.StubOutWithMock(ovz_utils.sys, 'platform')
|
||||
self.mox.StubOutWithMock(ovz_utils.sys.platform, 'upper')
|
||||
ovz_utils.sys.platform.upper().AndReturn('DARWIN')
|
||||
self.mox.ReplayAll()
|
||||
result = ovz_utils.get_memory_mb_total()
|
||||
self.assertEqual(result, 1)
|
||||
|
||||
def test_get_memory_mb_used(self):
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'vzlist', '--all', '-H', '-o', 'ctid,privvmpages.l',
|
||||
raise_on_error=False, run_as_root=True).AndReturn(
|
||||
fakes.PRIVVMPAGES)
|
||||
self.mox.ReplayAll()
|
||||
memory_used = (((int(
|
||||
fakes.PRIVVMPAGES_1024.strip().split()[1]) * 4096) / 1024 ** 2) +
|
||||
((int(
|
||||
fakes.PRIVVMPAGES_2048.strip().split()[1]) *
|
||||
4096) / 1024 ** 2))
|
||||
result = ovz_utils.get_memory_mb_used()
|
||||
self.assertEqual(memory_used, result)
|
||||
|
||||
def test_get_memory_mb_used_instance(self):
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'vzlist', '-H', '-o', 'ctid,privvmpages.l',
|
||||
str(fakes.INSTANCE['id']),
|
||||
raise_on_error=False, run_as_root=True).AndReturn(
|
||||
fakes.PRIVVMPAGES_2048)
|
||||
self.mox.ReplayAll()
|
||||
memory_used = ((int(
|
||||
fakes.PRIVVMPAGES_2048.strip().split()[1]) * 4096) / 1024 ** 2)
|
||||
result = ovz_utils.get_memory_mb_used(fakes.INSTANCE['id'])
|
||||
self.assertEqual(memory_used, result)
|
||||
|
||||
def test_get_local_gb_total(self):
|
||||
self.mox.StubOutWithMock(ovz_utils.os, 'statvfs')
|
||||
ovz_utils.os.statvfs(
|
||||
CONF.ovz_ve_private_dir).AndReturn(fakes.STATVFSRESULT)
|
||||
self.mox.ReplayAll()
|
||||
total = ((fakes.STATVFSRESULT.f_frsize * fakes.STATVFSRESULT.f_blocks)
|
||||
/ 1024 ** 3)
|
||||
result = ovz_utils.get_local_gb_total()
|
||||
self.assertEqual(total, result)
|
||||
|
||||
def test_get_local_gb_used(self):
|
||||
self.mox.StubOutWithMock(ovz_utils.os, 'statvfs')
|
||||
ovz_utils.os.statvfs(
|
||||
CONF.ovz_ve_private_dir).AndReturn(fakes.STATVFSRESULT)
|
||||
self.mox.ReplayAll()
|
||||
used = ((fakes.STATVFSRESULT.f_frsize *
|
||||
(fakes.STATVFSRESULT.f_blocks - fakes.STATVFSRESULT.f_bfree)
|
||||
) / (1024 ** 3))
|
||||
result = ovz_utils.get_local_gb_used()
|
||||
self.assertEqual(used, result)
|
||||
|
||||
def test_get_hypervisor_version(self):
|
||||
self.mox.StubOutWithMock(ovz_utils.platform, 'uname')
|
||||
ovz_utils.platform.uname().AndReturn(fakes.UNAME)
|
||||
self.mox.ReplayAll()
|
||||
result = ovz_utils.get_hypervisor_version()
|
||||
self.assertEqual(result, fakes.UNAME[2])
|
||||
|
||||
def test_delete_path_good(self):
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'rmdir', CONF.ovz_ve_private_dir,
|
||||
run_as_root=True).AndReturn(('', ''))
|
||||
self.mox.ReplayAll()
|
||||
self.assertTrue(ovz_utils.delete_path(CONF.ovz_ve_private_dir))
|
||||
|
||||
def test_delete_path_bad(self):
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute(
|
||||
'rmdir', CONF.ovz_ve_private_dir,
|
||||
run_as_root=True).AndRaise(exception.InstanceUnacceptable(
|
||||
fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
self.assertFalse(ovz_utils.delete_path(CONF.ovz_ve_private_dir))
|
||||
|
||||
def test_set_permissions(self):
|
||||
perms = 755
|
||||
filename = '/tmp/testfile'
|
||||
self.mox.StubOutWithMock(ovz_utils, 'execute')
|
||||
ovz_utils.execute('chmod', perms, filename, run_as_root=True)
|
||||
self.mox.ReplayAll()
|
||||
ovz_utils.set_permissions(filename, perms)
|
||||
|
||||
def test_save_instance_metadata_success(self):
|
||||
self.mox.StubOutWithMock(ovz_utils.context, 'get_admin_context')
|
||||
ovz_utils.context.get_admin_context().AndReturn(fakes.ADMINCONTEXT)
|
||||
self.mox.StubOutWithMock(ovz_utils.conductor, 'instance_get')
|
||||
ovz_utils.conductor.instance_get(
|
||||
fakes.ADMINCONTEXT, fakes.INSTANCE['id']).AndReturn(fakes.INSTANCE)
|
||||
self.mox.StubOutWithMock(
|
||||
ovz_utils.conductor, 'instance_update')
|
||||
ovz_utils.conductor.instance_update(
|
||||
fakes.ADMINCONTEXT, fakes.INSTANCE['uuid'],
|
||||
system_metadata=ovz_utils.format_system_metadata(
|
||||
fakes.INSTANCE['system_metadata']))
|
||||
self.mox.ReplayAll()
|
||||
ovz_utils.save_instance_metadata(
|
||||
fakes.INSTANCE['id'], fakes.METAKEY, fakes.METAVALUE)
|
||||
|
||||
def test_save_instance_metadata_not_found(self):
|
||||
self.mox.StubOutWithMock(ovz_utils.context, 'get_admin_context')
|
||||
ovz_utils.context.get_admin_context().AndReturn(fakes.ADMINCONTEXT)
|
||||
self.mox.StubOutWithMock(ovz_utils.conductor, 'instance_get')
|
||||
ovz_utils.conductor.instance_get(
|
||||
fakes.ADMINCONTEXT, fakes.INSTANCE['id']).AndReturn(fakes.INSTANCE)
|
||||
self.mox.StubOutWithMock(
|
||||
ovz_utils.conductor, 'instance_update')
|
||||
ovz_utils.conductor.instance_update(
|
||||
fakes.ADMINCONTEXT, fakes.INSTANCE['uuid'],
|
||||
system_metadata=ovz_utils.format_system_metadata(
|
||||
fakes.INSTANCE['system_metadata'])).AndRaise(
|
||||
exception.InstanceNotFound(fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
ovz_utils.save_instance_metadata(
|
||||
fakes.INSTANCE['id'], fakes.METAKEY, fakes.METAVALUE)
|
||||
|
||||
def test_read_instance_metadata_success(self):
|
||||
self.mox.StubOutWithMock(ovz_utils.context, 'get_admin_context')
|
||||
ovz_utils.context.get_admin_context().AndReturn(fakes.ADMINCONTEXT)
|
||||
self.mox.StubOutWithMock(ovz_utils.conductor, 'instance_get')
|
||||
ovz_utils.conductor.instance_get(
|
||||
fakes.ADMINCONTEXT, fakes.INSTANCE['id']).AndReturn(fakes.INSTANCE)
|
||||
self.mox.ReplayAll()
|
||||
meta = ovz_utils.read_instance_metadata(fakes.INSTANCE['id'])
|
||||
self.assertTrue(isinstance(meta, dict))
|
||||
self.assertEqual(meta[fakes.METAKEY], fakes.METAVALUE)
|
||||
|
||||
def test_read_instance_metadata_not_found(self):
|
||||
self.mox.StubOutWithMock(ovz_utils.context, 'get_admin_context')
|
||||
ovz_utils.context.get_admin_context().AndReturn(fakes.ADMINCONTEXT)
|
||||
self.mox.StubOutWithMock(ovz_utils.conductor, 'instance_get')
|
||||
ovz_utils.conductor.instance_get(
|
||||
fakes.ADMINCONTEXT, fakes.INSTANCE['id']).AndRaise(
|
||||
exception.InstanceNotFound(fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
meta = ovz_utils.read_instance_metadata(fakes.INSTANCE['id'])
|
||||
self.assertTrue(isinstance(meta, dict))
|
||||
self.assertTrue(len(meta) == 0)
|
||||
|
||||
def test_read_instance_metadata_dberror(self):
|
||||
self.mox.StubOutWithMock(ovz_utils.context, 'get_admin_context')
|
||||
ovz_utils.context.get_admin_context().AndReturn(fakes.ADMINCONTEXT)
|
||||
self.mox.StubOutWithMock(ovz_utils.conductor, 'instance_get')
|
||||
ovz_utils.conductor.instance_get(
|
||||
fakes.ADMINCONTEXT, fakes.INSTANCE['id']).AndRaise(
|
||||
exception.InstanceNotFound(fakes.ERRORMSG))
|
||||
self.mox.ReplayAll()
|
||||
meta = ovz_utils.read_instance_metadata(fakes.INSTANCE['id'])
|
||||
self.assertTrue(isinstance(meta, dict))
|
||||
self.assertTrue(len(meta) == 0)
|
||||
@@ -29,13 +29,14 @@ from nova import exception
|
||||
from nova.network import linux_net
|
||||
from nova.openstack.common import importutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import utils
|
||||
from nova.openstack.common import loopingcall
|
||||
from nova.virt import driver
|
||||
from nova.virt import images
|
||||
from nova.virt.openvz import file as ovzfile
|
||||
from nova.virt.openvz.file_ext import boot as ovzboot
|
||||
from nova.virt.openvz.file_ext import ext_storage
|
||||
from nova.virt.openvz.file_ext import shutdown as ovzshutdown
|
||||
from nova.virt.openvz import migration as ovz_migration
|
||||
from nova.virt.openvz import network as ovznetwork
|
||||
from nova.virt.openvz.network_drivers import tc as ovztc
|
||||
from nova.virt.openvz import utils as ovz_utils
|
||||
@@ -43,6 +44,7 @@ from nova.virt.openvz.volume_drivers import iscsi as ovziscsi
|
||||
import os
|
||||
from oslo.config import cfg
|
||||
import socket
|
||||
import time
|
||||
|
||||
openvz_conn_opts = [
|
||||
cfg.StrOpt('ovz_template_path',
|
||||
@@ -85,6 +87,28 @@ openvz_conn_opts = [
|
||||
cfg.StrOpt('ovz_tmp_dir',
|
||||
default='/var/tmp',
|
||||
help='Directory to use as temporary storage'),
|
||||
cfg.StrOpt('ovz_migration_method',
|
||||
default='python',
|
||||
help='Method to use for migrations'),
|
||||
cfg.StrOpt('ovz_migration_user',
|
||||
default='nova',
|
||||
help='User to use for running migrations'),
|
||||
cfg.StrOpt('ovz_migration_transport',
|
||||
default='rsync',
|
||||
help='Method to use to transport migrations'),
|
||||
cfg.StrOpt('ovz_vzmigrate_opts',
|
||||
default=None,
|
||||
help='Optional arguments to pass to vzmigrate'),
|
||||
cfg.BoolOpt('ovz_vzmigrate_online_migration',
|
||||
default=True,
|
||||
help='Perform an online migration of a container'),
|
||||
cfg.BoolOpt('ovz_vzmigrate_destroy_source_container_on_migrate',
|
||||
default=True,
|
||||
help='If a migration is successful do we delete the '
|
||||
'container on the old host'),
|
||||
cfg.BoolOpt('ovz_vzmigrate_verbose_migration_logging',
|
||||
default=True,
|
||||
help='Log verbose messages from vzmigrate command'),
|
||||
cfg.BoolOpt('ovz_use_cpuunit',
|
||||
default=True,
|
||||
help='Use OpenVz cpuunits for guaranteed minimums'),
|
||||
@@ -341,7 +365,7 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
for network, mapping in network_info:
|
||||
if mapping['ips']:
|
||||
has_networking = True
|
||||
except Exception:
|
||||
except ValueError:
|
||||
has_networking = False
|
||||
if has_networking:
|
||||
self.plug_vifs(instance, network_info)
|
||||
@@ -366,7 +390,7 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
admin_password)
|
||||
|
||||
# Begin making our looping async call
|
||||
timer = utils.FixedIntervalLoopingCall()
|
||||
timer = loopingcall.FixedIntervalLoopingCall()
|
||||
|
||||
# I stole this from the libvirt driver but it is appropriate to
|
||||
# have this looping timer call so that if a VE doesn't start right
|
||||
@@ -789,7 +813,8 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
'--numfile', max_file_descriptors,
|
||||
run_as_root=True)
|
||||
|
||||
def _set_instance_size(self, instance, network_info=None):
|
||||
def _set_instance_size(self, instance, network_info=None,
|
||||
is_migration=False):
|
||||
"""
|
||||
Given that these parameters make up and instance's 'size' we are
|
||||
bundling them together to make resizing an instance on the host
|
||||
@@ -800,14 +825,32 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
|
||||
LOG.debug(_('Instance system metadata: %s') % instance_size)
|
||||
|
||||
instance_memory_bytes = ((int(instance_size['instance_type_memory_mb'])
|
||||
* 1024) * 1024)
|
||||
instance_memory_pages = self._calc_pages(
|
||||
instance_size['instance_type_memory_mb'])
|
||||
percent_of_resource = self._percent_of_resource(
|
||||
instance_size['instance_type_memory_mb'])
|
||||
if is_migration:
|
||||
instance_memory_mb = instance_size.get(
|
||||
'new_instance_type_memory_mb', None)
|
||||
if not instance_memory_mb:
|
||||
instance_memory_mb = instance_size.get(
|
||||
'instance_type_memory_mb')
|
||||
instance_vcpus = instance_size.get('new_instance_type_vcpus', None)
|
||||
if not instance_vcpus:
|
||||
instance_vcpus = instance_size.get('instance_type_vcpus')
|
||||
instance_root_gb = instance_size.get(
|
||||
'new_instance_type_root_gb', None)
|
||||
if not instance_root_gb:
|
||||
instance_root_gb = instance_size.get('instance_type_root_gb')
|
||||
else:
|
||||
instance_memory_mb = instance_size.get('instance_type_memory_mb')
|
||||
instance_vcpus = instance_size.get('instance_type_vcpus')
|
||||
instance_root_gb = instance_size.get('instance_type_root_gb')
|
||||
|
||||
instance_memory_mb = int(instance_memory_mb)
|
||||
instance_vcpus = int(instance_vcpus)
|
||||
instance_root_gb = int(instance_root_gb)
|
||||
|
||||
instance_memory_bytes = ((instance_memory_mb * 1024) * 1024)
|
||||
instance_memory_pages = self._calc_pages(instance_memory_mb)
|
||||
percent_of_resource = self._percent_of_resource(instance_memory_mb)
|
||||
|
||||
instance_memory_mb = int(instance_size['instance_type_memory_mb'])
|
||||
memory_unit_size = int(CONF.ovz_memory_unit_size)
|
||||
max_fd_per_unit = int(CONF.ovz_file_descriptors_per_unit)
|
||||
max_fd = int(instance_memory_mb / memory_unit_size) * max_fd_per_unit
|
||||
@@ -821,18 +864,16 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
if CONF.ovz_use_cpulimit:
|
||||
self._set_cpulimit(instance, percent_of_resource)
|
||||
if CONF.ovz_use_cpus:
|
||||
self._set_cpus(instance, instance_size['instance_type_vcpus'])
|
||||
self._set_cpus(instance, instance_vcpus)
|
||||
if CONF.ovz_use_ioprio:
|
||||
self._set_ioprio(
|
||||
instance, int(instance_size['instance_type_memory_mb']))
|
||||
self._set_ioprio(instance, instance_memory_mb)
|
||||
if CONF.ovz_use_disk_quotas:
|
||||
self._set_diskspace(
|
||||
instance, instance_size['instance_type_root_gb'])
|
||||
self._set_diskspace(instance, instance_root_gb)
|
||||
|
||||
if network_info:
|
||||
self._generate_tc_rules(instance, network_info)
|
||||
self._generate_tc_rules(instance, network_info, is_migration)
|
||||
|
||||
def _generate_tc_rules(self, instance, network_info):
|
||||
def _generate_tc_rules(self, instance, network_info, is_migration=False):
|
||||
"""
|
||||
Utility method to generate tc info for instances that have been
|
||||
resized and/or migrated
|
||||
@@ -840,10 +881,22 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
LOG.debug(_('Setting network sizing'))
|
||||
bf = ovzboot.OVZBootFile(instance['id'], 755)
|
||||
sf = ovzshutdown.OVZShutdownFile(instance['id'], 755)
|
||||
|
||||
if not is_migration:
|
||||
with sf:
|
||||
LOG.debug(_('Cleaning TC rules for %s') % instance['id'])
|
||||
sf.read()
|
||||
sf.run_contents(raise_on_error=False)
|
||||
|
||||
# On resize we throw away existing tc_id and make a new one
|
||||
# because the resize *could* have taken place on a different host
|
||||
# where the tc_id is already in use.
|
||||
meta = ovz_utils.read_instance_metadata(instance['id'])
|
||||
tc_id = meta.get('tc_id', None)
|
||||
if tc_id:
|
||||
ovz_utils.remove_instance_metadata_key(instance['id'], 'tc_id')
|
||||
|
||||
with sf:
|
||||
LOG.debug(_('Cleaning TC rules for %s') % instance['id'])
|
||||
sf.read()
|
||||
sf.run_contents()
|
||||
sf.set_contents(list())
|
||||
|
||||
with bf:
|
||||
@@ -1148,7 +1201,7 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
context, instance['uuid'],
|
||||
{'power_state': power_state.NOSTATE})
|
||||
LOG.error(_('During reboot %s disappeared') % instance['name'])
|
||||
raise utils.LoopingCallDone
|
||||
raise loopingcall.LoopingCallDone
|
||||
|
||||
if state == power_state.RUNNING:
|
||||
self.virtapi.instance_update(
|
||||
@@ -1160,12 +1213,12 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
with bf:
|
||||
bf.read()
|
||||
bf.run_contents()
|
||||
raise utils.LoopingCallDone
|
||||
raise loopingcall.LoopingCallDone
|
||||
elif state == power_state.NOSTATE:
|
||||
LOG.error(_('Error rebooting %s') % instance['name'])
|
||||
raise utils.LoopingCallDone
|
||||
raise loopingcall.LoopingCallDone
|
||||
|
||||
timer = utils.FixedIntervalLoopingCall(_wait_for_reboot)
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_reboot)
|
||||
return timer.start(interval=0.5)
|
||||
|
||||
def _inject_files(self, instance, files_to_inject):
|
||||
@@ -1339,7 +1392,7 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
self._detach_volumes(
|
||||
instance, block_device_info)
|
||||
|
||||
timer = utils.FixedIntervalLoopingCall()
|
||||
timer = loopingcall.FixedIntervalLoopingCall()
|
||||
|
||||
def _wait_for_destroy():
|
||||
try:
|
||||
@@ -1427,7 +1480,28 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
ext_str.add_volume(mountpoint, connection_info)
|
||||
ext_str.save()
|
||||
|
||||
def _detach_volumes(self, instance, block_device_mapping):
|
||||
def _disconnect_volume(self, connection_info, instance, mountpoint,
|
||||
container_is_running=True):
|
||||
"""
|
||||
Necessary for migrations to disconnect but not permanently remove
|
||||
volumes.
|
||||
|
||||
:param connection_info:
|
||||
:param instance:
|
||||
:param mountpoint:
|
||||
:return:
|
||||
"""
|
||||
if connection_info['driver_volume_type'] == 'iscsi':
|
||||
volume = ovziscsi.OVZISCSIStorageDriver(
|
||||
instance['id'], mountpoint, connection_info)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
_('There are no suitable storage drivers'))
|
||||
|
||||
volume.detach(container_is_running)
|
||||
|
||||
def _detach_volumes(self, instance, block_device_mapping,
|
||||
disconnect_only=False, container_is_running=True):
|
||||
"""
|
||||
Move bulk operations of volume connections back into the driver as
|
||||
they are relevant here and no longer need to be in their own module.
|
||||
@@ -1436,9 +1510,14 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
:return: None
|
||||
"""
|
||||
for volume in block_device_mapping['block_device_mapping']:
|
||||
self.detach_volume(
|
||||
volume['connection_info'], instance,
|
||||
volume['mount_device'])
|
||||
if disconnect_only:
|
||||
self._disconnect_volume(volume['connection_info'], instance,
|
||||
volume['mount_device'],
|
||||
container_is_running)
|
||||
else:
|
||||
self.detach_volume(
|
||||
volume['connection_info'], instance,
|
||||
volume['mount_device'])
|
||||
|
||||
def detach_volume(self, connection_info, instance, mountpoint=None):
|
||||
"""
|
||||
@@ -1448,14 +1527,7 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
if not mountpoint:
|
||||
mountpoint = connection_info['mount_device']
|
||||
|
||||
if connection_info['driver_volume_type'] == 'iscsi':
|
||||
volume = ovziscsi.OVZISCSIStorageDriver(
|
||||
instance['id'], mountpoint, connection_info)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
_('There are no suitable storage drivers'))
|
||||
|
||||
volume.detach()
|
||||
self._disconnect_volume(connection_info, instance, mountpoint)
|
||||
|
||||
# Remove storage connection info from the storage repo for the
|
||||
# instance.
|
||||
@@ -1505,10 +1577,6 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
|
||||
if state != new_state:
|
||||
state = new_state
|
||||
# Set the new instance power_state
|
||||
self.virtapi.instance_update(
|
||||
context.get_admin_context(), instance['uuid'],
|
||||
{'power_state': state})
|
||||
|
||||
LOG.debug(
|
||||
_('OpenVz says instance %(id)s is in state %(state)s') %
|
||||
@@ -1612,6 +1680,309 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
'host': CONF.host
|
||||
}
|
||||
|
||||
def migrate_disk_and_power_off(self, context, instance, dest,
|
||||
instance_type, network_info,
|
||||
block_device_info=None):
|
||||
"""
|
||||
Transfers the disk of a running instance in multiple phases, turning
|
||||
off the instance before the end.
|
||||
"""
|
||||
LOG.debug(_('Migration context: %s') % context)
|
||||
LOG.debug(_('Migration instance: %s') % instance)
|
||||
LOG.debug(_('Migration dest: %s') % dest)
|
||||
LOG.debug(_('Migration instance_type: %s') % instance_type)
|
||||
LOG.debug(_('Migration network_info: %s') % network_info)
|
||||
|
||||
if not dest:
|
||||
LOG.error(_('No destination given to migration'))
|
||||
raise exception.MigrationError(
|
||||
_('Migration destination is: %s') % dest)
|
||||
|
||||
if dest == CONF.host:
|
||||
# if this is an inplace resize we don't need to do any of this
|
||||
LOG.debug(_('This is an inplace migration'))
|
||||
ovz_utils.save_instance_metadata(instance['id'], 'migration_type',
|
||||
'resize_in_place')
|
||||
return
|
||||
|
||||
# Validate the ovz_migration_method flag
|
||||
if CONF.ovz_migration_method not in ['vzmigrate', 'python']:
|
||||
raise exception.MigrationError(
|
||||
_('I do not understand your migration method'))
|
||||
|
||||
# Find out if we have external volumes, this will determine
|
||||
# if we will freeze the instance and attempt to preserve state of if
|
||||
# we will stop the instance completely to preserve the integrity
|
||||
# of the attached filesystems.
|
||||
if block_device_info:
|
||||
live_migration = False
|
||||
self._stop(instance)
|
||||
self._detach_volumes(
|
||||
instance, block_device_info, True, live_migration)
|
||||
else:
|
||||
live_migration = True
|
||||
self.suspend(instance)
|
||||
|
||||
LOG.debug(_('ovz_migration_method is: %s') %
|
||||
CONF.ovz_migration_method)
|
||||
if CONF.ovz_migration_method == 'vzmigrate':
|
||||
self._vzmigration_send_to_host(instance, dest)
|
||||
elif CONF.ovz_migration_method == 'python':
|
||||
self._pymigration_send_to_host(
|
||||
instance, ovz_utils.generate_network_dict(instance['id'],
|
||||
network_info),
|
||||
block_device_info, dest, live_migration)
|
||||
|
||||
def _pymigration_send_to_host(self, instance, network_info,
|
||||
block_device_info, dest, live_migration):
|
||||
"""
|
||||
This performs a more complex but more secure migration using a pure
|
||||
python implemented vz migration driver.
|
||||
"""
|
||||
LOG.debug(_('Beginning pure python based migration'))
|
||||
mobj = ovz_migration.OVZMigration(
|
||||
instance, network_info, block_device_info, dest, live_migration)
|
||||
mobj.dump_and_transfer_instance()
|
||||
mobj.send()
|
||||
|
||||
def _vzmigration_send_to_host(self, instance, dest):
|
||||
"""
|
||||
This performs a simple migration using openvz's supplied vzmigrate
|
||||
script. It requires shared keys for root across all hosts and
|
||||
does not support containers with externally attached volumes. And
|
||||
currently TC rules aren't preserved.
|
||||
"""
|
||||
LOG.debug(_('Beginning vzmigrate based migration'))
|
||||
cmd = ['vzmigrate']
|
||||
if CONF.ovz_vzmigrate_opts:
|
||||
if isinstance(CONF.ovz_vzmigrate_opts, str):
|
||||
cmd += CONF.ovz_vzmigrate_opts.split()
|
||||
elif isinstance(CONF.ovz_vzmigrate_opts, list):
|
||||
cmd += CONF.ovz_vzmigrate_opts
|
||||
if CONF.ovz_vzmigrate_online_migration:
|
||||
cmd.append('--online')
|
||||
if CONF.ovz_vzmigrate_destroy_source_container_on_migrate:
|
||||
cmd += ['-r', 'yes']
|
||||
if CONF.ovz_vzmigrate_verbose_migration_logging:
|
||||
cmd.append('-v')
|
||||
cmd.append(dest)
|
||||
cmd.append(instance['id'])
|
||||
LOG.debug(
|
||||
_('Beginning the migration of %(instance_id)s to %(dest)s') %
|
||||
{'instance_id': instance['id'], 'dest': dest})
|
||||
out = ovz_utils.execute(*cmd, run_as_root=True)
|
||||
LOG.debug(_('Output from migration process: %s') % out)
|
||||
|
||||
def finish_migration(self, context, migration, instance, disk_info,
|
||||
network_info, image_meta, resize_instance,
|
||||
block_device_info=None):
|
||||
"""Completes a resize, turning on the migrated instance
|
||||
|
||||
:param network_info:
|
||||
:py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
|
||||
:param image_meta: image object returned by nova.image.glance that
|
||||
defines the image from which this instance
|
||||
was created
|
||||
"""
|
||||
# Get the instance metadata to see what we need to do
|
||||
meta = ovz_utils.read_instance_metadata(instance['id'])
|
||||
migration_type = meta.get('migration_type')
|
||||
|
||||
if migration_type == 'resize_in_place':
|
||||
# This is a resize on the same host so its simple, resize
|
||||
# in place and then exit the method
|
||||
self._set_instance_size(instance, network_info, False)
|
||||
return
|
||||
|
||||
if block_device_info:
|
||||
# It is assumed that if there are externally attached volumes
|
||||
# then this is not a live migration.
|
||||
live_migration = False
|
||||
else:
|
||||
live_migration = True
|
||||
|
||||
if CONF.ovz_migration_method == 'vzmigrate':
|
||||
self._vzmigrate_setup_dest_host(instance, network_info)
|
||||
elif CONF.ovz_migration_method == 'python':
|
||||
self._pymigrate_finish_migration(instance,
|
||||
network_info,
|
||||
live_migration)
|
||||
|
||||
if block_device_info:
|
||||
# Once the files have been moved into place we need to attach
|
||||
# volumes.
|
||||
self._attach_volumes(instance, block_device_info)
|
||||
|
||||
# Somehow the name of the instance is lost in migration so
|
||||
# set it here.
|
||||
self._set_name(instance)
|
||||
|
||||
# The uuid is lost from the description field in the migration
|
||||
# so set it here.
|
||||
self._set_description(instance)
|
||||
|
||||
if resize_instance:
|
||||
LOG.debug(_('A resize after migration was requested: %s') %
|
||||
instance['id'])
|
||||
self._set_instance_size(instance, network_info, True)
|
||||
LOG.debug(_('Resized instance after migration: %s') %
|
||||
instance['id'])
|
||||
else:
|
||||
LOG.debug(_('Regenerating TC rules for instance %s') %
|
||||
instance['id'])
|
||||
self._generate_tc_rules(instance, network_info, True)
|
||||
LOG.debug(_('Regenerated TC rules for instance %s') %
|
||||
instance['id'])
|
||||
|
||||
if not live_migration:
|
||||
self._start(instance)
|
||||
|
||||
def _pymigrate_finish_migration(self, instance, network_info,
|
||||
live_migration):
|
||||
"""
|
||||
Take all transferred files and put them back into place to create a
|
||||
working instance.
|
||||
"""
|
||||
LOG.debug(_('Beginning python based finish_migration'))
|
||||
interfaces = ovz_utils.generate_network_dict(instance['id'],
|
||||
network_info)
|
||||
mobj = ovz_migration.OVZMigration(
|
||||
instance, interfaces, None, live_migration)
|
||||
mobj.undump_instance()
|
||||
|
||||
# Crude but we just need to give things time to settle before cleaning
|
||||
# up all the dumped stuff
|
||||
# TODO(imsplitbit): maybe a wait_for_start method with a looping
|
||||
# timer is better here, will check into it soon
|
||||
time.sleep(5)
|
||||
mobj.cleanup_destination()
|
||||
LOG.debug(_('Finished python based finish_migration'))
|
||||
|
||||
def _vzmigrate_setup_dest_host(self, instance, network_info):
|
||||
"""
|
||||
Sequence to run on destination host should the migration be done
|
||||
by the vzmigrate tools.
|
||||
"""
|
||||
LOG.debug(_('Stopping instance: %s') % instance['id'])
|
||||
self._stop(instance)
|
||||
LOG.debug(_('Stopped instance: %s') % instance['id'])
|
||||
|
||||
self.plug_vifs(instance, network_info)
|
||||
|
||||
LOG.debug(_('Starting instance: %s') % instance['id'])
|
||||
self._start(instance)
|
||||
LOG.debug(_('Started instance: %s') % instance['id'])
|
||||
|
||||
def confirm_migration(self, migration, instance, network_info):
|
||||
"""
|
||||
Run on the source host to confirm the migration and cleans up the
|
||||
the files from the source host.
|
||||
"""
|
||||
LOG.debug(_('Beginning confirm migration for %s') % instance['id'])
|
||||
|
||||
# Get the instance metadata to see what we need to do
|
||||
meta = ovz_utils.read_instance_metadata(instance['id'])
|
||||
migration_type = meta.get('migration_type')
|
||||
|
||||
live_migration = True
|
||||
ext_str = ext_storage.OVZExtStorage(instance['id'])
|
||||
if ext_str._volumes:
|
||||
live_migration = False
|
||||
|
||||
if migration_type == 'resize_in_place':
|
||||
# This is a resize on the same host so its simple, resize
|
||||
# in place and then exit the method
|
||||
if ovz_utils.remove_instance_metadata_key(instance['id'],
|
||||
'migration_type'):
|
||||
LOG.debug(_('Removed migration_type metadata'))
|
||||
else:
|
||||
LOG.debug(_('Failed to remove migration_type metadata'))
|
||||
return
|
||||
|
||||
try:
|
||||
status = self.get_info(instance)['state']
|
||||
LOG.debug(_('State in confirm_migration: %s') % status)
|
||||
if status == power_state.RUNNING:
|
||||
LOG.warn(
|
||||
_('Instance %s is running on source after migration') %
|
||||
instance['uuid'])
|
||||
self._stop(instance)
|
||||
status = self.get_info(instance)['state']
|
||||
|
||||
if status == power_state.SHUTDOWN:
|
||||
LOG.debug(_('Cleaning up migration on source host'))
|
||||
mobj = ovz_migration.OVZMigration(
|
||||
instance, ovz_utils.generate_network_dict(
|
||||
instance['id'], network_info), None, live_migration)
|
||||
mobj.cleanup_source()
|
||||
self._destroy(instance['id'])
|
||||
self._clean_orphaned_files(instance['id'])
|
||||
else:
|
||||
LOG.warn(
|
||||
_('Check instance: %(instance_id)s, it may be broken. '
|
||||
'power_state: %(ps)s') %
|
||||
{'instance_id': instance['id'],
|
||||
'ps': status})
|
||||
except exception.InstanceNotFound:
|
||||
LOG.warn(
|
||||
_('Instance %s not found, migration cleaned itself up?') %
|
||||
instance['id'])
|
||||
except exception.InstanceUnacceptable:
|
||||
LOG.error(_('Failed to stop and destroy the instance'))
|
||||
LOG.debug(_('Finished confirm migration for %s') % instance['id'])
|
||||
|
||||
def finish_revert_migration(self, instance, network_info,
|
||||
block_device_info=None):
|
||||
"""Finish reverting a resize, powering back on the instance."""
|
||||
# Get the instance metadata to see what we need to do
|
||||
LOG.debug(_('Beginning finish_revert_migration'))
|
||||
meta = ovz_utils.read_instance_metadata(instance['id'])
|
||||
migration_type = meta.get('migration_type')
|
||||
|
||||
if migration_type == 'resize_in_place':
|
||||
# This is a resize on the same host so its simple, resize
|
||||
# in place and then exit the method
|
||||
LOG.debug(_('Reverting in-place migration for %s') %
|
||||
instance['id'])
|
||||
self._set_instance_size(instance, network_info)
|
||||
if ovz_utils.remove_instance_metadata_key(instance['id'],
|
||||
'migration_type'):
|
||||
LOG.debug(_('Removed migration_type metadata'))
|
||||
LOG.debug(_('Done reverting in-place migration for %s') %
|
||||
instance['id'])
|
||||
else:
|
||||
LOG.debug(_('Failed to remove migration_type metadata'))
|
||||
return
|
||||
|
||||
if block_device_info:
|
||||
LOG.debug(_('Instance %s has volumes') % instance['id'])
|
||||
# the instance has external volumes and was not a live migration
|
||||
# so we need to reattach external volumes
|
||||
live_migration = False
|
||||
LOG.debug(_('Starting instance %s, after revert') % instance['id'])
|
||||
ext_str = ext_storage.OVZExtStorage(instance['id'])
|
||||
|
||||
for mountpoint, connection_info in ext_str.volumes():
|
||||
self.attach_volume(connection_info, instance, mountpoint)
|
||||
|
||||
self._start(instance)
|
||||
else:
|
||||
LOG.debug(_('Instance %s has no volumes') % instance['id'])
|
||||
live_migration = True
|
||||
LOG.debug(_('Resuming live migration for %s') % instance['id'])
|
||||
self.resume(instance, network_info)
|
||||
|
||||
mobj = ovz_migration.OVZMigration(
|
||||
instance, ovz_utils.generate_network_dict(
|
||||
instance['id'], network_info), None, live_migration)
|
||||
mobj.cleanup_files()
|
||||
|
||||
def get_host_ip_addr(self):
|
||||
"""
|
||||
Retrieves the IP address of the host
|
||||
"""
|
||||
return CONF.my_ip
|
||||
|
||||
# TODO(imsplitbit): finish the outstanding software contract with nova
|
||||
# All methods in the driver below this need to be worked out.
|
||||
def snapshot(self, context, instance, image_id, update_task_state):
|
||||
@@ -1886,13 +2257,6 @@ class OpenVzDriver(driver.ComputeDriver):
|
||||
running VM"""
|
||||
return []
|
||||
|
||||
def get_host_ip_addr(self):
|
||||
"""
|
||||
Retrieves the IP address of the dom0.
|
||||
"""
|
||||
# TODO(Vek): Need to pass context in for access to auth_token
|
||||
return
|
||||
|
||||
def snapshot_instance(self, context, instance_id, image_id):
|
||||
return
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ class OVZFile(object):
|
||||
raise TypeError(_("I don't know what to do with this type: %s") %
|
||||
type(new_contents))
|
||||
|
||||
def run_contents(self):
|
||||
def run_contents(self, raise_on_error=True):
|
||||
# Because only approved commands in rootwrap can be executed we
|
||||
# don't need to worry about unwanted command injection
|
||||
for line in self.contents:
|
||||
@@ -209,7 +209,9 @@ class OVZFile(object):
|
||||
_('Running line from %(filename)s: %(line)s') %
|
||||
{'filename': self.filename, 'line': line})
|
||||
line = line.split()
|
||||
ovz_utils.execute(*line, run_as_root=True)
|
||||
ovz_utils.execute(
|
||||
*line, run_as_root=True,
|
||||
raise_on_error=raise_on_error)
|
||||
except exception.InstanceUnacceptable as err:
|
||||
LOG.error(_('Cannot execute: %s') % line)
|
||||
LOG.error(err)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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.
|
||||
|
||||
@@ -63,11 +63,11 @@ class OVZExtStorage(object):
|
||||
:return: list
|
||||
"""
|
||||
try:
|
||||
self.volumes = json.loads(self.local_store.contents[0])
|
||||
self._volumes = json.loads(self.local_store.contents[0])
|
||||
except (ValueError, IndexError):
|
||||
self.volumes = dict()
|
||||
self._volumes = dict()
|
||||
|
||||
def add_volume(self, device, volume_info, overwrite=True):
|
||||
def add_volume(self, device, volume_info):
|
||||
"""
|
||||
Add a volume to local storage so volumes can be reconnected to in
|
||||
emergencies without nova services if need be.
|
||||
@@ -76,14 +76,7 @@ class OVZExtStorage(object):
|
||||
:param volume_info: Nova volume information from block_device_map
|
||||
:return: None
|
||||
"""
|
||||
if device in self.volumes and not overwrite:
|
||||
msg = (_('You told me not to overwrite volume info for device: '
|
||||
'%(device)s on instance: %(instnace_id)s') %
|
||||
{'device': device, 'instance_id': self.instance_id})
|
||||
LOG.error(msg)
|
||||
raise KeyError(msg)
|
||||
|
||||
self.volumes[device] = volume_info
|
||||
self._volumes[device] = volume_info
|
||||
|
||||
def remove_volume(self, device):
|
||||
"""
|
||||
@@ -93,7 +86,7 @@ class OVZExtStorage(object):
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
self.volumes.pop(device)
|
||||
self._volumes.pop(device)
|
||||
LOG.debug(
|
||||
_('Removed volume %(device)s from instance %(instance_id)s') %
|
||||
{'device': device, 'instance_id': self.instance_id})
|
||||
@@ -105,10 +98,20 @@ class OVZExtStorage(object):
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Flushes contents of self.volumes to disk for persistance
|
||||
Flushes contents of self._volumes to disk for persistance
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.local_store.set_contents(json.dumps(self.volumes))
|
||||
self.local_store.set_contents(json.dumps(self._volumes))
|
||||
with self.local_store:
|
||||
self.local_store.write()
|
||||
|
||||
def volumes(self):
|
||||
"""
|
||||
Simple generator to give back the volumes in an iterable and
|
||||
adventurous way.
|
||||
|
||||
:return: device name, connection info
|
||||
"""
|
||||
for key in self._volumes.keys():
|
||||
yield key, self._volumes[key]
|
||||
|
||||
369
nova/virt/openvz/migration.py
Normal file
369
nova/virt/openvz/migration.py
Normal file
@@ -0,0 +1,369 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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.
|
||||
|
||||
"""
|
||||
A driver specific to OpenVz as the support for Ovz in libvirt
|
||||
is sketchy at best.
|
||||
"""
|
||||
from nova import exception
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.virt.openvz import utils as ovz_utils
|
||||
import os
|
||||
from oslo.config import cfg
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger('nova.virt.openvz.volume')
|
||||
|
||||
|
||||
class OVZMigration(object):
|
||||
def __init__(self, instance, interfaces, block_device_info=None,
|
||||
dest=None, live=False):
|
||||
self.instance = instance
|
||||
self.interfaces = interfaces
|
||||
self.block_device_info = block_device_info
|
||||
self.destination_host = dest
|
||||
self.live_migration = live
|
||||
|
||||
# Hook in archives if migrations aren't done with the root user
|
||||
# because we need to preserve ownerships on the files inside
|
||||
# the container's root.
|
||||
if CONF.ovz_migration_user == 'root':
|
||||
self.unprivileged_user = False
|
||||
self.instance_tarfile = None
|
||||
else:
|
||||
self.unprivileged_user = True
|
||||
self.instance_tarfile = os.path.abspath('%s/%s.tar' %
|
||||
(CONF.ovz_tmp_dir,
|
||||
self.instance['id']))
|
||||
|
||||
self.instance_parent = CONF.ovz_ve_private_dir
|
||||
self.instance_source = os.path.abspath(
|
||||
'%s/%s' % (self.instance_parent, self.instance['id']))
|
||||
self.dumpdir_name = '%s-dumpdir' % instance['id']
|
||||
self.dumpdir_parent = CONF.ovz_tmp_dir
|
||||
self.dumpdir = os.path.abspath('%s/%s' % (self.dumpdir_parent,
|
||||
self.dumpdir_name))
|
||||
self.i_dumpdir = os.path.abspath('%s/instance' % self.dumpdir)
|
||||
self.dumpfile = os.path.abspath(
|
||||
'%s/dump.%s' % (self.i_dumpdir, self.instance['id']))
|
||||
self.q_dumpdir = os.path.abspath('%s/quotas' % self.dumpdir)
|
||||
self.qdumpfile = os.path.abspath(
|
||||
'%s/qdump.%s' % (self.q_dumpdir, self.instance['id']))
|
||||
self.as_dumpdir = os.path.abspath('%s/ascripts' % self.dumpdir)
|
||||
self.actionscripts = ['start', 'stop', 'mount', 'umount', 'premount',
|
||||
'postumount', 'boot', 'shutdown', 'conf',
|
||||
'ext_storage']
|
||||
self.dumpdir_tarfile = '%s.tar' % self.dumpdir
|
||||
|
||||
def dump_and_transfer_instance(self):
|
||||
"""
|
||||
Put all the pieces together to dump the instance and make the dump
|
||||
ready for transfer to the destination host.
|
||||
"""
|
||||
self.make_dump_dir()
|
||||
|
||||
# Begin the dumping process
|
||||
if self.live_migration:
|
||||
# this is a live migration so dump current memory and network
|
||||
# state.
|
||||
LOG.debug(_('Making container backup for %s') %
|
||||
self.instance['id'])
|
||||
self.dump()
|
||||
|
||||
if self.unprivileged_user:
|
||||
# we are transferring the instance by using an unprivileged user
|
||||
# so we need to tar the instance to preserve ownerships and
|
||||
# permissions
|
||||
LOG.debug(_('self.unprivileged_user is True'))
|
||||
self.tar_instance()
|
||||
|
||||
# Take all instance scripts from /etc/vz/conf and put them in a
|
||||
# tarball for transfer.
|
||||
LOG.debug(_('Making action script backups for %s') %
|
||||
self.instance['id'])
|
||||
self.backup_action_scripts()
|
||||
LOG.debug(_('Archiving misc files: %s') % self.instance['id'])
|
||||
self.tar_dumpdir()
|
||||
LOG.debug(_('Migration image created'))
|
||||
|
||||
def undump_instance(self):
|
||||
"""
|
||||
Take the pieces of a dump archive and put them back in place.
|
||||
"""
|
||||
# If a non-root user was used to transfer the files then we
|
||||
# need to move everything to where it is expected to be.
|
||||
LOG.debug(_('Restoring action scripts from archive: %s') %
|
||||
self.dumpdir_tarfile)
|
||||
self.untar_dumpdir()
|
||||
LOG.debug(_('Restoring action scripts for %s') % self.instance['id'])
|
||||
self.restore_action_scripts()
|
||||
|
||||
if self.unprivileged_user:
|
||||
LOG.debug(_('Restoring container from tar: %s') %
|
||||
self.instance['id'])
|
||||
self.untar_instance()
|
||||
|
||||
if self.live_migration:
|
||||
LOG.debug(_('Restoring container state from dump for %s') %
|
||||
self.instance['id'])
|
||||
self.undump()
|
||||
LOG.debug(_('Resuming container: %s') % self.instance['id'])
|
||||
self.resume()
|
||||
|
||||
LOG.debug(_('Done restoring instance %s') % self.instance['id'])
|
||||
|
||||
def make_dump_dir(self):
|
||||
"""
|
||||
Make our dump locations
|
||||
"""
|
||||
LOG.debug(_('Making dump location for %s') % self.instance['id'])
|
||||
ovz_utils.make_dir(self.dumpdir)
|
||||
ovz_utils.make_dir(self.i_dumpdir)
|
||||
ovz_utils.make_dir(self.q_dumpdir)
|
||||
ovz_utils.make_dir(self.as_dumpdir)
|
||||
LOG.debug(_('Done making location for %s') % self.instance['id'])
|
||||
|
||||
def cleanup_source(self):
|
||||
"""
|
||||
Helper method to wrap up all methods required to clean up a
|
||||
migration.
|
||||
"""
|
||||
if self.live_migration:
|
||||
self.kill()
|
||||
|
||||
self.cleanup_files()
|
||||
|
||||
def cleanup_destination(self):
|
||||
"""
|
||||
Do anything you need to do on the destination host to clean it up
|
||||
"""
|
||||
self.cleanup_files()
|
||||
|
||||
def dump(self):
|
||||
"""
|
||||
Create a vz dump file from a container. This is the file that we
|
||||
transfer to do a full migration
|
||||
"""
|
||||
LOG.debug(_('Dumping instance %s') % self.instance['id'])
|
||||
ovz_utils.execute('vzctl', 'chkpnt', self.instance['id'],
|
||||
'--dump', '--dumpfile', self.dumpfile,
|
||||
run_as_root=True)
|
||||
LOG.debug(_('Dumped instance %(instance_id)s to %(dumpfile)s') %
|
||||
{'instance_id': self.instance['id'],
|
||||
'dumpfile': self.dumpfile})
|
||||
|
||||
def undump(self):
|
||||
"""
|
||||
Restore a VZ from a dump file
|
||||
"""
|
||||
LOG.debug(_('Undumping instance %s') % self.instance['id'])
|
||||
ovz_utils.execute('vzctl', 'restore', self.instance['id'], '--undump',
|
||||
'--dumpfile', self.dumpfile, '--skip_arpdetect',
|
||||
run_as_root=True)
|
||||
LOG.debug(_('Undumped instance %(instance_id)s from %(dumpfile)s') %
|
||||
{'instance_id': self.instance['id'],
|
||||
'dumpfile': self.dumpfile})
|
||||
|
||||
def resume(self):
|
||||
"""
|
||||
Resume a container from an undumped migration
|
||||
"""
|
||||
LOG.debug(_('Resuming instance %s') % self.instance['id'])
|
||||
ovz_utils.execute('vzctl', 'restore', self.instance['id'],
|
||||
'--resume', run_as_root=True)
|
||||
LOG.debug(_('Resumed instance %s') % self.instance['id'])
|
||||
|
||||
def kill(self):
|
||||
"""
|
||||
This is used to stop a container once it's suspended without having to
|
||||
resume it to properly destroy it
|
||||
"""
|
||||
LOG.debug(_('Killing instance %s') % self.instance['id'])
|
||||
ovz_utils.execute('vzctl', 'chkpnt', self.instance['id'],
|
||||
'--kill', run_as_root=True)
|
||||
LOG.debug(_('Killed instance %s') % self.instance['id'])
|
||||
|
||||
def quotadump(self):
|
||||
"""
|
||||
Dump the quotas for containers
|
||||
"""
|
||||
LOG.debug(_('Dumping quotas for %s') % self.instance['id'])
|
||||
ovz_utils.execute('vzdqdump', self.instance['id'], '-U', '-G',
|
||||
'-T', '>', self.qdumpfile, run_as_root=True)
|
||||
LOG.debug(_('Dumped quotas for %s') % self.instance['id'])
|
||||
|
||||
def quotaload(self):
|
||||
"""
|
||||
Load quotas from quota file
|
||||
"""
|
||||
LOG.debug(_('Loading quotas for %s') % self.instance['id'])
|
||||
ovz_utils.execute('vzdqload', self.instance['id'], '-U', '-G', '-T',
|
||||
'<', self.qdumpfile, run_as_root=True)
|
||||
LOG.debug(_('Loaded quotas for %s') % self.instance['id'])
|
||||
|
||||
def quotaenable(self):
|
||||
"""
|
||||
enable quotas for a given container
|
||||
"""
|
||||
LOG.debug(_('Enabling quotas for %s') % self.instance['id'])
|
||||
ovz_utils.execute('vzquota', 'reload2',
|
||||
self.instance['id'], run_as_root=True)
|
||||
LOG.debug(_('Enabled quotas for %s') % self.instance['id'])
|
||||
|
||||
def quota_init(self):
|
||||
"""
|
||||
Initialize quotas for instance
|
||||
"""
|
||||
LOG.debug(_('Initializing quotas for %s') % self.instance['id'])
|
||||
ovz_utils.execute('vzctl', 'quotainit', self.instance['id'])
|
||||
LOG.debug(_('Initialized quotas for %s') % self.instance['id'])
|
||||
|
||||
def quota_on(self):
|
||||
"""
|
||||
Turn on quotas for instance
|
||||
"""
|
||||
LOG.debug(_('Turning on quotas for %s') % self.instance['id'])
|
||||
ovz_utils.execute('vzctl', 'quotaon', self.instance['id'],
|
||||
run_as_root=True)
|
||||
LOG.debug(_('Turned on quotas for %s') % self.instance['id'])
|
||||
|
||||
def backup_action_scripts(self):
|
||||
"""
|
||||
Take the action scripts with us in the backup/migration
|
||||
"""
|
||||
LOG.debug(_('Copying actionscripts into place'))
|
||||
for a_script in self.actionscripts:
|
||||
a_script = os.path.abspath(
|
||||
'%s/%s.%s' % (CONF.ovz_config_dir, self.instance['id'],
|
||||
a_script))
|
||||
if os.path.exists(a_script):
|
||||
LOG.debug(_('Copying actionscript: %s') % a_script)
|
||||
ovz_utils.copy(a_script, self.as_dumpdir)
|
||||
LOG.debug(_('Copied actionscript: %s') % a_script)
|
||||
LOG.debug(_('Copied actionscripts into place'))
|
||||
|
||||
def restore_action_scripts(self):
|
||||
"""
|
||||
Put the action scripts back into place
|
||||
"""
|
||||
LOG.debug(_('Restoring actionscripts into place'))
|
||||
for a_script in self.actionscripts:
|
||||
a_script = os.path.abspath('%s/%s.%s' % (self.as_dumpdir,
|
||||
self.instance['id'],
|
||||
a_script))
|
||||
if os.path.exists(a_script):
|
||||
LOG.debug(_('Restoring actionscript: %s') % a_script)
|
||||
ovz_utils.copy(a_script, CONF.ovz_config_dir)
|
||||
LOG.debug(_('Restored actionscript: %s') % a_script)
|
||||
LOG.debug(_('Restored actionscripts into place'))
|
||||
|
||||
def send(self):
|
||||
"""
|
||||
Use the configured transport to transfer the image from the src to
|
||||
the dest host. This will run on the source host.
|
||||
"""
|
||||
# Set the destination and source for the instance transfer
|
||||
if self.unprivileged_user:
|
||||
# Since we tarred up the instance we don't need to skip rsync'ing
|
||||
# any paths.
|
||||
src_path = self.instance_tarfile
|
||||
dest_path = CONF.ovz_tmp_dir
|
||||
else:
|
||||
src_path = self.instance_source
|
||||
dest_path = self.instance_parent
|
||||
|
||||
transport_instance = self._setup_transport(src_path, dest_path)
|
||||
transport_instance.send()
|
||||
|
||||
# Set the destination and source for the dumpdir transfer
|
||||
src_path = self.dumpdir_tarfile
|
||||
dest_path = self.dumpdir_parent
|
||||
|
||||
transport_instance_scripts = self._setup_transport(src_path, dest_path)
|
||||
transport_instance_scripts.send()
|
||||
|
||||
def receive(self):
|
||||
"""
|
||||
Use the configured transport to transfer the image form the src to
|
||||
dest host. This will run on the destination host
|
||||
"""
|
||||
# Right now we don't have a transport that needs this.
|
||||
raise NotImplementedError()
|
||||
|
||||
def cleanup_files(self):
|
||||
"""
|
||||
Remove the files in the OpenVz temp dir
|
||||
"""
|
||||
LOG.debug(_('Cleaning migration files for %s') % self.instance['id'])
|
||||
ovz_utils.execute('rm', '-rf', self.dumpdir, run_as_root=True)
|
||||
ovz_utils.execute('rm', '-f', self.dumpdir_tarfile, run_as_root=True)
|
||||
if self.instance_tarfile:
|
||||
ovz_utils.execute('rm', '-f', self.instance_tarfile,
|
||||
run_as_root=True)
|
||||
LOG.debug(
|
||||
_('Cleaned up migration files for %s') % self.instance['id'])
|
||||
|
||||
def tar_instance(self):
|
||||
"""
|
||||
Not an optimal way to do this but if you aren't using the root user
|
||||
to rsync the files from host to host you need to preserve the
|
||||
permissions and ownerships thus tar is your only hope.
|
||||
"""
|
||||
# Create our batch volume operations object
|
||||
LOG.debug(_('Tarring up instance: %s') % self.instance['id'])
|
||||
ovz_utils.tar(self.instance['id'], self.instance_tarfile,
|
||||
self.instance_parent)
|
||||
LOG.debug(_('Tarred up instance: %s') % self.instance['id'])
|
||||
|
||||
def tar_dumpdir(self):
|
||||
"""
|
||||
Archive the instance action scripts
|
||||
"""
|
||||
LOG.debug(_('Tarring up instance dumpdir: %s') % self.dumpdir)
|
||||
ovz_utils.tar(self.dumpdir_name, self.dumpdir_tarfile,
|
||||
self.dumpdir_parent)
|
||||
LOG.debug(_('Tarred up instance dumpdir: %s') % self.dumpdir)
|
||||
|
||||
def untar_instance(self):
|
||||
"""
|
||||
Expand the tarball from the instance and expand it into place
|
||||
"""
|
||||
LOG.debug(_('Untarring instance: %s') % self.instance_source)
|
||||
ovz_utils.untar(self.instance_tarfile, self.instance_parent)
|
||||
LOG.debug(_('Untarred instance: %s') % self.instance_source)
|
||||
|
||||
def untar_dumpdir(self):
|
||||
"""
|
||||
Expand the dumpdir into place on the destination machine
|
||||
"""
|
||||
LOG.debug(_('Untarring instance dumpdir: %s') % self.dumpdir)
|
||||
ovz_utils.untar(self.dumpdir_tarfile, self.dumpdir_parent)
|
||||
LOG.debug(_('Untarred instance dumpdir: %s') % self.dumpdir)
|
||||
|
||||
def _setup_transport(self, src_path, dest_path, skip_list=None):
|
||||
if CONF.ovz_migration_transport == 'rsync':
|
||||
from nova.virt.openvz.migration_drivers import rsync
|
||||
return rsync.OVZMigrationRsyncTransport(
|
||||
src_path, dest_path, self.instance['id'],
|
||||
self.destination_host, skip_list)
|
||||
else:
|
||||
LOG.error(
|
||||
_('I do not understand your migration transport: %s') %
|
||||
CONF.ovz_migration_transport)
|
||||
raise exception.MigrationError(
|
||||
_('No valid migration transport: %s') %
|
||||
CONF.ovz_migration_transport)
|
||||
16
nova/virt/openvz/migration_drivers/__init__.py
Normal file
16
nova/virt/openvz/migration_drivers/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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.
|
||||
62
nova/virt/openvz/migration_drivers/rsync.py
Normal file
62
nova/virt/openvz/migration_drivers/rsync.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Driver for OVZ Migrations. Uses rsync as a backend.
|
||||
"""
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.virt.openvz.migration_drivers import transport
|
||||
from nova.virt.openvz import utils as ovz_utils
|
||||
import os
|
||||
from oslo.config import cfg
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger('nova.virt.openvz.migration_drivers.rsync')
|
||||
|
||||
|
||||
class OVZMigrationRsyncTransport(transport.OVZMigrationTransport):
|
||||
def __init__(self, src_path, dest_path, instance_id,
|
||||
dest_host, skip_list=None):
|
||||
super(OVZMigrationRsyncTransport, self).__init__(src_path,
|
||||
dest_path,
|
||||
instance_id,
|
||||
dest_host,
|
||||
skip_list)
|
||||
|
||||
def send(self):
|
||||
"""
|
||||
Send image/files to a destination. This should be run on the source
|
||||
host.
|
||||
"""
|
||||
LOG.debug(_('Running _rsync()'))
|
||||
self._rsync(self.src_path, self.dest_path)
|
||||
LOG.debug(_('Ran _rsync()'))
|
||||
super(OVZMigrationRsyncTransport, self).send()
|
||||
|
||||
def _rsync(self, src_path, dest_path):
|
||||
"""
|
||||
Copy a path from one place to another using rsync
|
||||
"""
|
||||
dest = '%s@%s:%s' % (self.user, self.dest_host,
|
||||
os.path.abspath(dest_path))
|
||||
counter = 1
|
||||
while counter <= CONF.ovz_rsync_iterations:
|
||||
LOG.debug(_('RSyncing %(src_path)s, attempt: %(counter)s') %
|
||||
locals())
|
||||
ovz_utils.execute('rsync', '-qavz', src_path,
|
||||
dest, run_as_root=True)
|
||||
counter += 1
|
||||
72
nova/virt/openvz/migration_drivers/transport.py
Normal file
72
nova/virt/openvz/migration_drivers/transport.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Generic transport class for OVZ Migrations. Common methods will be placed
|
||||
in this file for reuse.
|
||||
"""
|
||||
from nova.openstack.common import log as logging
|
||||
import os
|
||||
from oslo.config import cfg
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger('nova.virt.openvz.migration_drivers.transport')
|
||||
|
||||
|
||||
class OVZMigrationTransport(object):
|
||||
def __init__(self, src_path, dest_path, instance_id,
|
||||
dest_host, skip_list=None):
|
||||
self.src_path = src_path
|
||||
self.dest_path = dest_path
|
||||
self.instance_id = instance_id
|
||||
self.dest_host = dest_host
|
||||
self.skip_list = skip_list
|
||||
self.user = CONF.ovz_migration_user
|
||||
self.container_path = os.path.abspath('%s/%s' %
|
||||
(CONF.ovz_ve_private_dir,
|
||||
self.instance_id))
|
||||
|
||||
def send(self):
|
||||
"""
|
||||
Code should go here to support what needs to be done upon sending a
|
||||
container to another host. This should be called from all transport
|
||||
drivers after their work is done on the source host.
|
||||
"""
|
||||
LOG.debug(_('Beginning send() for %s') % self.instance_id)
|
||||
# Generic use case transport code goes here, this code is run
|
||||
# after any code in a custom transport
|
||||
LOG.debug(_('Finished send() for %s') % self.instance_id)
|
||||
|
||||
def receive(self):
|
||||
"""
|
||||
Code should go here to support what needs to be done upon receiving a
|
||||
container from another host. This should be called from all transport
|
||||
drivers after their work is done on the destination host.
|
||||
"""
|
||||
LOG.debug(_('Beginning receive() for %s') % self.instance_id)
|
||||
# Generic use case transport code goes here, this code is run
|
||||
# after any code in a custom transport
|
||||
LOG.debug(_('Finished receive() for %s') % self.instance_id)
|
||||
|
||||
def verify(self):
|
||||
"""
|
||||
Check things on the destination host to make sure that the migration
|
||||
went well.
|
||||
"""
|
||||
LOG.debug(_('Beginning verify() for %s') % self.instance_id)
|
||||
# Generic use case transport code goes here
|
||||
LOG.debug(_('Finished verify() for %s') % self.instance_id)
|
||||
@@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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.
|
||||
|
||||
@@ -115,7 +115,7 @@ class OVZTcRules(object):
|
||||
LOG.debug(_('TC id %s inflight already, pulling another') % tc_id)
|
||||
tc_id = self._pull_id()
|
||||
LOG.debug(_('TC id %s pulled, testing for dupe') % tc_id)
|
||||
LOG.debug(_('TC id %s pulled, verified unique'))
|
||||
LOG.debug(_('TC id %s pulled, verified unique') % tc_id)
|
||||
self._reserve_id(tc_id)
|
||||
return tc_id
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ from nova.conductor import api
|
||||
from nova import context
|
||||
from nova import exception
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import processutils
|
||||
from nova import utils
|
||||
import os
|
||||
from oslo.config import cfg
|
||||
@@ -57,7 +58,7 @@ def execute(*cmd, **kwargs):
|
||||
LOG.debug(_('Stderr from %(command)s: %(out)s') %
|
||||
{'command': cmd[0], 'out': err})
|
||||
return out
|
||||
except exception.ProcessExecutionError as err:
|
||||
except processutils.ProcessExecutionError as err:
|
||||
msg = (_('Stderr from %(command)s: %(out)s') %
|
||||
{'command': cmd[0], 'out': err})
|
||||
if raise_on_error:
|
||||
|
||||
@@ -23,6 +23,7 @@ is sketchy at best.
|
||||
import glob
|
||||
from nova import exception
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import processutils
|
||||
from nova.virt.openvz import file as ovzfile
|
||||
from nova.virt.openvz import utils as ovz_utils
|
||||
import os
|
||||
@@ -68,11 +69,11 @@ class OVZVolume(object):
|
||||
"""
|
||||
self._attach_raw_devices()
|
||||
|
||||
def detach(self):
|
||||
def detach(self, container_is_running=True):
|
||||
"""
|
||||
Public method for detaching volumes from an instance.
|
||||
"""
|
||||
self._detach_raw_devices()
|
||||
self._detach_raw_devices(container_is_running)
|
||||
|
||||
def device_name(self):
|
||||
"""
|
||||
@@ -92,7 +93,7 @@ class OVZVolume(object):
|
||||
ovz_utils.execute('blockdev', '--getsize64', device_path,
|
||||
attempts=CONF.ovz_system_num_tries,
|
||||
run_as_root=True)
|
||||
except exception.ProcessExecutionError:
|
||||
except processutils.ProcessExecutionError:
|
||||
raise exception.InvalidDevicePath(path=device_path)
|
||||
|
||||
def _find_device(self):
|
||||
@@ -312,7 +313,7 @@ class OVZVolume(object):
|
||||
|
||||
self._set_block_devices(devices)
|
||||
|
||||
def _del_block_devices(self, new_devices):
|
||||
def _del_block_devices(self, new_devices, container_is_running=True):
|
||||
"""
|
||||
New devices should be a list that looks like this:
|
||||
|
||||
@@ -327,7 +328,7 @@ class OVZVolume(object):
|
||||
if dev in devices:
|
||||
devices.remove(dev)
|
||||
|
||||
self._set_block_devices(devices)
|
||||
self._set_block_devices(devices, container_is_running)
|
||||
|
||||
def _detach_remaining_block_devices(self):
|
||||
"""
|
||||
@@ -349,7 +350,7 @@ class OVZVolume(object):
|
||||
|
||||
ovz_utils.execute(*cmd, run_as_root=True)
|
||||
|
||||
def _set_block_devices(self, devices):
|
||||
def _set_block_devices(self, devices, container_is_running=True):
|
||||
"""
|
||||
Run the command necessary to save the block device permissions to
|
||||
the container config file so that they persist on reboot.
|
||||
@@ -368,7 +369,9 @@ class OVZVolume(object):
|
||||
# if the device list is empty this means that it's the last device
|
||||
# attached to the container and we need to run some special case
|
||||
# code to remove that device.
|
||||
self._detach_remaining_block_devices()
|
||||
if container_is_running:
|
||||
self._detach_remaining_block_devices()
|
||||
|
||||
self._remove_all_device_entries()
|
||||
|
||||
def _remove_all_device_entries(self):
|
||||
@@ -389,7 +392,7 @@ class OVZVolume(object):
|
||||
ct_conf.contents.remove(line)
|
||||
ct_conf.write()
|
||||
|
||||
def _detach_raw_devices(self):
|
||||
def _detach_raw_devices(self, container_is_running=True):
|
||||
"""
|
||||
Remove the associated devices from the container.
|
||||
|
||||
@@ -409,7 +412,11 @@ class OVZVolume(object):
|
||||
# TODO(imsplitbit): research to see if running this remove command
|
||||
# adhoc is necessary or if just removing the device from the config
|
||||
# file is adequate.
|
||||
self._detach_raw_device(maj_min['major'], maj_min['minor'])
|
||||
if container_is_running:
|
||||
# If the container isn't running this command makes things
|
||||
# unhappy. Very very unhappy.
|
||||
self._detach_raw_device(maj_min['major'], maj_min['minor'])
|
||||
|
||||
m = re.match('^(?P<device>/[a-z]+/[a-z]+)(?P<part>\\d*)$', dev)
|
||||
device_name = self.mountpoint
|
||||
|
||||
@@ -419,7 +426,7 @@ class OVZVolume(object):
|
||||
self._delete_device(device_name)
|
||||
|
||||
if bdevs_conf:
|
||||
self._del_block_devices(bdevs_conf)
|
||||
self._del_block_devices(bdevs_conf, container_is_running)
|
||||
|
||||
def _detach_raw_device(self, major, minor):
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Rackspace
|
||||
# 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.
|
||||
|
||||
@@ -22,6 +22,7 @@ is sketchy at best.
|
||||
from nova import exception
|
||||
from nova.openstack.common import lockutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import processutils
|
||||
from nova.virt.openvz import utils as ovz_utils
|
||||
from nova.virt.openvz import volume as ovzvolume
|
||||
from oslo.config import cfg
|
||||
@@ -122,16 +123,16 @@ class OVZISCSIStorageDriver(ovzvolume.OVZVolume):
|
||||
self._run_iscsiadm(("--login",))
|
||||
LOG.debug(_('iSCSI session for %s connected') %
|
||||
self.iscsi_properties['target_iqn'])
|
||||
except exception.ProcessExecutionError as err:
|
||||
except processutils.ProcessExecutionError as err:
|
||||
if "15 - already exists" in err.message:
|
||||
raise exception.VolumeUnattached()
|
||||
LOG.error(err)
|
||||
raise exception.VolumeNotFound(_("iSCSI device %s not found") %
|
||||
self.iscsi_properties['target_iqn'])
|
||||
|
||||
def detach(self, raise_on_error=True):
|
||||
def detach(self, container_is_running=True):
|
||||
"""Detach the volume from instance_name."""
|
||||
super(OVZISCSIStorageDriver, self).detach()
|
||||
super(OVZISCSIStorageDriver, self).detach(container_is_running)
|
||||
self._detach_iscsi()
|
||||
|
||||
@lockutils.synchronized('iscsiadm_lock', 'openvz-')
|
||||
@@ -157,7 +158,7 @@ class OVZISCSIStorageDriver(ovzvolume.OVZVolume):
|
||||
(self.iscsi_properties['target_portal'],
|
||||
self.iscsi_properties['target_iqn'],
|
||||
self.iscsi_properties['target_lun']))
|
||||
except exception.ProcessExecutionError as err:
|
||||
except processutils.ProcessExecutionError as err:
|
||||
LOG.error(err)
|
||||
raise exception.ISCSITargetNotFoundForVolume(
|
||||
_("Error rescanning iscsi device: %s") %
|
||||
|
||||
Reference in New Issue
Block a user