Retire stackforge/openvz-nova-driver
This commit is contained in:
parent
f1cf199214
commit
bed4b3ff68
38
.gitignore
vendored
38
.gitignore
vendored
@ -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
|
|
@ -1,4 +0,0 @@
|
|||||||
[gerrit]
|
|
||||||
host=review.openstack.org
|
|
||||||
port=29418
|
|
||||||
project=stackforge/openvz-nova-driver.git
|
|
11
README.md
11
README.md
@ -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
7
README.rst
Normal 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
15
debian/changelog
vendored
@ -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
1
debian/compat
vendored
@ -1 +0,0 @@
|
|||||||
8
|
|
15
debian/control
vendored
15
debian/control
vendored
@ -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
0
debian/copyright
vendored
38
debian/rules
vendored
38
debian/rules
vendored
@ -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
|
|
1
debian/source/format
vendored
1
debian/source/format
vendored
@ -1 +0,0 @@
|
|||||||
1.0
|
|
@ -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
|
|
@ -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.
|
|
||||||
"""
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
#TODO(tim.simpson): Figure out some way to link this to Nova's localization
|
|
||||||
# function.
|
|
||||||
_ = str
|
|
@ -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
|
|
@ -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
@ -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)
|
|
@ -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.
|
|
@ -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)
|
|
@ -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]
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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.
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
||||||
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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}
|
|
@ -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}
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||||||
tc qdisc del dev ${host_iface} root
|
|
@ -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()
|
|
@ -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
|
|
@ -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)
|
|
@ -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.
|
|
@ -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'])
|
|
@ -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.
|
|
@ -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
@ -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)
|
|
@ -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()
|
|
@ -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)
|
|
@ -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)
|
|
33
setup.py
33
setup.py
@ -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)',
|
|
||||||
]
|
|
||||||
)
|
|
Loading…
x
Reference in New Issue
Block a user