Retire stackforge/openvz-nova-driver

This commit is contained in:
Monty Taylor 2015-10-17 16:04:08 -04:00
parent f1cf199214
commit bed4b3ff68
48 changed files with 7 additions and 9668 deletions

38
.gitignore vendored
View File

@ -1,38 +0,0 @@
*.py[cod]
# C extensions
*.so
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
nosetests.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Pycharm
.idea

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=stackforge/openvz-nova-driver.git

View File

@ -1,11 +0,0 @@
openvz-nova-driver
==================
Virt driver that allows openstack nova to control openvz containers.
Once installed simply set these flags in the nova.conf file:
<code>
compute_driver=openvz
</code>

7
README.rst Normal file
View File

@ -0,0 +1,7 @@
This project is no longer maintained.
The contents of this repository are still available in the Git source code
management system. To see the contents of this repository before it reached
its end of life, please check out the previous commit with
"git checkout HEAD^1".

15
debian/changelog vendored
View File

@ -1,15 +0,0 @@
openvz-nova-driver (1.3-01) precise; urgency=low
* Added setting numtcpsock to instance sizing
-- Daniel Salinas <imsplitbit@gmail.com> Wed, 26 June 2013 16:08:03 -0500
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

1
debian/compat vendored
View File

@ -1 +0,0 @@
8

15
debian/control vendored
View File

@ -1,15 +0,0 @@
Source: openvz-nova-driver
Maintainer: Daniel Salinas <imsplitbit@gmail.com>
Section: python
Priority: optional
Build-Depends: debhelper (>= 7.4.3), python-support (>= 0.8.4)
Standards-Version: 3.9.3
Package: openvz-nova-driver
Architecture: all
Depends: ${misc:Depends},
${python:Depends},
python-nova, nova-compute
XB-Python-Version: ${python:Versions}
Provides: ${python:Provides}
Description: Nova virt driver for OpenVz

0
debian/copyright vendored
View File

38
debian/rules vendored
View File

@ -1,38 +0,0 @@
#! /usr/bin/make -f
SHELL = /bin/bash
# all versions
PYVERS := $(shell pyversions -vs)
VER := $(shell /usr/bin/python -c 'import sys; print sys.version[:3]')
clean:
: # rm -rf *-stamp build-python* build
: # rm -rf $(addprefix debian/,$(packages)) debian/files debian/substvars
: # rm -rf _trial_temp test.log
: # find . -name "*.pyc" |xargs -r rm
dh_clean
binary-arch: binary-indep
binary-indep:
dh_installdirs /usr/lib/python$(PYVERS)/dist-packages/ovznovadriver /usr/lib/python$(PYVERS)/dist-packages/nova/tests /etc/nova/rootwrap.d
cp -R $(CURDIR)/ovznovadriver/openvz $(CURDIR)/debian/openvz-nova-driver/usr/lib/python$(PYVERS)/dist-packages/ovznovadriver
cp -R $(CURDIR)/ovznovadriver/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/ovznovadriver
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
dh_installchangelogs -i
dh_installdocs -i -A README.md
dh_installmenu -i
dh_compress -i -X.py
dh_fixperms -i
dh_installdeb -i
dh_gencontrol -i
dh_md5sums -i
dh_builddeb -i
binary: binary-indep
.PHONY: build clean binary-indep binary

View File

@ -1 +0,0 @@
1.0

View File

@ -1,85 +0,0 @@
# nova-rootwrap command filters for openvz nodes
# This file should be owned by (and only-writeable by) the root user
[Filters]
# ovznovadriver/openvz/utils.py: 'mount', '-o', 'defaults' ...
mount: CommandFilter, /bin/mount, root
# ovznovadriver/openvz/utils.py: 'umount'
umount: CommandFilter, /bin/umount, root
# ovznovadriver/openvz/utils.py: 'mkdir', path
mkdir: CommandFilter, /bin/mkdir, root
# ovznovadriver/openvz/utils.py: 'chown', owner_uid, path
chown: CommandFilter, /bin/chown, root
# ovznovadriver/openvz/utils.py: 'chmod'
chmod: CommandFilter, /bin/chmod, root
# ovznovadriver/openvz/volume_drivers/iscsi.py: 'iscsiadm', '-m', ...
iscsiadm: CommandFilter, iscsiadm, root
# ovznovadriver/openvz/volume.py: fdisk %(dev_path)s
fdisk: CommandFilter, /sbin/fdisk, root
# ovznovadriver/openvz/driver.py: 'arping', '-U', floating_ip, '-A', '-I', ...
arping: CommandFilter, arping, root
# ovznovadriver/openvz/file.py: 'touch', target
touch: CommandFilter, /usr/bin/touch, root
# Rackspace Openvz starts here
# nova/compute/manager.py: 'blockdev', '--getsize64', host_device
blockdev: CommandFilter, /sbin/blockdev, root
# ovznovadriver/openvz/driver.py: '/usr/sbin/vzlist'
vzlist: CommandFilter, /usr/sbin/vzlist, root
# ovznovadriver/openvz/driver.py: '/usr/sbin/vzctl'
vzctl: CommandFilter, /usr/sbin/vzctl, root
# ovznovadriver/openvz/driver.py: '/bin/rm'
rm: CommandFilter, /bin/rm, root
# ovznovadriver/openvz/driver.py:
cpuinfo: ReadFileFilter, /proc/cpuinfo
# ovznovadriver/openvz/driver.py:
meminfo: ReadFileFilter, /proc/meminfo
# ovznovadriver/openvz/driver.py: '/usr/sbin/vzcpucheck'
vzcpucheck: CommandFilter, /usr/sbin/vzcpucheck, root
# ovznovadriver/openvz/driver.py: '/bin/rmdir'
rmdir: CommandFilter, /bin/rmdir, root
# ovznovadriver/openvz/volume_drivers/iscsi.py: '/usr/bin/iscsiadm'
iscsiadm_usrsbin: CommandFilter, /usr/bin/iscsiadm, root
# ovznovadriver/openvz/utils.py: '/sbin/blkid'
blkid: CommandFilter, /sbin/blkid, root
# ovznovadriver/openvz/network_plugins/tc.py
tc: CommandFilter, /sbin/tc, root
# ovznovadriver/openvz/volume.py
ls: CommandFilter, /bin/ls, root
# ovznovadriver/openvz/volume.py
sfdisk: CommandFilter, /sbin/sfdisk, root
# ovznovadriver/openvz/volume.py
mknod: CommandFilter, /bin/mknod, root
# ovznovadriver/openvz/driver.py
vzmigrate: CommandFilter, /usr/sbin/vzmigrate, root
# ovznovadriver/openvz/migrate.py
cp: CommandFilter, /bin/cp, root
# ovznovadriver/openvz/utils.py
tar: CommandFilter, /bin/tar, root
# ovznovadriver/openvz/migration_drivers/rsync.py
rsync: CommandFilter, /usr/bin/rsync, root

View File

@ -1,12 +0,0 @@
"""
OpenVZ Nova Driver
------------------
This is included in this oddly named directory instead of "nova/virt" since in
the later case it wouldn't be possible to have a setup.py and thus develop on
it at the same time without shadowing the real nova package.
If you don't like that, complain to the Nova core team so they accept this
driver into the actual Nova codebase, or go write a ton of code for libvirt.
"""

View File

@ -1,3 +0,0 @@
#TODO(tim.simpson): Figure out some way to link this to Nova's localization
# function.
_ = str

View File

@ -1,20 +0,0 @@
# 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 ovznovadriver.openvz import driver
OpenVzDriver = driver.OpenVzDriver

View File

