merged in migration code

Change-Id: Id526ba721ba3c531f75768f2d5552189d7e1e3c5
This commit is contained in:
Daniel Salinas
2013-05-28 14:20:27 -05:00
parent 024e9c0604
commit b123580ca3
24 changed files with 4689 additions and 83 deletions

4
debian/changelog vendored
View File

@@ -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
View File

@@ -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

View File

@@ -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

View 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
View 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')

File diff suppressed because it is too large Load Diff

View 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)

View 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()

View 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)

View 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)

View File

@@ -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

View File

@@ -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)

View 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.

View File

@@ -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]

View 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)

View 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.

View 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

View 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)

View 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.

View File

@@ -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

View File

@@ -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:

View File

@@ -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):
"""

View 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.

View File

@@ -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") %