@ -1,606 +0,0 @@
# Copyright 2014 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 itertools
import json
import os
from nova import exception
from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from oslo.config import cfg
from ovznovadriver.localization import _
from ovznovadriver.openvz import utils as ovz_utils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
# More of the openvz logic belongs in here, but it was hard to de-tangle it
# from all the libraries, so a minimal subset to get the desired outcome
# was ported with the hope that future commits will further improve upon it
def _is_int(value):
try:
int(value)
return True
except ValueError:
return False
class OvzContainer(object):
"""
A class for objects that represent OpenVZ containers.
All methods for interacting with OpenVZ containers spawn from here.
"""
def __init__(self, ovz_id, nova_id=None, name=None, uuid=None,
host=None, state=None):
self.ovz_data = {
'ovz_id': ovz_id,
'nova_id': nova_id,
'name': name,
'uuid': uuid,
'host': host,
}
if state is not None:
self.ovz_data['state'] = state
@property
def ovz_id(self):
return self.ovz_data['ovz_id']
@property
def nova_id(self):
return self.ovz_data['nova_id']
@property
def name(self):
return self.ovz_data['name']
@property
def uuid(self):
return self.ovz_data['uuid']
@property
def host(self):
return self.ovz_data['host']
@property
def state(self):
if 'state' in self.ovz_data:
return self.ovz_data['state']
self._load_state()
return self.ovz_data['state']
@classmethod
def create(cls, image, **kwargs):
"""
Create an OpenVZ container with the specified arguments.
"""
# TODO(imsplitbit): This needs to set an os template for the image
# as well as an actual OS template for OpenVZ to know what config
# scripts to use. This can be problematic because there is no concept
# of OS name, it is arbitrary so we will need to find a way to
# correlate this to what type of disto the image actually is because
# this is the clue for openvz's utility scripts. For now we will have
# to set it to 'ubuntu'
container = OvzContainer(ovz_id=cls.get_next_id(), **kwargs)
create_cmd = ['vzctl', 'create', container.ovz_id, '--ostemplate',
image]
if CONF.ovz_layout:
create_cmd.append('--layout')
create_cmd.append(CONF.ovz_layout)
ovz_utils.execute(*create_cmd, run_as_root=True)
container.save_ovz_metadata()
return container
@classmethod
@lockutils.synchronized('OVZ_DRIVER_GET_NEXT_ID')
def get_next_id(cls):
"""
Gets the next available local openvz id.
"""
# openvz reserves ids 0-100, so start at 101
# We wish to know all vz directories that currently exist
# since a container that has been deleted could have not
# been completely cleaned up
ovz_id = str(max(OvzContainers.ctid_in_use()) + 1)
# Create a lock dir to workaround a concurrency issue where multiple
# create calls can get the same next_id.
lock_dir = os.path.join(CONF.ovz_lock_dir, ovz_id)
ovz_utils.execute('mkdir', '-p', lock_dir, run_as_root=True)
return ovz_id
def save_ovz_metadata(self):
"""
Save information important to associate nova information with this
instance.
"""
description = json.dumps({
'host': CONF.host,
'name': self.name,
'nova_id': str(self.nova_id),
'uuid': self.uuid,
})
# setting the name to the uuid lets you use the uuid with vzctl
ovz_utils.execute(
'vzctl', 'set', self.ovz_id, '--save', '--name', self.uuid,
'--description', description, run_as_root=True)
def _load_state(self):
"""
Load the current state data from OpenVZ.
"""
out = ovz_utils.execute('vzlist', '--no-header', '--all',
'--output', 'status', self.ovz_id,
raise_on_error=False, run_as_root=True)
if not out:
raise exception.InstanceNotFound(
_('Instance %s doesnt exist') % self.ovz_id)
out = out.split()
self.ovz_data.state = out[0]
@classmethod
def find(cls, ovz_id=None, name=None, nova_id=None, uuid=None):
"""
Find a specific OpenVZ container by name, ovz_id (ctid),
nova_id (instance id), or uuid.
"""
if (ovz_id):
return cls._find(ovz_id)
if (name):
name_wildcard = '*"name": "' + name + '"*'
return cls._find('--description', name_wildcard)
if (uuid):
uuid_wildcard = '*"uuid": "' + uuid + '"*'
return cls._find('--description', uuid_wildcard)
if (nova_id):
nova_id_wildcard = '*"nova_id": "' + str(nova_id) + '"*'
return cls._find('--description', nova_id_wildcard)
@classmethod
def _find(cls, *params):
vzlist_cmd = ['vzlist', '--no-header', '--all',
'--output', 'ctid,status,description']
vzlist_cmd.extend(params)
out = ovz_utils.execute(*vzlist_cmd, raise_on_error=False,
run_as_root=True)
# for testing, we allow multiple nova hosts per node. We don't want
# to load containers for the other hosts, so pretend they don't exist
found = None
if out is not None:
lines = out.splitlines()
for line in lines:
possible = cls._load_from_vzlist(line)
if CONF.host == possible.host:
found = possible
break
if found is None:
raise exception.InstanceNotFound(
_('Instance %s does not exist') % str(params))
LOG.debug(_('Loading container from vzlist %(params)s: %(ovz_data)s') %
{'params': params, 'ovz_data': found.ovz_data})
return found
@classmethod
def _load_from_vzlist(cls, line):
# since the JSON bits have spaces in them, limit how many splits we do
raw = line.split(None, 2)
ovz_data = {}
if raw[2] is not None and raw[2] != '-':
try:
ovz_data = json.loads(raw[2])
except BaseException, e:
LOG.error("ERROR parsing json %s: %s" % (raw[2], e))
ovz_data['ovz_id'] = raw[0]
ovz_data['state'] = raw[1]
return OvzContainer(**ovz_data)
def prep_for_migration(self):
# this only really happens in development VMs, but we can't have two
# containers with the same name, so we need to rename the old one
temp_name = '%s-migrated' % self.uuid
ovz_utils.execute(
'vzctl', 'set', self.ovz_id, '--save', '--name', temp_name,
run_as_root=True)
def delete(self):
"""
Remove the OpenVZ container this object represents.
"""
ovz_utils.execute('vzctl', 'destroy', self.ovz_id, run_as_root=True)
# Clean up the folders if they get left behind.
root_dir = os.path.join(CONF.ovz_ve_root_dir, self.ovz_id)
private_dir = os.path.join(CONF.ovz_ve_private_dir, self.ovz_id)
lock_dir = os.path.join(CONF.ovz_lock_dir, self.ovz_id)
confs = (
os.path.join(CONF.ovz_ve_conf_dir, f)
for f in os.listdir(CONF.ovz_ve_conf_dir)
if f.startswith(self.ovz_id + '.')
)
for pth in itertools.chain((root_dir, private_dir, lock_dir), confs):
ovz_utils.execute('rm', '-rf', pth, run_as_root=True)
def apply_config(self, config):
"""
This adds the container root into the vz meta data so that
OpenVz acknowledges it as a container. Punting to a basic
config for now.
Run the command:
vzctl set <ctid> --save --applyconfig <config>
This sets the default configuration file for openvz containers. This
is a requisite step in making a container from an image tarball.
If this fails to run successfully an exception is raised because the
container this executes against requires a base config to start.
"""
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--applyconfig', config, run_as_root=True)
def set_vz_os_hint(self, ostemplate='ubuntu'):
"""
This exists as a stopgap because currently there are no os hints
in the image managment of nova. There are ways of hacking it in
via image_properties but this requires special case code just for
this driver.
Run the command:
vzctl set <ctid> --save --ostemplate <ostemplate>
Currently ostemplate defaults to ubuntu. This facilitates setting
the ostemplate setting in OpenVZ to allow the OpenVz helper scripts
to setup networking, nameserver and hostnames. Because of this, the
openvz driver only works with debian based distros.
If this fails to run an exception is raised as this is a critical piece
in making openvz run a container.
"""
# This sets the distro hint for OpenVZ to later use for the setting
# of resolver, hostname and the like
# TODO(imsplitbit): change the ostemplate default value to a flag
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--ostemplate', ostemplate, run_as_root=True)
def set_numflock(self, max_file_descriptors):
"""
Run the command:
vzctl set <ctid> --save --numflock <number>
"""
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--numflock', max_file_descriptors,
run_as_root=True)
def set_numfiles(self, max_file_descriptors):
"""
Run the command:
vzctl set <ctid> --save --numfile <number>
"""
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--numfile', max_file_descriptors,
run_as_root=True)
# TODO(jcru) looks like this method is not being used anywhere, delete?
# TODO(jcru) extract calculations from here and only pass tcp_sockets to
# function.
def set_numtcpsock(self, memory_mb):
"""
Run the commnand:
vzctl set <ctid> --save --numtcpsock <number>
:param instance:
:return:
"""
try:
tcp_sockets = CONF.ovz_numtcpsock_map[str(memory_mb)]
except (ValueError, TypeError, KeyError, cfg.NoSuchOptError):
LOG.error(_('There was no acceptable tcpsocket number found '
'defaulting to %s') % CONF.ovz_numtcpsock_default)
tcp_sockets = CONF.ovz_numtcpsock_default
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--numtcpsock', tcp_sockets, run_as_root=True)
def set_vmguarpages(self, num_pages):
"""
Set the vmguarpages attribute for a container. This number represents
the number of 4k blocks of memory that are guaranteed to the container.
This is what shows up when you run the command 'free' in the container.
Run the command:
vzctl set <ctid> --save --vmguarpages <num_pages>
If this fails to run then an exception is raised because this affects
the memory allocation for the container.
"""
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--vmguarpages', num_pages, run_as_root=True)
def set_privvmpages(self, num_pages):
"""
Set the privvmpages attribute for a container. This represents the
memory allocation limit. Think of this as a bursting limit. For now
We are setting to the same as vmguarpages but in the future this can be
used to thin provision a box.
Run the command:
vzctl set <ctid> --save --privvmpages <num_pages>
If this fails to run an exception is raised as this is essential for
the running container to operate properly within it's memory
constraints.
"""
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--privvmpages', num_pages, run_as_root=True)
def set_kmemsize(self, kmem_barrier, kmem_limit):
"""
Set the kmemsize attribute for a container. This represents the
amount of the container's memory allocation that will be made
available to the kernel. This is used for tcp connections, unix
sockets and the like.
This runs the command:
vzctl set <ctid> --save --kmemsize <barrier>:<limit>
If this fails to run an exception is raised as this is essential for
the container to operate under a normal load. Defaults for this
setting are completely inadequate for any normal workload.
"""
kmemsize = '%d:%d' % (kmem_barrier, kmem_limit)
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--kmemsize', kmemsize, run_as_root=True)
def set_cpuunits(self, units):
"""
Set the cpuunits setting for the container. This is an integer
representing the number of cpu fair scheduling counters that the
container has access to during one complete cycle.
Run the command:
vzctl set <ctid> --save --cpuunits <units>
If this fails to run an exception is raised because this is the secret
sauce to constraining each container within it's subscribed slice of
the host node.
"""
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--cpuunits', units, run_as_root=True)
def set_cpulimit(self, cpulimit):
"""
This is a number in % equal to the amount of cpu processing power
the container gets. NOTE: 100% is 1 logical cpu so if you have 12
cores with hyperthreading enabled then 100% of the whole host machine
would be 2400% or --cpulimit 2400.
Run the command:
vzctl set <ctid> --save --cpulimit <cpulimit>
If this fails to run an exception is raised because this is the secret
sauce to constraining each container within it's subscribed slice of
the host node.
"""
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--cpulimit', cpulimit, run_as_root=True)
def set_cpus(self, vcpus):
"""
The number of logical cpus that are made available to the container.
Default to showing 2 cpus to each container at a minimum.
Run the command:
vzctl set <ctid> --save --cpus <num_cpus>
If this fails to run an exception is raised because this limits the
number of cores that are presented to each container and if this fails
to set *ALL* cores will be presented to every container, that be bad.
"""
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save', '--cpus',
vcpus, run_as_root=True)
def set_ioprio(self, ioprio):
"""
Set the IO priority setting for a given container. This is represented
by an integer between 0 and 7.
Run the command:
vzctl set <ctid> --save --ioprio <iopriority>
If this fails to run an exception is raised because all containers are
given the same weight by default which will cause bad performance
across all containers when there is input/output contention.
"""
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--ioprio', ioprio, run_as_root=True)
def set_diskspace(self, soft_limit, hard_limit):
"""
Implement OpenVz disk quotas for local disk space usage.
This method takes a soft and hard limit. This is also the amount
of diskspace that is reported by system tools such as du and df inside
the container. If no argument is given then one will be calculated
based on the values in the instance_types table within the database.
Run the command:
vzctl set <ctid> --save --diskspace <soft_limit:hard_limit>
If this fails to run an exception is raised because this command
limits a container's ability to hijack all available disk space.
"""
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--diskspace', '%s:%s' % (soft_limit, hard_limit),
run_as_root=True)
def set_vswap(self, ram, swap):
"""
Implement OpenVz vswap memory management model (The other being user
beancounters).
The sum of physpages_limit and swappages_limit limits the maximum
amount of memory which can be used by a container. When physpages limit
is reached, memory pages belonging to the container are pushed out to
so called virtual swap (vswap). The difference between normal swap and
vswap is that with vswap no actual disk I/O usually occurs. Instead, a
container is artificially slowed down, to emulate the effect of the
real swapping. Actual swap out occurs only if there is a global memory
shortage on the system.
Run the command:
vzctl set <ctid> --ram <physpages_limit> --swap <swappages_limit>
"""
ovz_utils.execute('vzctl', 'set', self.ovz_id, '--save',
'--ram', ram, '--swap', swap, run_as_root=True)
class OvzContainers(object):
@classmethod
def _list(cls, fields, host=CONF.host):
# it's important that description is the last field so it doesn't get
# truncated. Alternatively, the --no-trim option added in openvz 3.2
# will make the point moot, if we want to force that dependency
vzlist_cmd = ['vzlist', '--all', '--no-header',
'--output', fields]
if host is not None:
host_wildcard = '*"host": "' + host + '"*'
vzlist_cmd.extend(['--description', host_wildcard])
out = ovz_utils.execute(*vzlist_cmd,
raise_on_error=False, run_as_root=True)
if out:
return out.splitlines()
return list()
@classmethod
def list(cls, host=CONF.host):
results = list()
lines = cls._list('ctid,status,description', host=host)
for line in lines:
results.append(OvzContainer._load_from_vzlist(line))
return results
@classmethod
def ctid_in_use(cls):
"""Get an iterable of all CTIDs currently in use on the system."""
return itertools.chain(
xrange(100), # Reserved by OpenVZ.
(int(cont.ovz_id, base=10)
for cont in OvzContainers.list(host=None)),
OvzContainers.list_vz_artifacts(),
)
@classmethod
def _artifacts_from_dirs(cls):
"""Generate container ids that have artifacts in vz directories.
Directories in the searched directories are named with the CTID of the
container they represent. Generate a list of all CTIDs in use by
looking in these directories. This accounts for containers that are
deleted but still leave behind certain directories.
"""
directories_to_check = (
CONF.ovz_ve_private_dir,
CONF.ovz_ve_root_dir,
CONF.ovz_lock_dir,
)
for dir_path in directories_to_check:
for item in os.listdir(dir_path):
item_path = os.path.join(dir_path, item)
if os.path.isdir(item_path) and _is_int(item):
yield int(item)
@classmethod
def _artifacts_from_confs(cls):
"""Generate container ids that have artifacts in the vz configs.
Each container also comes with a series of config files. Each file is
named in the pattern <CTID>.<CONFIG>. Generate a list of CTIDs based on
the config files that are present. This accounts for containers that
were deleted but left behind their config files.
"""
for item in os.listdir(CONF.ovz_ve_conf_dir):
if '.' in item and _is_int(item.split('.')[0]):
yield int(item.split('.')[0])
@classmethod
def list_vz_artifacts(cls):
"""Lists container ids that have artifacts within vz directories
"""
return itertools.chain(
OvzContainers._artifacts_from_dirs(),
OvzContainers._artifacts_from_confs(),
)
@classmethod
def get_memory_mb_used(cls, block_size=4096):
total_used_mb = 0
lines = cls._list('ctid,privvmpages.l')
for line in lines:
line = line.split()
total_used_mb += ((int(line[1]) * block_size) / 1024 ** 2)
return total_used_mb

File diff suppressed because it is too large Load Diff

View File

@ -1,224 +0,0 @@
# 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 ovznovadriver.localization import _
from nova.openstack.common import log as logging
from ovznovadriver.openvz import utils as ovz_utils
import os
from oslo.config import cfg
CONF = cfg.CONF
LOG = logging.getLogger('ovznovadriver.openvz.file')
class OVZFile(object):
"""
This is a generic file class for wrapping up standard file operations that
may need to run on files associated with OpenVz
"""
def __init__(self, filename, permissions):
self.filename = os.path.abspath(filename)
self.contents = []
self.permissions = permissions
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 read(self):
"""
Open the file for reading only and read it's contents into the instance
attribute self.contents
"""
try:
with open(self.filename, 'r') as fh:
self.contents = fh.read().split('\n')
except Exception as err:
LOG.error(_('Output from open: %s') % err)
raise exception.FileNotFound(
_('Failed to read %s') % self.filename)
def write(self):
"""
Because self.contents may or may not get manipulated throughout the
process, this is a method used to dump the contents of self.contents
back into the file that this object represents.
"""
LOG.debug(_('File contents before write: %s') %
'\n'.join(self.contents))
try:
with open(self.filename, 'w') as fh:
fh.writelines('\n'.join(self.contents) + '\n')
except Exception as err:
LOG.error(_('Output from open: %s') % err)
raise exception.FileNotFound(
_('Failed to write %s') % self.filename)
def touch(self):
"""
There are certain conditions where we create an OVZFile object but that
file may or may not exist and this provides us with a way to create
that file if it doesn't exist.
Run the command:
touch <filename>
If this does not run an exception is raised as a failure to touch a
file when you intend to do so would cause a serious failure of
procedure.
"""
self.make_path()
ovz_utils.execute('touch', self.filename, run_as_root=True)
def make_proper_script(self):
"""
OpenVz mount and umount files must be properly formatted scripts.
Prepend the proper shell script header to the files
"""
if not self.contents[:1] == ['#!/bin/sh']:
self.prepend('#!/bin/sh')
def append(self, contents):
"""
Add the argument contents to the end of self.contents
"""
if not isinstance(contents, list):
contents = [str(contents)]
self.contents += contents
LOG.debug(_('File contents: %s') % self.contents)
def prepend(self, contents):
"""
Add the argument contents to the beginning of self.contents
"""
if not isinstance(contents, list):
contents = [str(contents)]
self.contents = contents + self.contents
LOG.debug(_('File contents: %s') % self.contents)
def delete(self, contents):
"""
Delete the argument contents from self.contents if they exist.
"""
if isinstance(contents, list):
LOG.debug(_('A list was passed to delete: %s') % contents)
for line in contents:
LOG.debug(_('Line: %(line)s \tFound in: %(contents)s') %
locals())
self.remove_line(line)
else:
LOG.debug(_('A string was passed to delete: %s') % contents)
self.remove_line(contents)
def remove_line(self, line):
"""
Simple helper method to actually do the removal of a line from an array
"""
LOG.debug(_('Removing line if present: %s') % line)
if line in self.contents:
LOG.debug(_('Line found: %s') % line)
self.contents.remove(line)
LOG.debug(_('Line removed: %(line)s \t%(contents)s') %
{'line': line, 'contents': self.contents})
def set_permissions(self, permissions):
"""
Because nova runs as an unprivileged user we need a way to mangle
permissions on files that may be owned by root for manipulation
Run the command:
chmod <permissions> <filename>
If this doesn't run an exception is raised because the permissions not
being set to what the application believes they are set to can cause
a failure of epic proportions.
"""
ovz_utils.execute('chmod', permissions, self.filename,
run_as_root=True)
def make_path(self, path=None):
"""
Helper method for an OVZFile object to be able to create the path for
self.filename if it doesn't exist before running touch()
"""
if not path:
path = self.filename
basedir = os.path.dirname(path)
ovz_utils.make_dir(basedir)
def set_contents(self, new_contents):
"""
We need the ability to overwrite all contents and this is the cleanest
way while allowing some validation.
"""
if isinstance(new_contents, str):
LOG.debug(_('Type of new_contents is: str'))
self.contents = new_contents.split('\n')
LOG.debug(_('Contents of file: %s') % self.contents)
elif isinstance(new_contents, list):
LOG.debug(_('Type of new_contents is: list'))
self.contents = new_contents
else:
raise TypeError(_("I don't know what to do with this type: %s") %
type(new_contents))
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:
if len(line) > 0:
if line[0] != '#' and line != '\n':
line = line.strip()
try:
LOG.debug(
_('Running line from %(filename)s: %(line)s') %
{'filename': self.filename, 'line': line})
line = line.split()
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)
def exists(self):
"""
Simple wrapper for os.path.exists
"""
return os.path.exists(self.filename)

View File

@ -1,16 +0,0 @@
# 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

@ -1,43 +0,0 @@
# 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.
"""
OpenVZ doesn't allow for a script to be run after a container has started up
using the host node's context so we are implementing one here
"""
from nova.openstack.common import log as logging
from ovznovadriver.openvz import file as ovzfile
import os
from oslo.config import cfg
CONF = cfg.CONF
LOG = logging.getLogger('ovznovadriver.openvz.file_ext.boot')
class OVZBootFile(ovzfile.OVZFile):
def __init__(self, instance_id, permissions):
"""
Child class of OVZFile used to manage the CTID.boot file. This file
will be executed line by line after the container has started but
using the host's context.
:param instance_id: Instance used for the file
"""
filename = "%s/%s.boot" % (CONF.ovz_config_dir, instance_id)
filename = os.path.abspath(filename)
super(OVZBootFile, self).__init__(filename, permissions)

View File

@ -1,118 +0,0 @@
# 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.
"""
import json
from nova.openstack.common import log as logging
from ovznovadriver.localization import _
from ovznovadriver.openvz import file as ovzfile
import os
from oslo.config import cfg
CONF = cfg.CONF
LOG = logging.getLogger('ovznovadriver.openvz.file_ext.ext_storage')
class OVZExtStorage(object):
"""
Local store for volume information needing to be persisted to the local
host.
"""
def __init__(self, instance_id):
"""
:param instance_id: CTID of the container
:return: None
"""
filename = '%s/%s.ext_storage' % (CONF.ovz_config_dir, instance_id)
filename = os.path.abspath(filename)
self.instance_id = instance_id
self.local_store = ovzfile.OVZFile(filename, 600)
# read the local store for the CTID or create it if it doesn't already
# exist.
with self.local_store:
self.local_store.read()
# preload volume info
self.load_volumes()
def load_volumes(self):
"""
return the contents of self.contents *after* converting it from json
to python objects.
:return: list
"""
try:
self._volumes = json.loads(self.local_store.contents[0])
except (ValueError, IndexError):
self._volumes = dict()
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.
:param device: Device name
:param volume_info: Nova volume information from block_device_map
:return: None
"""
self._volumes[device] = volume_info
def remove_volume(self, device):
"""
removes a volume from the volume store.
:param device:
:return: None
"""
try:
self._volumes.pop(device)
LOG.debug(
_('Removed volume %(device)s from instance %(instance_id)s') %
{'device': device, 'instance_id': self.instance_id})
except KeyError:
LOG.error(
_('Volume %(device)s was not in local store for '
'instance %(instance_id)s') %
{'device': device, 'instance_id': self.instance_id})
def save(self):
"""
Flushes contents of self._volumes to disk for persistance
:return: None
"""
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

@ -1,45 +0,0 @@
# 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.
"""
OpenVz doesn't have provision for a script to be run before a container
is stopped but with the host node's context so we are implementing one here.
"""
from nova.openstack.common import log as logging
from ovznovadriver.localization import _
from ovznovadriver.openvz import file as ovzfile
import os
from oslo.config import cfg
CONF = cfg.CONF
LOG = logging.getLogger('ovznovadriver.openvz.file_ext.shutdown')
class OVZShutdownFile(ovzfile.OVZFile):
def __init__(self, instance_id, permissions):
"""
Child class of OVZFile used to manage the CTID.boot file. This file
will be executed line by line after the container has started but
using the host's context.
:param instance_id: Instance used for the file
"""
LOG.debug(_('Beginning OVZShutdownFile'))
filename = "%s/%s.shutdown" % (CONF.ovz_config_dir, instance_id)
filename = os.path.abspath(filename)
LOG.debug(_('OVZShutdownFile: %s') % filename)
super(OVZShutdownFile, self).__init__(filename, permissions)

View File

@ -1,33 +0,0 @@
# 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 ovznovadriver.openvz import file as ovzfile
import os
from oslo.config import cfg
CONF = cfg.CONF
class OVZContainerStartScript(ovzfile.OVZFile):
def __init__(self, instance_id):
filename = "%s/%s.start" % (CONF.ovz_config_dir, instance_id)
filename = os.path.abspath(filename)
super(OVZContainerStartScript, self).__init__(filename)

View File

@ -1,33 +0,0 @@
# 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 ovznovadriver.openvz import file as ovzfile
import os
from oslo.config import cfg
CONF = cfg.CONF
class OVZContainerStopScript(ovzfile.OVZFile):
def __init__(self, instance_id):
filename = "%s/%s.stop" % (CONF.ovz_config_dir, instance_id)
filename = os.path.abspath(filename)
super(OVZContainerStopScript, self).__init__(filename)

View File

@ -1,386 +0,0 @@
# 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 ovznovadriver.localization import _
from ovznovadriver.openvz.container import OvzContainer
from ovznovadriver.openvz import utils as ovz_utils
import os
from oslo.config import cfg
CONF = cfg.CONF
LOG = logging.getLogger('ovznovadriver.openvz.volume')
class OVZMigration(object):
def __init__(self, container, interfaces, block_device_info=None,
dest=None, live=False):
self.container = container
# TODO(pdmars): does interfaces need to be in the legacy
# network_info format? because it's not anymore
self.interfaces = interfaces
self.block_device_info = block_device_info
self.destination_host = dest
self.live_migration = live
self.instance_tarfile = os.path.abspath('%s/%s.tar' %
(CONF.ovz_tmp_dir,
self.container.uuid))
self.instance_parent = CONF.ovz_ve_private_dir
self.instance_source = os.path.abspath(
'%s/%s' % (self.instance_parent, self.container.ovz_id))
self.dumpdir_name = '%s-dumpdir' % self.container.uuid
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.container.uuid))
self.q_dumpdir = os.path.abspath('%s/quotas' % self.dumpdir)
self.qdumpfile = os.path.abspath(
'%s/qdump.%s' % (self.q_dumpdir, self.container.uuid))
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.container.prep_for_migration()
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.container.ovz_id)
self.dump()
else:
LOG.debug(_('Archiving container to tar: %s') %
self.container.ovz_id)
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.container.ovz_id)
self.backup_action_scripts()
LOG.debug(_('Archiving misc files: %s') % self.container.ovz_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.container.ovz_id)
self.restore_action_scripts()
if self.live_migration:
LOG.debug(_('Restoring container state from dump for %s') %
self.container.ovz_id)
self.undump()
LOG.debug(_('Resuming container: %s') % self.container.ovz_id)
self.resume()
else:
LOG.debug(_('Restoring container from tar: %s') %
self.container.ovz_id)
self.untar_instance()
root_dir = os.path.join(CONF.ovz_ve_root_dir, self.container.ovz_id)
if os.path.exists(root_dir) is False:
ovz_utils.execute('mkdir', '-p', root_dir, run_as_root=True)
# ensure we can interact with the new instance via its uuid
self.container.save_ovz_metadata()
LOG.debug(_('Done restoring instance %s') % self.container.ovz_id)
def make_dump_dir(self):
"""
Make our dump locations
"""
LOG.debug(_('Making dump location for %s') % self.container.ovz_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.container.ovz_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.container.ovz_id)
ovz_utils.execute('vzctl', 'chkpnt', self.container.ovz_id,
'--dump', '--dumpfile', self.dumpfile,
run_as_root=True)
LOG.debug(_('Dumped instance %(instance_id)s to %(dumpfile)s') %
{'instance_id': self.container.ovz_id,
'dumpfile': self.dumpfile})
def undump(self):
"""
Restore a VZ from a dump file
"""
LOG.debug(_('Undumping instance %s') % self.container.ovz_id)
ovz_utils.execute('vzctl', 'restore', self.container.ovz_id, '--undump',
'--dumpfile', self.dumpfile, '--skip_arpdetect',
run_as_root=True)
LOG.debug(_('Undumped instance %(instance_id)s from %(dumpfile)s') %
{'instance_id': self.container.ovz_id,
'dumpfile': self.dumpfile})
def resume(self):
"""
Resume a container from an undumped migration
"""
LOG.debug(_('Resuming instance %s') % self.container.ovz_id)
ovz_utils.execute('vzctl', 'restore', self.container.ovz_id,
'--resume', run_as_root=True)
LOG.debug(_('Resumed instance %s') % self.container.ovz_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.container.ovz_id)
ovz_utils.execute('vzctl', 'chkpnt', self.container.ovz_id,
'--kill', run_as_root=True)
LOG.debug(_('Killed instance %s') % self.container.ovz_id)
def quotadump(self):
"""
Dump the quotas for containers
"""
LOG.debug(_('Dumping quotas for %s') % self.container.ovz_id)
ovz_utils.execute('vzdqdump', self.container.ovz_id, '-U', '-G',
'-T', '>', self.qdumpfile, run_as_root=True)
LOG.debug(_('Dumped quotas for %s') % self.container.ovz_id)
def quotaload(self):
"""
Load quotas from quota file
"""
LOG.debug(_('Loading quotas for %s') % self.container.ovz_id)
ovz_utils.execute('vzdqload', self.container.ovz_id, '-U', '-G', '-T',
'<', self.qdumpfile, run_as_root=True)
LOG.debug(_('Loaded quotas for %s') % self.container.ovz_id)
def quotaenable(self):
"""
enable quotas for a given container
"""
LOG.debug(_('Enabling quotas for %s') % self.container.ovz_id)
ovz_utils.execute('vzquota', 'reload2',
self.container.ovz_id, run_as_root=True)
LOG.debug(_('Enabled quotas for %s') % self.container.ovz_id)
def quota_init(self):
"""
Initialize quotas for instance
"""
LOG.debug(_('Initializing quotas for %s') % self.container.ovz_id)
ovz_utils.execute('vzctl', 'quotainit', self.container.ovz_id)
LOG.debug(_('Initialized quotas for %s') % self.container.ovz_id)
def quota_on(self):
"""
Turn on quotas for instance
"""
LOG.debug(_('Turning on quotas for %s') % self.container.ovz_id)
ovz_utils.execute('vzctl', 'quotaon', self.container.ovz_id,
run_as_root=True)
LOG.debug(_('Turned on quotas for %s') % self.container.ovz_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_src = os.path.abspath(
'%s/%s.%s' % (CONF.ovz_config_dir, self.container.ovz_id,
a_script))
if os.path.exists(a_script_src):
LOG.debug(_('Copying actionscript: %s') % a_script_src)
a_script_dest = os.path.abspath(
'%s/%s.%s' % (self.as_dumpdir, self.container.uuid,
a_script))
ovz_utils.copy(a_script_src, a_script_dest)
LOG.debug(_('Copied actionscript: %(src)s as %(dest)s')
% { 'src': a_script_src, 'dest': a_script_dest })
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_src = os.path.abspath('%s/%s.%s' % (self.as_dumpdir,
self.container.uuid,
a_script))
if os.path.exists(a_script_src):
LOG.debug(_('Restoring actionscript: %s') % a_script_src)
a_script_dest = os.path.abspath(
'%s/%s.%s' % (CONF.ovz_config_dir, self.container.ovz_id,
a_script))
ovz_utils.copy(a_script_src, a_script_dest)
LOG.debug(_('Restored actionscript: %(src)s as %(dest)s')
% { 'src': a_script_src, 'dest': a_script_dest })
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
src_path = self.instance_tarfile
dest_path = CONF.ovz_tmp_dir
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.container.ovz_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.container.ovz_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.container.ovz_id)
sed_regex = 's/%(ctid)s/%(uuid)s/' % {
'ctid': self.container.ovz_id,
'uuid': self.container.uuid,
}
ovz_utils.tar(self.container.ovz_id, self.instance_tarfile,
working_dir=self.instance_parent,
extra=['--transform', sed_regex])
LOG.debug(_('Tarred up instance: %s') % self.container.ovz_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_tarfile)
LOG.debug(_('Make sure directory exists: %s') % self.instance_source)
ovz_utils.make_dir(self.instance_source)
sed_regex = 's/%(uuid)s/%(ctid)s/' % {
'ctid': self.container.ovz_id,
'uuid': self.container.uuid,
}
ovz_utils.untar(self.instance_tarfile, self.instance_parent,
extra=['--transform', sed_regex])
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 ovznovadriver.openvz.migration_drivers import rsync
return rsync.OVZMigrationRsyncTransport(
src_path, dest_path, self.container.ovz_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

@ -1,16 +0,0 @@
# 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

@ -1,66 +0,0 @@
# 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 ovznovadriver.localization import _
from ovznovadriver.openvz.migration_drivers import transport
from ovznovadriver.openvz import utils as ovz_utils
import os
from oslo.config import cfg
CONF = cfg.CONF
LOG = logging.getLogger('ovznovadriver.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

@ -1,73 +0,0 @@
# 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
from ovznovadriver.localization import _
import os
from oslo.config import cfg
CONF = cfg.CONF
LOG = logging.getLogger('ovznovadriver.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

@ -1,201 +0,0 @@
# 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 import netutils
from ovznovadriver.localization import _
from ovznovadriver.openvz import file as ovzfile
from ovznovadriver.openvz.container import OvzContainer
from ovznovadriver.openvz.file_ext import boot as ovzboot
from ovznovadriver.openvz.file_ext import shutdown as ovzshutdown
from ovznovadriver.openvz.network_drivers import tc as ovztc
from ovznovadriver.openvz import utils as ovz_utils
import os
from oslo.config import cfg
CONF = cfg.CONF
LOG = logging.getLogger('ovznovadriver.openvz.network')
class OVZNetworkInterfaces(object):
"""
Helper class for managing interfaces in OpenVz
"""
#TODO(imsplitbit): fix this to work with redhat based distros
def __init__(self, container, interface_info, network_info=None):
"""
Manage the network interfaces for your OpenVz containers.
"""
self.interface_info = interface_info
self.network_info = network_info
LOG.debug(_('Interface info: %s') % self.interface_info)
self.container = container
self.boot_file = ovzboot.OVZBootFile(container.ovz_id, 755)
with self.boot_file:
self.boot_file.set_contents(list())
self.shutdown_file = ovzshutdown.OVZShutdownFile(container.ovz_id, 755)
with self.shutdown_file:
self.shutdown_file.set_contents(list())
def add(self):
"""
Add all interfaces and addresses to the container.
"""
if CONF.ovz_use_veth_devs:
for net_dev in self.interface_info:
self._add_netif(net_dev['name'], net_dev['bridge'],
net_dev['mac'])
tc_rules = ovztc.OVZTcRules()
tc_rules.instance_info(self.container.nova_id,
net_dev['address'],
net_dev['vz_host_if'])
with self.boot_file:
self.boot_file.append(tc_rules.container_start())
with self.shutdown_file:
self.shutdown_file.append(tc_rules.container_stop())
self._fill_templates()
else:
for net_dev in self.interface_info:
self._add_ip(net_dev['address'])
with self.boot_file:
self.boot_file.write()
with self.shutdown_file:
self.shutdown_file.write()
self._set_nameserver(self.interface_info[0]['dns'])
def _fill_templates(self):
"""
Iterate through each file necessary for creating interfaces on a
given platform, open the file and write the contents of the template
to the file.
"""
iface_file = netutils.get_injected_network_template(self.network_info,
use_ipv6=CONF.use_ipv6,
template=CONF.injected_network_template)
for filename in self._filename_factory():
network_file = OVZNetworkFile(filename)
with network_file:
network_file.append(iface_file)
network_file.write()
def _filename_factory(self, variant='debian'):
"""
Generate a path for the file needed to implement an interface
"""
#TODO(imsplitbit): Figure out how to introduce OS hints into nova
# so we can generate support for redhat based distros. This will
# require an os hint to be placed in glance to use for making
# decisions. Then we can create a generator that will generate
# redhat style interface paths like:
#
# /etc/sysconfig/network-scripts/ifcfg-eth0
#
# for now, we just return the debian path.
redhat_path = '/etc/sysconfig/network-scripts/'
debian_path = '/etc/network/interfaces'
prefix = '%(private_dir)s/%(instance_id)s' %\
{'private_dir': CONF.ovz_ve_private_dir,
'instance_id': self.container.ovz_id}
prefix = os.path.abspath(prefix)
#TODO(imsplitbit): fix this placeholder for RedHat compatibility.
if variant == 'redhat':
for net_dev in self.interface_info:
path = prefix + redhat_path + ('ifcfg-%s' % net_dev['name'])
path = os.path.abspath(path)
LOG.debug(_('Generated filename %(path)s') % locals())
yield path
elif variant == 'debian':
path = prefix + debian_path
path = os.path.abspath(path)
LOG.debug(_('Generated filename %(path)s') % locals())
yield path
else:
raise exception.InvalidMetadata(
_('Variant %(variant)s is not known') % locals())
def _add_netif(self, netif, bridge, host_mac):
"""
This is a work around to add the eth devices the way OpenVZ
wants them.
When this works, it runs a command similar to this:
vzctl set 1 --save --netif_add \
eth0,,veth1.eth0,11:11:11:11:11:11,br100
"""
host_if = 'veth%s.%s' % (self.container.ovz_id, netif)
ovz_utils.execute('vzctl', 'set', self.container.ovz_id, '--save',
'--netif_add',
'%s,,%s,%s,%s' % (netif, host_if, host_mac, bridge),
run_as_root=True)
def _add_ip(self, ip):
"""
Add an IP address to a container if you are not using veth devices.
Run the command:
vzctl set <ctid> --save --ipadd <ip>
If this fails to run an exception is raised as this indicates a failure
to create a network available connection within the container thus
making it unusable to all but local users and therefore unusable to
nova.
"""
ovz_utils.execute('vzctl', 'set', self.container.ovz_id, '--save',
'--ipadd', ip, run_as_root=True)
def _set_nameserver(self, dns):
"""
Get the nameserver for the assigned network and set it using
OpenVz's tools.
Run the command:
vzctl set <ctid> --save --nameserver <nameserver>
If this fails to run an exception is raised as this will indicate
the container's inability to do name resolution.
"""
ovz_utils.execute('vzctl', 'set', self.container.ovz_id, '--save',
'--nameserver', dns, run_as_root=True)
class OVZNetworkFile(ovzfile.OVZFile):
"""
An abstraction for network files. This is necessary for multi-platform
support. OpenVz runs on all linux distros and can host all linux distros
but they don't all create interfaces the same way. This should make it
easy to add interface files to all flavors of linux.
"""
def __init__(self, filename):
super(OVZNetworkFile, self).__init__(filename, 644)

View File

@ -1,16 +0,0 @@
# 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

@ -1,59 +0,0 @@
# 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.network import linux_net
from ovznovadriver.localization import _
from nova.openstack.common import log as logging
LOG = logging.getLogger('ovznovadriver.openvz.network_drivers.network_bridge')
class OVZNetworkBridgeDriver(object):
"""
VIF driver for a Linux Bridge
"""
def plug(self, instance, vif):
"""
Ensure that the bridge exists and add a vif to it.
"""
if (not vif['network'].get_meta('should_create_bridge', False) and
vif['network'].get_meta('should_create_vlan', False)):
if vif['network'].get_meta('should_create_vlan', False):
LOG.debug(_('Ensuring bridge %(bridge)s and vlan %(vlan)s') %
{'bridge': vif['network']['bridge'],
'vlan': vif['network'].get_meta('vlan')})
linux_net.LinuxBridgeInterfaceDriver.ensure_vlan_bridge(
vif['network'].get_meta('vlan'),
vif['network']['bridge'],
vif['network'].get_meta('bridge_interface'))
else:
LOG.debug(_('Ensuring bridge %s') % vif['network']['bridge'])
linux_net.LinuxBridgeInterfaceDriver.ensure_bridge(
vif['network']['bridge'],
vif['network'].get_meta('bridge_interface'))
def unplug(self, instance, vif):
"""
No manual unplugging required
"""
pass

View File

@ -1,284 +0,0 @@
# 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
from nova.conductor import api
from nova import context
from nova import exception
from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from ovznovadriver.localization import _
from ovznovadriver.openvz import utils as ovz_utils
import os
from oslo.config import cfg
import random
CONF = cfg.CONF
LOG = logging.getLogger('ovznovadriver.openvz.network_drivers.tc')
global _ovz_tc_available_ids
global _ovz_tc_inflight_ids
_ovz_tc_available_ids = None
_ovz_tc_inflight_ids = None
class OVZTcRules(object):
# Set our class variables if they don't exist
# TODO(imsplitbit): figure out if there is a more pythonic way to do this
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):
"""
This class is used to return TC rulesets for both a host and guest for
use with host and guest startup and shutdown.
"""
self.conductor = api.API()
if not len(OVZTcRules._ovz_tc_available_ids):
LOG.debug(_('Available_ids is empty, filling it with numbers'))
OVZTcRules._ovz_tc_available_ids = [
i for i in range(1, CONF.ovz_tc_id_max)
]
# do an initial clean of the available ids
self._remove_used_ids()
LOG.debug(
_('OVZTcRules thinks ovz_tc_host_slave_device is set to %s')
% CONF.ovz_tc_host_slave_device)
def instance_info(self, instance_id, address, vz_iface):
"""
Use this method when creating or resizing an instance. It will
generate a new tc ruleset
:param instance_id: Instance to generate the rules for
:param address: IP address for the instance
:param vz_iface: interface on the hosts bridge that is associated with
the instance
"""
# TODO(jcru) figure out if there is a case for no instance_id else
# take it out
if not instance_id:
self.instance_type = dict()
self.instance_type['memory_mb'] = 2048
else:
admin_context = context.get_admin_context()
self.instance = self.conductor.instance_get(
admin_context, instance_id)
self.instance_type = self.conductor.instance_type_get(
admin_context, self.instance['instance_type_id'])
LOG.debug(_('CT TC address: %s') % address)
self.address = address
self.vz_iface = vz_iface
# TODO(jcru) Ideally wish to move this to resources.py
# check under instance_type/flavor extra_specs to see if bandwidth has
# been predefined for flavor
extra_specs = self.instance_type.get("extra_specs", {})
self.bandwidth = extra_specs.get("vz_bandwidth", None)
if not self.bandwidth:
LOG.debug(_('No (vz_bandwidth) extra_specs key/value defined for '
'flavor id (%s)') % self.instance_type['flavorid'])
# Calculate the bandwidth total by figuring out how many units we
# have
self.bandwidth = int(round(self.instance_type['memory_mb'] /
CONF.ovz_memory_unit_size)) * CONF.ovz_tc_mbit_per_unit
else:
int(self.bandwidth)
LOG.debug(_('Allotted bandwidth: %s') % self.bandwidth)
self.tc_id = self._get_instance_tc_id()
if not self.tc_id:
LOG.debug(_('No preassigned tc_id for %s, getting a new one') %
instance_id)
self.tc_id = self.get_id()
self._save_instance_tc_id()
LOG.debug(_('Saved the tc_id in the database for the instance'))
@lockutils.synchronized('get_id_lock', 'openvz-')
def get_id(self):
"""
Uses nova utils decorator @lockutils.synchronized to make sure that we
do not return duplicate available ids. This will return a random id
number between 1 and 9999 which are the limits of TC.
"""
self._remove_used_ids()
LOG.debug(_('Pulling new TC id'))
tc_id = self._pull_id()
LOG.debug(_('TC id %s pulled, testing for dupe') % tc_id)
while tc_id in OVZTcRules._ovz_tc_inflight_ids:
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') % tc_id)
self._reserve_id(tc_id)
return tc_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
}
ovz_utils.save_instance_metadata(self.instance['id'],
'tc_id', self.tc_id,
fail_on_dupe_key=False)
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
}
ovz_utils.save_instance_metadata(self.instance['id'],
'tc_id', self.tc_id,
fail_on_dupe_key=False)
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):
"""
read and return the template file
"""
full_template_path = '%s/%s' % (
CONF.ovz_tc_template_dir, template_name)
full_template_path = os.path.abspath(full_template_path)
try:
LOG.debug(_('Opening template file: %s') % full_template_path)
template_file = open(full_template_path).read()
LOG.debug(_('Template file opened successfully'))
return template_file
except Exception as err:
LOG.error(_('Unable to open template: %s') % full_template_path)
raise exception.FileNotFound(err)
def _fill_template(self, template, search_list):
"""
Take the vars in search_list and fill out template
"""
return str(Template.Template(template, searchList=[search_list]))
def _pull_id(self):
"""
Pop a random id from the list of available ids for tc rules
"""
return OVZTcRules._ovz_tc_available_ids[random.randint(
0, len(OVZTcRules._ovz_tc_available_ids) - 1)]
def _list_existing_ids(self):
LOG.debug(_('Attempting to list existing IDs'))
out = ovz_utils.execute('tc', 'filter', 'show', 'dev',
CONF.ovz_tc_host_slave_device,
run_as_root=True)
ids = list()
for line in out.splitlines():
line = line.split()
if line[0] == 'filter':
tc_id = int(line[6])
if tc_id not in ids:
ids.append(int(tc_id))
# get the metadata for instances from the database
# this will provide us with any ids of instances that aren't
# currently running so that we can remove active and provisioned
# but inactive ids from the available list
instances_metadata = ovz_utils.read_all_instance_metadata()
LOG.debug(_('Instances metadata: %s') % instances_metadata)
if instances_metadata:
for instance_id, meta in instances_metadata.iteritems():
tc_id = meta.get('tc_id')
if tc_id:
if tc_id not in ids:
LOG.debug(
_('TC id "%(tc_id)s" for instance '
'"%(instance_id)s" found') % locals())
ids.append(int(tc_id))
return ids
def _reserve_id(self, tc_id):
"""
This removes the id from the _ovz_tc_available_ids and adds it to the
_ovz_tc_inflight_ids
"""
LOG.debug(_('Beginning reservation process'))
OVZTcRules._ovz_tc_inflight_ids.append(tc_id)
LOG.debug(_('Added id "%s" to _ovz_tc_inflight_ids') % tc_id)
OVZTcRules._ovz_tc_available_ids.remove(tc_id)
LOG.debug(_('Removed id "%s" from _ovz_tc_available_ids') % tc_id)
def _remove_used_ids(self):
"""
Clean ids that are currently provisioned on the host from the
list of _ovz_tc_available_ids
"""
LOG.debug(_('Beginning cleanup of used ids'))
used_ids = self._list_existing_ids()
LOG.debug(_('Used ids found, removing from _ovz_tc_available_ids'))
for tc_id in used_ids:
if tc_id in OVZTcRules._ovz_tc_available_ids:
OVZTcRules._ovz_tc_available_ids.remove(tc_id)
LOG.debug(_('Removed all ids in use'))
def _save_instance_tc_id(self):
"""
Save the tc id in the instance metadata in the database
"""
ovz_utils.save_instance_metadata(self.instance['id'], 'tc_id',
self.tc_id, fail_on_dupe_key=False)
def _get_instance_tc_id(self):
"""
Look up instance metadata in the db and see if there is already
a tc_id for the instance
"""
instance_metadata = ovz_utils.read_instance_metadata(
self.instance['id'])
LOG.debug(_('Instances metadata: %s') % instance_metadata)
if instance_metadata:
tc_id = instance_metadata.get('tc_id')
LOG.debug(
_('TC id for instance %(instance_id)s is %(tc_id)s') %
{'instance_id': self.instance['id'], 'tc_id': tc_id})
return tc_id

View File

@ -1,8 +0,0 @@
tc qdisc add dev ${vz_iface} root handle 1: htb default 10
tc class add dev ${vz_iface} parent 1: classid 1:1 htb rate ${line_speed}mbit ceil ${line_speed}mbit burst 15k
tc class add dev ${vz_iface} parent 1:1 classid 1:${prio} htb rate ${bandwidth}mbit ceil ${bandwidth}mbit burst 15k
tc qdisc add dev ${vz_iface} parent 1:${prio} handle ${prio}: sfq perturb 10
tc filter replace dev ${vz_iface} protocol ip parent 1: prio ${prio} u32 match ip dst ${vz_address} flowid 1:${prio}
tc class add dev ${host_iface} parent 1:1 classid 1:${prio} htb rate ${bandwidth}mbit ceil ${bandwidth}mbit burst 15k
tc qdisc add dev ${host_iface} parent 1:${prio} handle ${prio}: sfq perturb 10
tc filter replace dev ${host_iface} protocol ip parent 1: prio ${prio} u32 match ip src ${vz_address} flowid 1:${prio}

View File

@ -1,6 +0,0 @@
tc filter del dev ${host_iface} protocol ip parent 1:0 prio ${prio}
tc class del dev ${host_iface} parent 1:1 classid 1:${prio} htb rate ${bandwidth}mbit
tc qdisc delete dev ${host_iface} parent 1:${prio} handle ${prio}
tc filter del dev ${vz_iface} protocol ip parent 1:0 prio ${prio}
tc class del dev ${vz_iface} parent 1:1 classid 1:${prio} htb rate ${bandwidth}mbit
tc qdisc delete dev ${vz_iface} parent 1:${prio} handle ${prio}

View File

@ -1,5 +0,0 @@
# Outgoing traffic control
# Should only be run once at setup or all rules are wiped
tc qdisc del dev ${host_iface} root
tc qdisc add dev ${host_iface} root handle 1: htb default 10
tc class add dev ${host_iface} parent 1: classid 1:1 htb rate ${line_speed}mbit ceil ${line_speed}mbit burst 15k

View File

@ -1 +0,0 @@
tc qdisc del dev ${host_iface} root

View File

@ -1,405 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2014 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 math
from ovznovadriver.openvz import utils as ovz_utils
from ovznovadriver.openvz.file_ext import boot as ovzboot
from ovznovadriver.openvz.file_ext import shutdown as ovzshutdown
from ovznovadriver.openvz.network_drivers import tc as ovztc
from ovznovadriver.localization import _
from oslo.config import cfg
from nova.openstack.common import log as logging
__openvz_resource_opts = [
cfg.BoolOpt('ovz_use_cpuunit',
default=True,
help='Use OpenVz cpuunits for guaranteed minimums'),
cfg.BoolOpt('ovz_use_cpulimit',
default=True,
help='Use OpenVz cpulimit for maximum cpu limits'),
cfg.FloatOpt('ovz_cpulimit_overcommit_multiplier',
default=1.0,
help='Multiplier for cpulimit to facilitate over '
'committing cpu resources'),
cfg.BoolOpt('ovz_use_cpus',
default=True,
help='Use OpenVz cpus for max cpus '
'available to the container'),
cfg.BoolOpt('ovz_use_ioprio',
default=True,
help='Use IO fair scheduling'),
cfg.BoolOpt('ovz_use_ubc',
default=False,
help='Use OpenVz User BeanCounters memory management model '
'instead of vswap'),
cfg.IntOpt('ovz_file_descriptors_per_unit',
default=4096,
help='Max open file descriptors per memory unit'),
cfg.IntOpt('ovz_memory_unit_size',
default=512,
help='Unit size in MB'),
cfg.BoolOpt('ovz_use_disk_quotas',
default=True,
help='Use disk quotas to contain disk usage'),
cfg.StrOpt('ovz_disk_space_increment',
default='G',
help='Disk subscription increment'),
cfg.FloatOpt('ovz_disk_space_oversub_percent',
default=1.10,
help='Local disk over subscription percentage'),
cfg.IntOpt('ovz_kmemsize_percent_of_memory',
default=20,
help='Percent of memory of the container to allow to be used '
'by the kernel'),
cfg.IntOpt('ovz_kmemsize_barrier_differential',
default=10,
help='Difference of kmemsize barrier vs limit'),
cfg.StrOpt('ovz_default_config',
default='basic',
help='Default config file to apply if no config is set for '
'flavor extra_specs'),
]
CONF = cfg.CONF
CONF.register_opts(__openvz_resource_opts)
LOG = logging.getLogger(__name__)
class VZResourceManager(object):
"""Manage OpenVz container resources
The purpose of this class is meant to decide/calculate vz resource configs
and apply them through the Container class"""
# TODO (jcru) make this a config?
# OpenVz sets the upper limit of cpuunits to 500000
MAX_CPUUNITS = 500000
def __init__(self, virtapi):
"""Requires virtapi (api to conductor) to get flavor info"""
self.virtapi = virtapi
# TODO (jcru) replace dict (self.utility) with self.cpulimit?
self.utility = dict()
def _get_flavor_info(self, context, flavor_id):
"""Get the latest flavor info which contains extra_specs"""
# instnace_type refers to the flavor (what you see in flavor list)
return self.virtapi.flavor_get(context, flavor_id)
def _calc_pages(self, instance_memory, block_size=4096):
"""
Returns the number of pages for a given size of storage/memory
"""
return ((int(instance_memory) * 1024) * 1024) / block_size
def _percent_of_resource(self, instance_memory):
"""
In order to evenly distribute resources this method will calculate a
multiplier based on memory consumption for the allocated container and
the overall host memory. This can then be applied to the cpuunits in
self.utility to be passed as an argument to the self._set_cpuunits
method to limit cpu usage of the container to an accurate percentage of
the host. This is only done on self.spawn so that later, should
someone choose to do so, they can adjust the container's cpu usage
up or down.
"""
cont_mem_mb = (
float(instance_memory) / float(ovz_utils.get_memory_mb_total()))
# We shouldn't ever have more than 100% but if for some unforseen
# reason we do, lets limit it to 1 to make all of the other
# calculations come out clean.
if cont_mem_mb > 1:
LOG.error(_('_percent_of_resource came up with more than 100%'))
return 1.0
else:
return cont_mem_mb
def get_cpulimit(self):
"""
Fetch the total possible cpu processing limit in percentage to be
divided up across all containers. This is expressed in percentage
being added up by logical processor. If there are 24 logical
processors then the total cpulimit for the host node will be
2400.
"""
self.utility['CPULIMIT'] = ovz_utils.get_vcpu_total() * 100
LOG.debug(_('Updated cpulimit in utility'))
LOG.debug(
_('Current cpulimit in utility: %s') % self.utility['CPULIMIT'])
def get_cpuunits_usage(self):
"""
Use openvz tools to discover the total used processing power. This is
done using the vzcpucheck -v command.
Run the command:
vzcpucheck -v
If this fails to run an exception should not be raised as this is a
soft error and results only in the lack of knowledge of what the
current cpuunit usage of each container.
"""
out = ovz_utils.execute(
'vzcpucheck', '-v', run_as_root=True, raise_on_error=False)
if out:
for line in out.splitlines():
line = line.split()
if len(line) > 0:
if line[0].isdigit():
LOG.debug(_('Usage for CTID %(id)s: %(usage)s') %
{'id': line[0], 'usage': line[1]})
if int(line[0]) not in self.utility.keys():
self.utility[int(line[0])] = dict()
self.utility[int(line[0])] = int(line[1])
def configure_container_resources(self, context, container,
requested_flavor_id):
instance_type = self._get_flavor_info(context, requested_flavor_id)
self._setup_memory(container, instance_type)
self._setup_file_limits(container, instance_type)
self._setup_cpu(container, instance_type)
self._setup_io(container, instance_type)
self._setup_disk_quota(container, instance_type)
# TODO(jcru) normally we would pass a context and requested flavor as
# configure_container_resources but we look up instance_type within tc
# code. Ideally all of the networking setup would happen here
def configure_container_network(self, container, network_info,
is_migration=False):
self._generate_tc_rules(container, network_info, is_migration)
def apply_config(self, context, container, requested_flavor_id):
"""In order to succesfully apply a config file the file must exist
within /etc/vz/config if just the file name is passed (e.g. samples)
or the full file path must be specified
It is possible to not define a config file which in that case nothing
is applied.
"""
instance_type = self._get_flavor_info(context, requested_flavor_id)
instance_type_extra_specs = instance_type.get('extra_specs', {})
# TODO(jcru) handle default config for both UBC and Vswap
config_file = instance_type_extra_specs.get("vz_config_file",
CONF.ovz_default_config)
if config_file:
container.apply_config(config_file)
def _setup_memory(self, container, instance_type):
"""Decides on what VZ memory model to use.
By default we use Vswap. If User Beancounters (UBC) is desired, enable
through config.
"""
if CONF.ovz_use_ubc:
self._setup_memory_with_ubc(instance_type, container)
return
self._setup_memory_with_vswap(container, instance_type)
def _setup_memory_with_ubc(self, container, instance_type):
instance_memory_mb = int(instance_type.get('memory_mb'))
instance_memory_bytes = ((instance_memory_mb * 1024) * 1024)
instance_memory_pages = self._calc_pages(instance_memory_mb)
# Now use the configuration CONF to calculate the appropriate
# values for both barrier and limit.
kmem_limit = int(instance_memory_mb * (
float(CONF.ovz_kmemsize_percent_of_memory) / 100.0))
kmem_barrier = int(kmem_limit * (
float(CONF.ovz_kmemsize_barrier_differential) / 100.0))
container.set_vmguarpages(instance_memory_pages)
container.set_privvmpages(instance_memory_pages)
container.set_kmemsize(kmem_barrier, kmem_limit)
def _setup_memory_with_vswap(self, container, instance_type):
memory = int(instance_type.get('memory_mb'))
swap = instance_type.get('swap', 0)
# Memory should be in MB
memory = "%sM" % memory
# Swap should be in GB
swap = "%sG" % swap
container.set_vswap(memory, swap)
def _setup_file_limits(self, container, instance_type):
instance_memory_mb = int(instance_type.get('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
container.set_numfiles(max_fd)
container.set_numflock(max_fd)
# TODO(jcru) overide caclulated values?
def _setup_cpu(self, container, instance_type):
"""
"""
instance_memory_mb = instance_type.get('memory_mb')
instance_type_extra_specs = instance_type.get('extra_specs', {})
percent_of_resource = self._percent_of_resource(instance_memory_mb)
if CONF.ovz_use_cpuunit:
LOG.debug(_('Reported cpuunits %s') % self.MAX_CPUUNITS)
LOG.debug(_('Reported percent of resource: %s') % percent_of_resource)
units = int(round(self.MAX_CPUUNITS * percent_of_resource))
if units > self.MAX_CPUUNITS:
units = self.MAX_CPUUNITS
container.set_cpuunits(units)
if CONF.ovz_use_cpulimit:
# Check if cpulimit for flavor is predefined in flavors extra_specs
cpulimit = instance_type_extra_specs.get('vz_cpulimit', None)
if not cpulimit:
cpulimit = int(round(
(self.utility['CPULIMIT'] * percent_of_resource) *
CONF.ovz_cpulimit_overcommit_multiplier))
else:
cpulimit = int(cpulimit)
if cpulimit > self.utility['CPULIMIT']:
LOG.warning(_("The cpulimit that was calculated or predefined "
"(%s) is to high based on the CPULIMIT (%s)") %
(cpulimit, self.utility['CPULIMIT']))
LOG.warning(_("Using CPULIMIT instead."))
cpulimit = self.utility['CPULIMIT']
container.set_cpulimit(cpulimit)
if CONF.ovz_use_cpus:
vcpus = int(instance_type.get('vcpus'))
LOG.debug(_('VCPUs: %s') % vcpus)
utility_cpus = self.utility['CPULIMIT'] / 100
if vcpus > utility_cpus:
LOG.debug(
_('OpenVZ thinks vcpus "%(vcpus)s" '
'is greater than "%(utility_cpus)s"') % locals())
# We can't set cpus higher than the number of actual logical cores
# on the system so set a cap here
vcpus = self.utility['CPULIMIT'] / 100
LOG.debug(_('VCPUs: %s') % vcpus)
container.set_cpus(vcpus)
def _setup_io(self, container, instance_type):
# The old algorithm made it impossible to distinguish between a
# 512MB container and a 2048MB container for IO priority. We will
# for now follow a simple map to create a more non-linear
# relationship between the flavor sizes and their IO priority groups
# The IO priority of a container is grouped in 1 of 8 groups ranging
# from 0 to 7. We can calculate an appropriate value by finding out
# how many ovz_memory_unit_size chunks are in the container's memory
# allocation and then using python's math library to solve for that
# number's logarithm.
if CONF.ovz_use_ioprio:
instance_memory_mb = instance_type.get('memory_mb')
num_chunks = int(int(instance_memory_mb) / CONF.ovz_memory_unit_size)
try:
ioprio = int(round(math.log(num_chunks, 2)))
except ValueError:
ioprio = 0
if ioprio > 7:
# ioprio can't be higher than 7 so set a ceiling
ioprio = 7
container.set_ioprio(ioprio)
def _setup_disk_quota(self, container, instance_type):
if CONF.ovz_use_disk_quotas:
instance_root_gb = instance_type.get('root_gb')
soft_limit = int(instance_root_gb)
hard_limit = int(soft_limit * CONF.ovz_disk_space_oversub_percent)
# Now set the increment of the limit. I do this here so that I don't
# have to do this in every line above.
soft_limit = '%s%s' % (soft_limit, CONF.ovz_disk_space_increment)
hard_limit = '%s%s' % (hard_limit, CONF.ovz_disk_space_increment)
container.set_diskspace(soft_limit, hard_limit)
# TODO(jcru) move more of tc logic into here ?
def _generate_tc_rules(self, container, network_info, is_migration=False):
"""
Utility method to generate tc info for instances that have been
resized and/or migrated
"""
LOG.debug(_('Setting network sizing'))
boot_file = ovzboot.OVZBootFile(container.ovz_id, 755)
shutdown_file = ovzshutdown.OVZShutdownFile(container.ovz_id, 755)
if not is_migration:
with shutdown_file:
LOG.debug(_('Cleaning TC rules for %s') % container.nova_id)
shutdown_file.read()
shutdown_file.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(container.nova_id)
tc_id = meta.get('tc_id', None)
if tc_id:
ovz_utils.remove_instance_metadata_key(container.nova_id, 'tc_id')
with shutdown_file:
shutdown_file.set_contents(list())
with boot_file:
boot_file.set_contents(list())
LOG.debug(_('Getting network dict for: %s') % container.uuid)
interfaces = ovz_utils.generate_network_dict(container,
network_info)
for net_dev in interfaces:
LOG.debug(_('Adding tc rules for: %s') %
net_dev['vz_host_if'])
tc = ovztc.OVZTcRules()
tc.instance_info(container.nova_id, net_dev['address'],
net_dev['vz_host_if'])
with boot_file:
boot_file.append(tc.container_start())
with shutdown_file:
shutdown_file.append(tc.container_stop())
with boot_file:
if not is_migration:
# during migration, the instance isn't yet running, so it'll
# just spew errors to attempt to apply these rules before then
LOG.debug(_('Running TC rules for: %s') % container.ovz_id)
boot_file.run_contents()
LOG.debug(_('Saving TC rules for: %s') % container.ovz_id)
boot_file.write()
with shutdown_file:
shutdown_file.write()

View File

@ -1,610 +0,0 @@
# 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.
"""
import multiprocessing
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 ovznovadriver.localization import _
from nova import utils
import os
from oslo.config import cfg
import platform
import re
import sys
import uuid
CONF = cfg.CONF
LOG = logging.getLogger('ovznovadriver.openvz.utils')
conductor = api.API()
def execute(*cmd, **kwargs):
"""
This is a wrapper for utils.execute so as to reduce the amount of
duplicated code in the driver.
"""
if 'raise_on_error' in kwargs:
raise_on_error = kwargs.pop('raise_on_error')
else:
raise_on_error = True
try:
out, err = utils.execute(*cmd, **kwargs)
if out:
LOG.debug(_('Stdout from %(command)s: %(out)s') %
{'command': cmd[0], 'out': out})
if err:
LOG.debug(_('Stderr from %(command)s: %(out)s') %
{'command': cmd[0], 'out': err})
return out
except processutils.ProcessExecutionError as err:
msg = (_('Stderr from %(command)s: %(out)s') %
{'command': cmd[0], 'out': err})
if raise_on_error:
LOG.error(msg)
raise exception.InstanceUnacceptable(
_('Error running %(command)s: %(out)s') %
{'command': cmd[0], 'out': err})
else:
LOG.warn(msg)
return None
def mkfs(path, fs, fs_uuid=None, fs_label=None):
"""Format a file or block device
:param fs: Filesystem type (examples include 'ext3', 'ext4'
'btrfs', etc.)
:param path: Path to file or block device to format
:param fs_uuid: Volume uuid to use
:param fs_label: Volume label to use
"""
if not fs_uuid:
fs_uuid = str(uuid.uuid4())
args = ['mkfs', '-F', '-t', fs]
if fs_uuid:
args.extend(['-U', fs_uuid])
if fs_label:
args.extend(['-L', fs_label])
args.append(path)
LOG.debug(_('Mkfs command: %s') % args)
execute(*args, run_as_root=True)
def get_fs_uuid(device):
"""
Because we may need to operate on a volume by it's uuid
we need a way to see if a filesystem has a uuid on it
"""
LOG.debug(_('Getting FS UUID for: %s') % device)
out = execute('blkid', '-o', 'value', '-s', 'UUID', device,
run_as_root=True, raise_on_error=False)
if out:
LOG.debug(_('Found something in get_fs_uuid'))
for line in out.split('\n'):
line = line.strip()
LOG.debug(_('Examining line: %s') % line)
result = re.search(
'[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}',
line)
if result:
LOG.debug(
_('Found the FS UUID for: %(device)s It is: %(uuid)s') %
{'device': device, 'uuid': result.group(0)})
return result.group(0)
return None
def get_vcpu_total():
"""Get vcpu number of physical computer.
:returns: the number of cpu core.
"""
LOG.debug(_('Getting cpu count'))
# On certain platforms, this will raise a NotImplementedError.
try:
cpu_count = multiprocessing.cpu_count()
LOG.debug(_('Got cpu count: %s') % cpu_count)
return cpu_count
except NotImplementedError:
LOG.warn(_("Cannot get the number of cpu, because this "
"function is not implemented for this platform. "
"This error can be safely ignored for now."))
return 0
def get_cpuinfo():
"""
Gather detailed statistics on the host's cpus
"""
if sys.platform.upper() not in ['LINUX2', 'LINUX3']:
return 0
cpuinfo = dict()
cpuinfo['features'] = list()
# Gather the per core/hyperthread info for accumulation
for line in open('/proc/cpuinfo').readlines():
# Do some cleaning up to make searching for keys easier
line = line.split(':')
line[0] = line[0].strip().replace(' ', '_')
if len(line) > 1:
line[1] = line[1].strip()
if line[0] == 'vendor_id':
cpuinfo['vendor'] = line[1]
if line[0] == 'model_name':
cpuinfo['model'] = line[1]
if line[0] == 'flags':
cpuinfo['features'] += line[1].split()
cpuinfo['arch'] = platform.uname()[4]
# dedupe the features
cpuinfo['features'] = list(set(cpuinfo['features']))
return cpuinfo
def get_iscsi_initiator():
"""
This is a basic implementation of the iscsi stuff needed for external
volumes. This should live in a utility module when the openvz driver is
broken up.
"""
# code borrowed from libvirt utils
contents = utils.read_file_as_root('/etc/iscsi/initiatorname.iscsi')
for l in contents.split('\n'):
if l.startswith('InitiatorName='):
return l[l.index('=') + 1:].strip()
def get_cpuunits_capability():
"""
Use openvz tools to discover the total processing capability of the
host node. This is done using the vzcpucheck utility.
Run the command:
vzcpucheck
If this fails to run an exception is raised because the output of this
method is required to calculate the overall bean count available on the
host to be carved up for guests to use.
"""
result = {}
out = execute('vzcpucheck', run_as_root=True)
for line in out.splitlines():
line = line.split()
if len(line) > 0:
if line[0] == 'Power':
LOG.debug(_('Power of host: %s') % line[4])
result['total'] = int(line[4])
elif line[0] == 'Current':
LOG.debug(_('Current cpuunits subscribed: %s') % line[3])
result['subscribed'] = int(line[3])
if len(result.keys()) == 2:
return result
else:
raise exception.InvalidCPUInfo(
_("Cannot determine the CPUUNITS for host"))
def get_vcpu_used():
"""
OpenVz doesn't have the concept of VCPUs but we can approximate
what they would be by looking at the percentage of subscribed
cpuunits.
:returns: The total number of vcpu that currently used.
"""
cpuunits = get_cpuunits_capability()
cpus = get_vcpu_total()
pct_cpuunits_used = float(cpuunits['subscribed']) / cpuunits['total']
return int(cpus * pct_cpuunits_used)
def get_memory_mb_total():
"""Get the total memory size(MB) of physical computer.
:returns: the total amount of memory(MB).
"""
if sys.platform.upper() not in ['LINUX2', 'LINUX3']:
return 1
meminfo = open('/proc/meminfo').read().split()
idx = meminfo.index(u'MemTotal:')
# transforming kb to mb.
return int(meminfo[idx + 1]) / 1024
def get_local_gb(path):
"""
Get the total hard drive space at <path>
"""
hddinfo = os.statvfs(path)
total = hddinfo.f_frsize * hddinfo.f_blocks
free = hddinfo.f_frsize * hddinfo.f_bavail
used = hddinfo.f_frsize * (hddinfo.f_blocks - hddinfo.f_bfree)
return {'total': total, 'free': free, 'used': used}
def get_local_gb_total():
"""
Get the total hdd size(GB) of physical computer.
:returns:
The total amount of HDD(GB).
Note that this value shows a partition where
OVZ_VE_PRIVATE_DIR is.
"""
return get_local_gb(CONF.ovz_ve_private_dir)['total'] / (1024 ** 3)
def get_local_gb_used():
"""
Get the total used disk space on the host computer.
:returns:
The total amount of HDD(GB)
Note that this value show a partition where
OVZ_VE_PRIVATE_DIR is.
"""
return get_local_gb(CONF.ovz_ve_private_dir)['used'] / (1024 ** 3)
def get_hypervisor_type():
"""
Just return the type of hypervisor we are using. This will always
be 'openvz'
"""
return 'openvz'
def get_hypervisor_version():
"""
Since the version of the hypervisor is really determined by the kernel
it is running lets just return that for version. I don't think it
matters at this point and this is the most accurate representation of
what *version* we are running.
"""
return platform.uname()[2]
def make_dir(path):
"""
This is the method that actually creates directories. This is used by
make_path and can be called directly as a utility to create
directories.
Run the command:
mkdir -p <path>
If this doesn't run an exception is raised as this path creation is
required to ensure that other file operations are successful. Such
as creating the path for a file yet to be created.
"""
if not os.path.exists(path):
execute('mkdir', '-p', path, run_as_root=True)
def delete_path(path):
"""
After a volume has been detached and the mount statements have been
removed from the mount configuration for a container we want to remove
the paths created on the host system so as not to leave orphaned files
and directories on the system.
This runs a command like:
sudo rmdir /mnt/100/var/lib/mysql
"""
try:
execute('rmdir', path, run_as_root=True)
return True
except exception.InstanceUnacceptable:
return False
def set_permissions(filename, permissions):
"""
Because nova runs as an unprivileged user we need a way to mangle
permissions on files that may be owned by root for manipulation
Run the command:
chmod <permissions> <filename>
If this doesn't run an exception is raised because the permissions not
being set to what the application believes they are set to can cause
a failure of epic proportions.
"""
execute('chmod', permissions, filename, run_as_root=True)
def format_system_metadata(metadata):
"""
System metadata returned by conductor contains data that is irrelevant to
OpenVz and the nova driver. This method sanitizes this information down
to key / value pairs in a list
:param metadata: instance['system_metadata']
:return: dict
"""
if isinstance(metadata, dict):
return metadata
fmt_meta = dict()
if isinstance(metadata, list):
for item in metadata:
meta_item = dict()
for key, value in item.iteritems():
if key in ['key', 'value']:
meta_item[key] = value
fmt_meta[meta_item['key']] = meta_item['value']
return fmt_meta
def save_instance_metadata(instance_id, key, value, fail_on_dupe_key=False):
"""
Simple wrapper for saving data to the db as metadata for an instance
:param instance_id: Instance id to add metadata to
:param key: Key to save to db
:param value: Value of key to save to db
"""
LOG.debug(
_('Beginning saving instance metadata for '
'%(instance_id)s: {%(key)s: %(value)s}') % locals())
admin_context = context.get_admin_context()
LOG.debug(_('Got admin context'))
try:
instance = conductor.instance_get(admin_context, instance_id)
LOG.debug(_('Fetched instance: %s') % instance)
LOG.debug(_('Fixing system_metadata'))
sysmeta = format_system_metadata(instance['system_metadata'])
if key in sysmeta:
if fail_on_dupe_key:
raise KeyError(_('Key %(key)s is set already') % locals())
sysmeta[key] = value
conductor.instance_update(
admin_context, instance['uuid'], system_metadata=sysmeta)
LOG.debug(_('Updated instance metadata'))
except exception.InstanceNotFound as err:
LOG.error(_('Instance not in the database: %s') % instance_id)
LOG.error(_('Consistency check is needed, orphaned instance: %s') %
instance_id)
def read_instance_metadata(instance_id):
"""
Read the metadata in the database for a given instance
:param instance_id: Instance to read all metadata for from the db
:returns: dict()
"""
LOG.debug(_('Beginning read_instance_metadata: %s') % instance_id)
admin_context = context.get_admin_context()
LOG.debug(_('Got admin context for db access'))
try:
instance = conductor.instance_get(admin_context, instance_id)
LOG.debug(_('Fetched instance'))
LOG.debug(
_('Results from read_instance_metadata: %s') %
instance['system_metadata'])
return format_system_metadata(instance['system_metadata'])
except exception.InstanceNotFound:
LOG.error(_('Instance not in the database: %s') % instance_id)
LOG.error(_('Consistency check is needed, orphaned instance: %s') %
instance_id)
return dict()
def read_all_instance_metadata():
"""
Fetch a list of all instance ids on the local host and for each get
the associated metadata and return it in a dictionary
:returns dict():
"""
from ovznovadriver.openvz.container import OvzContainers
instances_metadata = {}
containers = OvzContainers.list()
for container in containers:
instances_metadata[container.nova_id] = read_instance_metadata(
container.nova_id)
return instances_metadata
def remove_instance_metadata_key(instance_id, key):
"""
Remove a specific key from an instance's metadata
"""
LOG.debug(_('Getting an admin context for remove_instance_metadata_key'))
admin_context = context.get_admin_context()
LOG.debug(_('Got admin context, fetching instance ref'))
instance = conductor.instance_get(admin_context, instance_id)
LOG.debug(_('Got instance ref, beginning deletion of metadata'))
sysmeta = format_system_metadata(instance['system_metadata'])
if key in sysmeta:
LOG.debug(_('Key %s exists in system_metadata') % key)
sysmeta.pop(key)
conductor.instance_update(
admin_context, instance['uuid'], system_metadata=sysmeta)
return True
else:
LOG.debug(_('Key %s does not exist in system_metadata') % key)
return False
def remove_instance_metadata(instance_id):
"""
Clean up instance metadata for an instance
"""
openvz_metadata_keys = ('tc_id','migration_type')
try:
for key in openvz_metadata_keys:
LOG.debug(_('Removing metadata key: %s') % key)
remove_instance_metadata_key(instance_id, key)
LOG.debug(_('Deleted metadata for %(instance_id)s') % locals())
return True
except exception.InstanceNotFound as err:
LOG.error(_('Instance not in the database: %s') % instance_id)
LOG.error(_('Consistency check is needed, orphaned instance: %s') %
instance_id)
return False
def generate_network_dict(container, network_info):
interfaces = list()
interface_num = -1
for vif in network_info:
if vif.labeled_ips():
interface_num += 1
v6_subnets = []
v4_subnets = []
for subnet in vif['network']['subnets']:
if subnet['version'] == 4:
v4_subnets.append(subnet)
elif subnet['version'] == 6:
v6_subnets.append(subnet)
#TODO(imsplitbit): make this work for ipv6
address_v6 = None
gateway_v6 = None
netmask_v6 = None
if CONF.use_ipv6:
if v6_subnets:
address_v6 = v6_subnets[0]['ips'][0]['address']
netmask_v6 = v6_subnets[0].as_netaddr()._prefixlen
gateway_v6 = v6_subnets[0]['gateway']
interface_info = {
'id': container.ovz_id,
'interface_number': interface_num,
'bridge': vif['network']['bridge'],
'name': 'eth%d' % interface_num,
'mac': vif['address'],
'address': v4_subnets[0]['ips'][0]['address'],
'netmask': v4_subnets[0].as_netaddr().netmask,
'gateway': v4_subnets[0]['gateway'],
'broadcast': v4_subnets[0].as_netaddr().broadcast,
'dns': ' '.join([ip['address'] for ip in v4_subnets[0]['dns']]),
'address_v6': address_v6,
'gateway_v6': gateway_v6,
'netmask_v6': netmask_v6
}
interface_info['vz_host_if'] = ('veth%s.%s' %
(interface_info['id'],
interface_info['name']))
interfaces.append(interface_info)
return interfaces
def move(src, dest):
"""
utility method to wrap up moving a file or directory from one place to
another.
"""
try:
execute('mv', src, dest, run_as_root=True)
return True
except exception.InstanceUnacceptable as err:
LOG.error(_('Error moving file %s') % src)
LOG.error(err)
return False
def copy(src, dest):
"""
utility method wrap up copying files from one directory to another
"""
try:
execute('cp', src, dest, run_as_root=True)
return True
except exception.InstanceUnacceptable as err:
LOG.error(_('Error copying file %s') % src)
LOG.error(err)
return False
def tar(source, target_file, working_dir=None, skip_list=None, extra=None):
"""
wrapper for tar for making backup archives
"""
cmd = ['tar', '-cf', target_file]
if working_dir:
cmd += ['-C', working_dir]
if skip_list:
for exclude_path in skip_list:
cmd += ['--exclude', '%s/*' % exclude_path]
if extra:
cmd.extend(extra)
cmd.append(source)
try:
execute(*cmd, run_as_root=True)
return True
except exception.InstanceUnacceptable as err:
LOG.error(_('Error tarring: %s') % source)
LOG.error(err)
return False
def untar(target_file, destination, extra=None):
"""
wrapper for untarring files into a destination directory
"""
cmd = ['tar', '-xf', target_file, '-C', destination]
if extra:
cmd.extend(extra)
try:
execute(*cmd, run_as_root=True)
return True
except exception.InstanceUnacceptable as err:
LOG.error(_('Error untarring: %s') % target_file)
LOG.error(err)
return False

View File

@ -1,497 +0,0 @@
# 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.
"""
import glob
from nova import block_device
from nova import exception
from nova.openstack.common import log as logging
from nova.openstack.common import processutils
from ovznovadriver.localization import _
from ovznovadriver.openvz import file as ovzfile
from ovznovadriver.openvz import utils as ovz_utils
import os
from oslo.config import cfg
import re
import time
CONF = cfg.CONF
CONF.register_opts([
cfg.IntOpt('ovz_volume_settle_num_tries',
default=10,
help='Number of attempts before we determine that a device '
'has had time to settle and we are beyond a reasonable '
'wait time'),
cfg.BoolOpt('ovz_create_disk_partition',
default=False,
help='Create a partition on the volume (if it does not exist) '
'when it is attached to the container'),
])
LOG = logging.getLogger('ovznovadriver.openvz.volume')
class OVZVolume(object):
"""
This Class is a helper class to manage the mount and umount files
for a given container.
"""
def __init__(self, instance_id, mountpoint, dev):
"""
Setup critical information for attaching/detaching volumes from
instance_id.
:param instance_id:
:param mountpoint: connection_info['data']['mount_device']
:param dev:
"""
#TODO(imsplitbit): Need to make this happen while supporting block
# devices and externally mounted filesystems at the same time.
# Currently it does not.
self.instance_id = instance_id
self.mountpoint = block_device.prepend_dev(mountpoint)
self.device = dev
def attach(self):
"""
Public method for attaching volumes either by creating bind mounts or
attaching a raw device to the instance.
"""
self._attach_raw_devices()
def detach(self, container_is_running=True):
"""
Public method for detaching volumes from an instance.
"""
self._detach_raw_devices(container_is_running)
def device_name(self):
"""
Generates an device name. If no volume driver is used this will
assist in generating an error should things get this far.
"""
return
def _check_device_exists(self, device_path):
"""Check that the device path exists.
Verify that the device path has actually been created and can report
it's size, only then can it be available for formatting, retry
num_tries to account for the time lag.
"""
try:
ovz_utils.execute('blockdev', '--getsize64', device_path,
attempts=CONF.ovz_system_num_tries,
run_as_root=True)
except processutils.ProcessExecutionError:
raise exception.InvalidDevicePath(path=device_path)
def _find_device(self):
try:
return os.path.realpath(self.device_name())
except (OSError, AttributeError) as err:
raise exception.VolumeNotFound(
_('Device cannot be found %s') % err)
def _list_host_devices(self):
dev = self._find_device()
LOG.debug(_('Device name: %(dev)s') % locals())
devs = list()
listing = glob.glob('/dev/sd*')
for device in listing:
LOG.debug(_('Trying to match against %(device)s') % locals())
m = re.match('^(?P<device>%s)(?P<part>\\d*)$' % dev, device)
if m:
LOG.debug(_('Matched device %(device)s') % locals())
devs.append(m.string)
LOG.debug(_('Devices: %(devs)s') % locals())
return devs
def _get_major_minor(self, device):
try:
dev = os.stat(device)
maj_min = dict()
maj_min['major'] = os.major(dev.st_rdev)
maj_min['minor'] = os.minor(dev.st_rdev)
LOG.debug(
_('Major Minor numbers for %(dev)s: %(maj_min)s') % locals())
return maj_min
except OSError as err:
LOG.error(_('Device error in OpenVz volumes: %(err)s') % locals())
raise exception.VolumeNotFound(
_('Unable to find device: %(device)s') % locals())
def _let_disk_settle(self):
"""
Check to see if the device_name is available in /dev/disk/by-path. If
it isn't then we'll wait a bit and retry. Disks don't just instantly
show up in /dev so this gives a little bit of wait time for things
to show up.
:return:
"""
ovz_utils.execute(
'ls', self.device_name(), run_as_root=True,
attempts=CONF.ovz_volume_settle_num_tries, delay_on_retry=True)
def _list_partition_table(self):
"""
There is a bit of work done here to make a nice partition table
structure for later use. Currently the only thing we care about is
if there is already a partition table but we may need this info soon
so the legwork is done.
:return: dict
"""
# Now we should be able to move on to goodness.
dev = self._find_device()
LOG.debug(_('Device name: %(dev)s') % locals())
part_table = dict()
try:
out = ovz_utils.execute('sfdisk', '-d', dev, run_as_root=True)
for line in out.splitlines():
line = line.split(':')
dev = line[0].strip()
LOG.debug(_('Device from part table: %(dev)s') % locals())
if dev.startswith('/dev'):
line = line[1].split(',')
LOG.debug(_('Line from part table: %(line)s') % locals())
part_table[dev] = {}
part_table[dev]['start'] = line[0].split('=')[1]
part_table[dev]['size'] = line[1].split('=')[1]
part_table[dev]['id'] = line[2].split('=')[1]
LOG.debug(
_('Device info from part table: %s') % part_table[dev])
if len(line) > 3:
part_table[dev]['flags'] = line[3]
LOG.debug(_('Disk flags exist for %(dev)s') % locals())
LOG.debug(_('Partition Table: %(part_table)s') % locals())
return part_table
except exception.InstanceUnacceptable as err:
LOG.warn(_('No partition table on %(dev)s') % locals())
return part_table
def _partition_disk(self):
"""
Openvz requires that we pass in not only the root device but if the
user is to access partitions on that disk we need also to pass in
the major/minor numbers for each partition. Given that requirement
we're making the assumption for now that volumes are going to be
presented as one partition. This will partition the root device so
we can later get the information on both the major and minor numbers.
If the disk contains a valid partition table then we just return
:return:
"""
# We need potentially need to give the device time to settle
self._let_disk_settle()
dev = self._find_device()
part_table = self._list_partition_table()
LOG.debug(_('Partition table for %(dev)s: %(part_table)s') % locals())
# If the volume is partitioned we're assuming this is a reconnect of
# a volume and we won't partition anything.
if not part_table:
commands = ['o,w', 'n,p,1,,,t,1,83,w']
for command in commands:
command = command.replace(',', '\n')
ovz_utils.execute(
'fdisk', dev, process_input=command, run_as_root=True)
time.sleep(3)
def _get_vz_device_list(self):
"""
Grab existing device information from vz config and create new
device string for device connections.
:return:
"""
devices = list()
config = os.path.normpath(
'%s/%s.conf' % (CONF.ovz_config_dir, self.instance_id))
config = ovzfile.OVZFile(config, 644)
config.read()
for line in config.contents:
if '=' in line:
line = line.split('=')
if line[0] == 'DEVICES':
devices = line[1].replace('\"', '').strip().split()
return devices
def _ovz_block_dev_name_generator(self, major, minor, privs):
"""
OpenVz requires a specific format for granting or revoking block
device privileges. This tool will create those names.
:param major:
:param minor:
:param privs:
:return:
"""
return 'b:%(major)s:%(minor)s:%(privs)s' % locals()
def _attach_raw_devices(self):
"""
If there are no partitions on the disk already, partition it with 1
partition. Then use openvz tools and mknod to attach these devices
to the container.
:return:
"""
if CONF.ovz_create_disk_partition:
LOG.debug(_('Partitioning disk in _attach_raw_devices'))
self._partition_disk()
# storage for block device major/minor pairs for setting in
# the container config file.
bdevs = []
for dev in self._list_host_devices():
maj_min = self._get_major_minor(dev)
bdevs.append(
self._ovz_block_dev_name_generator(
maj_min['major'], maj_min['minor'], 'rw'))
m = re.match('^(?P<device>/[a-z]+/[a-z]+)(?P<part>\\d*)$', dev)
device_name = self.mountpoint
if m and m.group('part'):
device_name = '%s%s' % (self.mountpoint, m.group('part'))
# Remove the device if it happens to exist. This prevents stale
# devices with old major/minor numbers existing.
self._delete_container_device(device_name)
self._mknod(maj_min['major'], maj_min['minor'], device_name)
if bdevs:
self._add_block_devices(bdevs)
def _attach_raw_device(self, major, minor):
ovz_utils.execute(
'vzctl', 'set', self.instance_id,
'--devices', 'b:%s:%s:rw' % (major, minor),
run_as_root=True)
def _add_block_devices(self, new_devices):
"""
New devices should be a list that looks like this:
['b:8:32:rw', 'b:8:33:rw']
:param new_devices:
:return:
"""
devices = self._get_vz_device_list()
for dev in new_devices:
if dev not in devices:
devices.append(dev)
self._set_block_devices(devices)
def _del_block_devices(self, new_devices, container_is_running=True):
"""
New devices should be a list that looks like this:
['b:8:32:rw', 'b:8:33:rw']
:param new_devices:
:return:
"""
devices = self._get_vz_device_list()
for dev in new_devices:
if dev in devices:
devices.remove(dev)
self._set_block_devices(devices, container_is_running)
def _detach_remaining_block_devices(self):
"""
If the final ephemeral volume is detached from the container we issue
a privs :none for the remaining devices in the conf file for the
container before we remove those entries from the conf file. This is
necessary to ensure the removal of the devices from the in memory
device list for openvz.
:return:
"""
devices = self._get_vz_device_list()
cmd = ['vzctl', 'set', self.instance_id]
for dev in devices:
dev = dev.replace(':rw', ':none')
cmd.append('--devices')
cmd.append(dev)
ovz_utils.execute(*cmd, run_as_root=True)
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.
:param devices:
:return:
"""
cmd = ['vzctl', 'set', self.instance_id, '--save']
if devices:
for dev in devices:
cmd.append('--devices')
cmd.append(dev)
ovz_utils.execute(*cmd, run_as_root=True)
else:
# 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.
if container_is_running:
self._detach_remaining_block_devices()
self._remove_all_device_entries()
def _remove_all_device_entries(self):
"""
When there are no devices left to remove we need to remove the
DEVICES line from the CTID.conf file. This cannot be achieved by
a vzctl command so the file has to be manipulated and saved.
:return: None
"""
filename = '%s/%s.conf' % (CONF.ovz_config_dir, self.instance_id)
filename = os.path.normpath(filename)
ct_conf = ovzfile.OVZFile(filename, 644)
ct_conf.read()
with ct_conf:
for line in ct_conf.contents:
if "DEVICES" in line:
ct_conf.contents.remove(line)
ct_conf.write()
def _detach_raw_devices(self, container_is_running=True):
"""
Remove the associated devices from the container.
:return:
"""
LOG.debug(_('Detaching raw devices'))
# storage for block device major/minor pairs for setting in
# the container config file.
bdevs_conf = list()
for dev in self._list_host_devices():
maj_min = self._get_major_minor(dev)
bdevs_conf.append(
self._ovz_block_dev_name_generator(
maj_min['major'], maj_min['minor'], 'rw'))
# 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.
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
if m and m.group('part'):
device_name = '%s%s' % (self.mountpoint, m.group('part'))
self._delete_container_device(device_name)
self._delete_device(dev)
if bdevs_conf:
self._del_block_devices(bdevs_conf, container_is_running)
def _detach_raw_device(self, major, minor):
"""
Remove perms from the container for a device based on major and minor
numbers.
:param major:
:param minor:
:return:
"""
ovz_utils.execute(
'vzctl', 'set', self.instance_id, '--devices',
'b:%s:%s:none' % (major, minor), run_as_root=True)
def _mknod(self, major, minor, device_name, dev_type='b'):
"""
Because openvz doesn't support device aliases we manually create
a block device sharing the same major and minor numbers as the device
on the host. This device should be created in the openvz chrooted
/dev directory so as to allow it to be cleaned up upon deletion.
:param major:
:param minor:
:param dev_type:
:return: None
"""
good_types = ['b']
if dev_type not in good_types:
raise TypeError(
_('Cannot make device of type "%(dev_type)s"') % locals())
device_path = '%s/%s/%s' % (
CONF.ovz_ve_private_dir, self.instance_id, device_name)
device_path = os.path.normpath(device_path)
ovz_utils.execute(
'mknod', device_path, dev_type, major, minor, run_as_root=True)
def _delete_device(self, device):
"""
Utility method for deleting/removing device file
:param device
"""
ovz_utils.execute('rm', '-f', device, run_as_root=True)
def _delete_container_device(self, device):
"""
Utility method for removing a device from inside the container. This
is to happen before you create a device because the existing device
may or may not have the proper major/minor numbers. This is obviously
done on volume detach as well.
:param device:
:return:
"""
device = '%s/%s/%s' % (
CONF.ovz_ve_private_dir, self.instance_id, device)
device = os.path.normpath(device)
self._delete_device(device)

View File

@ -1,16 +0,0 @@
# 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

@ -1,194 +0,0 @@
# 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 lockutils
from nova.openstack.common import log as logging
from nova.openstack.common import processutils
from ovznovadriver.localization import _
from ovznovadriver.openvz import utils as ovz_utils
from ovznovadriver.openvz import volume as ovzvolume
from oslo.config import cfg
import time
CONF = cfg.CONF
CONF.register_opt(
cfg.IntOpt('ovz_iscsiadm_num_tries',
default=1,
help='Number of attempts to make an iscsi connection'))
CONF.register_opt(
cfg.IntOpt('ovz_max_symlink_wait_time',
default=5,
help='Max seconds to wait for symlink resolution.'))
LOG = logging.getLogger('ovznovadriver.openvz.volume_drivers.iscsi')
class OVZISCSIStorageDriver(ovzvolume.OVZVolume):
def __init__(self, instance_id, mountpoint, connection_data):
"""
Driver for openvz to connect iscsi volumes to instances
:param connection_data: Data from the connection_info given to the
ovz driver
:param instance_id: Instance ID for the volume to be attached to
:param mountpoint: place to mount the volume within the container
"""
self.iscsi_properties = connection_data['data']
super(OVZISCSIStorageDriver, self).__init__(
instance_id, mountpoint, self.device_name())
self.init_iscsi_device()
def device_name(self):
"""
Generates an device name based on the iSCSI parameters in the
block device mapping
"""
return "/dev/disk/by-path/ip-%s-iscsi-%s-lun-%s" % \
(self.iscsi_properties['target_portal'],
self.iscsi_properties['target_iqn'],
self.iscsi_properties['target_lun'])
def _find_device(self):
idle_time = .1
total_wait_time = 0
while total_wait_time < CONF.ovz_max_symlink_wait_time:
path = super(OVZISCSIStorageDriver, self)._find_device()
if 'disk/by-path' not in path:
return path
time.sleep(idle_time)
total_wait_time += idle_time
idle_time *= 2
LOG.debug('Having trouble resolving %s. Trying Again.' % path)
raise IOError("Could not resolve symlink: %s" % path)
def init_iscsi_device(self):
"""
Log into the device and setup files
"""
self.discover_volume()
def _run_iscsiadm(self, iscsi_command, raise_on_error=True):
out = ovz_utils.execute('iscsiadm', '-m', 'node', '-T',
self.iscsi_properties['target_iqn'],
'-p', self.iscsi_properties['target_portal'],
*iscsi_command, run_as_root=True,
attempts=CONF.ovz_iscsiadm_num_tries,
raise_on_error=raise_on_error)
LOG.debug("iscsiadm %s: stdout=%s" %
(iscsi_command, out))
return out
def _iscsiadm_update(self, property_key, property_value,
raise_on_error=True):
iscsi_command = ('--op', 'update', '-n', property_key,
'-v', property_value)
return self._run_iscsiadm(iscsi_command, raise_on_error=raise_on_error)
def get_iscsi_properties_for_volume(self):
if not self.iscsi_properties['target_discovered']:
self._run_iscsiadm(('--op', 'new'))
def session_currently_connected(self, **kwargs):
check_exit_code = kwargs.pop('check_exit_code', [0])
out = ovz_utils.execute(
'iscsiadm', '-m', 'session', run_as_root=True,
raise_on_error=False,
check_exit_code=check_exit_code)
if out:
for line in out.splitlines():
if self.iscsi_properties['target_iqn'] in line:
return True
else:
return False
def set_iscsi_auth(self):
if self.iscsi_properties.get('auth_method', None):
self._iscsiadm_update("node.session.auth.authmethod",
self.iscsi_properties['auth_method'])
self._iscsiadm_update("node.session.auth.username",
self.iscsi_properties['auth_username'])
self._iscsiadm_update("node.session.auth.password",
self.iscsi_properties['auth_password'])
@lockutils.synchronized('iscsiadm_lock', 'openvz-')
def discover_volume(self):
"""Discover volume on a remote host."""
self.get_iscsi_properties_for_volume()
self.set_iscsi_auth()
try:
if not self.session_currently_connected(check_exit_code=[0, 21]):
LOG.debug(_('iSCSI session for %s not connected, '
'connecting now') %
self.iscsi_properties['target_iqn'])
self._run_iscsiadm(("--login",))
LOG.debug(_('iSCSI session for %s connected') %
self.iscsi_properties['target_iqn'])
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, container_is_running=True):
"""Detach the volume from instance_name."""
super(OVZISCSIStorageDriver, self).detach(container_is_running)
self._detach_iscsi()
@lockutils.synchronized('iscsiadm_lock', 'openvz-')
def _detach_iscsi(self):
"""
Detach all iscsi sessions for this volume
:return:
"""
self._iscsiadm_update("node.startup", "manual")
self._run_iscsiadm(("--logout",), raise_on_error=False)
self._run_iscsiadm(('--op', 'delete'), raise_on_error=False)
def get_blockdev(self, device_name):
return ovz_utils.execute('blockdev',
'--getsize64',
device_name,
attempts=CONF.ovz_system_num_tries,
run_as_root=True)
@lockutils.synchronized('iscsiadm_lock', 'openvz-')
def rescan(self):
"""Rescan the client storage connection."""
self.get_iscsi_properties_for_volume()
try:
LOG.debug("ISCSI Properties: %s" % self.iscsi_properties)
self._run_iscsiadm(("--rescan",))
return ("/dev/disk/by-path/ip-%s-iscsi-%s-lun-%s" %
(self.iscsi_properties['target_portal'],
self.iscsi_properties['target_iqn'],
self.iscsi_properties['target_lun']))
except processutils.ProcessExecutionError as err:
LOG.error(err)
raise exception.ISCSITargetNotFoundForVolume(
_("Error rescanning iscsi device: %s") %
self.iscsi_properties['target_iqn'])

View File

@ -1,16 +0,0 @@
# 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

@ -1,826 +0,0 @@
# 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 ovznovadriver.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
"""
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

@ -1,107 +0,0 @@
# 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 ovznovadriver.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

@ -1,126 +0,0 @@
# 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 ovznovadriver.openvz import driver as openvz_conn
from ovznovadriver.openvz import file as ovzfile
from ovznovadriver.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

@ -1,220 +0,0 @@
# 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 import fake_network
from nova.tests.openvz import fakes
from ovznovadriver.openvz import driver as openvz_conn
from ovznovadriver.openvz import network as openvz_net
from ovznovadriver.openvz.network_drivers import network_bridge
from oslo.config import cfg
CONF = cfg.CONF
_fake_network_info = fake_network.fake_get_instance_nw_info
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='ovznovadriver/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()
network_info = _fake_network_info(self.stubs, 1)
for vif in network_info:
# should_create_vlan isn't included in nova's fake network info by
# default so we need to inject it to hit this code
vif['network']['meta']['should_create_vlan'] = True
driver.plug(fakes.INSTANCE, vif)

View File

@ -1,302 +0,0 @@
# 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 ovznovadriver.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

@ -1,33 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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 setuptools
setuptools.setup(
name='openvz-nova-driver',
version='0.1.0',
description='The OpenVZ Driver for Nova',
author='Rackspace',
packages=setuptools.find_packages(exclude=['debian', 'etc']),
include_package_data=True,
# i dont know where to put this package to make sure its built
classifiers=[
'Development Status :: 4 - Beta',
'License :: OSI Approved :: Apache Software License',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python :: 2.6',
'Environment :: No Input/Output (Daemon)',
]
)