Merge commit
This commit is contained in:
commit
63d9378bcc
1
.mailmap
1
.mailmap
@ -19,6 +19,7 @@
|
||||
<mordred@inaugust.com> <mordred@hudson>
|
||||
<paul@openstack.org> <pvoccio@castor.local>
|
||||
<paul@openstack.org> <paul.voccio@rackspace.com>
|
||||
<soren.hansen@rackspace.com> <soren@linux2go.dk>
|
||||
<todd@ansolabs.com> <todd@lapex>
|
||||
<todd@ansolabs.com> <todd@rubidine.com>
|
||||
<vishvananda@gmail.com> <vishvananda@yahoo.com>
|
||||
|
@ -13,7 +13,7 @@ include nova/cloudpipe/client.ovpn.template
|
||||
include nova/compute/fakevirtinstance.xml
|
||||
include nova/compute/interfaces.template
|
||||
include nova/virt/interfaces.template
|
||||
include nova/virt/libvirt.*.xml.template
|
||||
include nova/virt/libvirt*.xml.template
|
||||
include nova/tests/CA/
|
||||
include nova/tests/CA/cacert.pem
|
||||
include nova/tests/CA/private/
|
||||
|
@ -194,6 +194,7 @@ class HostInfo(object):
|
||||
|
||||
|
||||
class NovaAdminClient(object):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
clc_url=DEFAULT_CLC_URL,
|
||||
|
@ -168,6 +168,7 @@ class AdminController(object):
|
||||
|
||||
# FIXME(vish): these host commands don't work yet, perhaps some of the
|
||||
# required data can be retrieved from service objects?
|
||||
|
||||
def describe_hosts(self, _context, **_kwargs):
|
||||
"""Returns status info for all nodes. Includes:
|
||||
* Disk Space
|
||||
|
@ -751,7 +751,7 @@ class CloudController(object):
|
||||
kwargs['image_id'],
|
||||
min_count=int(kwargs.get('min_count', max_count)),
|
||||
max_count=max_count,
|
||||
kernel_id=kwargs.get('kernel_id'),
|
||||
kernel_id=kwargs.get('kernel_id', None),
|
||||
ramdisk_id=kwargs.get('ramdisk_id'),
|
||||
display_name=kwargs.get('display_name'),
|
||||
description=kwargs.get('display_description'),
|
||||
|
@ -170,9 +170,16 @@ class APIRouter(wsgi.Router):
|
||||
|
||||
def __init__(self):
|
||||
mapper = routes.Mapper()
|
||||
|
||||
server_members = {'action': 'POST'}
|
||||
if FLAGS.allow_admin_api:
|
||||
logging.debug("Including admin operations in API.")
|
||||
server_members['pause'] = 'POST'
|
||||
server_members['unpause'] = 'POST'
|
||||
|
||||
mapper.resource("server", "servers", controller=servers.Controller(),
|
||||
collection={'detail': 'GET'},
|
||||
member={'action': 'POST'})
|
||||
member=server_members)
|
||||
|
||||
mapper.resource("backup_schedule", "backup_schedules",
|
||||
controller=backup_schedules.Controller(),
|
||||
@ -186,10 +193,6 @@ class APIRouter(wsgi.Router):
|
||||
mapper.resource("sharedipgroup", "sharedipgroups",
|
||||
controller=sharedipgroups.Controller())
|
||||
|
||||
if FLAGS.allow_admin_api:
|
||||
logging.debug("Including admin operations in API.")
|
||||
# TODO: Place routes for admin operations here.
|
||||
|
||||
super(APIRouter, self).__init__(mapper)
|
||||
|
||||
|
||||
|
@ -24,6 +24,7 @@ import nova.image.service
|
||||
|
||||
|
||||
class Controller(wsgi.Controller):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
@ -15,6 +15,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from webob import exc
|
||||
|
||||
from nova import exception
|
||||
@ -27,6 +30,10 @@ from nova.compute import power_state
|
||||
import nova.api.openstack
|
||||
|
||||
|
||||
LOG = logging.getLogger('server')
|
||||
LOG.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
def _entity_list(entities):
|
||||
""" Coerces a list of servers into proper dictionary format """
|
||||
return dict(servers=entities)
|
||||
@ -166,3 +173,25 @@ class Controller(wsgi.Controller):
|
||||
except:
|
||||
return faults.Fault(exc.HTTPUnprocessableEntity())
|
||||
return exc.HTTPAccepted()
|
||||
|
||||
def pause(self, req, id):
|
||||
""" Permit Admins to Pause the server. """
|
||||
ctxt = req.environ['nova.context']
|
||||
try:
|
||||
self.compute_api.pause(ctxt, id)
|
||||
except:
|
||||
readable = traceback.format_exc()
|
||||
logging.error("Compute.api::pause %s", readable)
|
||||
return faults.Fault(exc.HTTPUnprocessableEntity())
|
||||
return exc.HTTPAccepted()
|
||||
|
||||
def unpause(self, req, id):
|
||||
""" Permit Admins to Unpause the server. """
|
||||
ctxt = req.environ['nova.context']
|
||||
try:
|
||||
self.compute_api.unpause(ctxt, id)
|
||||
except:
|
||||
readable = traceback.format_exc()
|
||||
logging.error("Compute.api::unpause %s", readable)
|
||||
return faults.Fault(exc.HTTPUnprocessableEntity())
|
||||
return exc.HTTPAccepted()
|
||||
|
@ -73,14 +73,19 @@ class ComputeAPI(base.Base):
|
||||
is_vpn = image_id == FLAGS.vpn_image_id
|
||||
if not is_vpn:
|
||||
image = self.image_service.show(context, image_id)
|
||||
|
||||
# If kernel_id/ramdisk_id isn't explicitly set in API call
|
||||
# we take the defaults from the image's metadata
|
||||
if kernel_id is None:
|
||||
kernel_id = image.get('kernelId', FLAGS.default_kernel)
|
||||
kernel_id = image.get('kernelId', None)
|
||||
if ramdisk_id is None:
|
||||
ramdisk_id = image.get('ramdiskId', FLAGS.default_ramdisk)
|
||||
ramdisk_id = image.get('ramdiskId', None)
|
||||
|
||||
# Make sure we have access to kernel and ramdisk
|
||||
self.image_service.show(context, kernel_id)
|
||||
self.image_service.show(context, ramdisk_id)
|
||||
if kernel_id:
|
||||
self.image_service.show(context, kernel_id)
|
||||
if ramdisk_id:
|
||||
self.image_service.show(context, ramdisk_id)
|
||||
|
||||
if security_group is None:
|
||||
security_group = ['default']
|
||||
@ -103,8 +108,8 @@ class ComputeAPI(base.Base):
|
||||
base_options = {
|
||||
'reservation_id': utils.generate_uid('r'),
|
||||
'image_id': image_id,
|
||||
'kernel_id': kernel_id,
|
||||
'ramdisk_id': ramdisk_id,
|
||||
'kernel_id': kernel_id or '',
|
||||
'ramdisk_id': ramdisk_id or '',
|
||||
'state_description': 'scheduling',
|
||||
'user_id': context.user_id,
|
||||
'project_id': context.project_id,
|
||||
@ -275,6 +280,24 @@ class ComputeAPI(base.Base):
|
||||
{"method": "reboot_instance",
|
||||
"args": {"instance_id": instance['id']}})
|
||||
|
||||
def pause(self, context, instance_id):
|
||||
"""Pause the given instance."""
|
||||
instance = self.db.instance_get_by_internal_id(context, instance_id)
|
||||
host = instance['host']
|
||||
rpc.cast(context,
|
||||
self.db.queue_get_for(context, FLAGS.compute_topic, host),
|
||||
{"method": "pause_instance",
|
||||
"args": {"instance_id": instance['id']}})
|
||||
|
||||
def unpause(self, context, instance_id):
|
||||
"""Unpause the given instance."""
|
||||
instance = self.db.instance_get_by_internal_id(context, instance_id)
|
||||
host = instance['host']
|
||||
rpc.cast(context,
|
||||
self.db.queue_get_for(context, FLAGS.compute_topic, host),
|
||||
{"method": "unpause_instance",
|
||||
"args": {"instance_id": instance['id']}})
|
||||
|
||||
def rescue(self, context, instance_id):
|
||||
"""Rescue the given instance."""
|
||||
instance = self.db.instance_get_by_internal_id(context, instance_id)
|
||||
|
@ -106,6 +106,13 @@ def partition(infile, outfile, local_bytes=0, resize=True,
|
||||
% (outfile, local_type, local_first, local_last))
|
||||
|
||||
|
||||
def extend(image, size, execute):
|
||||
file_size = os.path.getsize(image)
|
||||
if file_size >= size:
|
||||
return
|
||||
return execute('truncate -s size %s' % (image,))
|
||||
|
||||
|
||||
def inject_data(image, key=None, net=None, partition=None, execute=None):
|
||||
"""Injects a ssh key and optionally net data into a disk image.
|
||||
|
||||
@ -115,7 +122,7 @@ def inject_data(image, key=None, net=None, partition=None, execute=None):
|
||||
If partition is not specified it mounts the image as a single partition.
|
||||
|
||||
"""
|
||||
out, err = execute('sudo losetup -f --show %s' % image)
|
||||
out, err = execute('sudo losetup --find --show %s' % image)
|
||||
if err:
|
||||
raise exception.Error('Could not attach image to loopback: %s' % err)
|
||||
device = out.strip()
|
||||
@ -129,6 +136,15 @@ def inject_data(image, key=None, net=None, partition=None, execute=None):
|
||||
partition)
|
||||
else:
|
||||
mapped_device = device
|
||||
|
||||
# We can only loopback mount raw images. If the device isn't there,
|
||||
# it's normally because it's a .vmdk or a .vdi etc
|
||||
if not os.path.exists(mapped_device):
|
||||
raise exception.Error('Mapped device was not found (we can'
|
||||
' only inject raw disk images): %s' %
|
||||
mapped_device)
|
||||
|
||||
# Configure ext2fs so that it doesn't auto-check every N boots
|
||||
out, err = execute('sudo tune2fs -c 0 -i 0 %s' % mapped_device)
|
||||
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
@ -156,7 +172,7 @@ def inject_data(image, key=None, net=None, partition=None, execute=None):
|
||||
execute('sudo kpartx -d %s' % device)
|
||||
finally:
|
||||
# remove loopback
|
||||
execute('sudo losetup -d %s' % device)
|
||||
execute('sudo losetup --detach %s' % device)
|
||||
|
||||
|
||||
def _inject_key_into_fs(key, fs, execute=None):
|
||||
@ -165,7 +181,7 @@ def _inject_key_into_fs(key, fs, execute=None):
|
||||
key is an ssh key string.
|
||||
fs is the path to the base of the filesystem into which to inject the key.
|
||||
"""
|
||||
sshdir = os.path.join(os.path.join(fs, 'root'), '.ssh')
|
||||
sshdir = os.path.join(fs, 'root', '.ssh')
|
||||
execute('sudo mkdir -p %s' % sshdir) # existing dir doesn't matter
|
||||
execute('sudo chown root %s' % sshdir)
|
||||
execute('sudo chmod 700 %s' % sshdir)
|
||||
|
@ -186,6 +186,47 @@ class ComputeManager(manager.Manager):
|
||||
self.driver.unrescue(instance_ref)
|
||||
self._update_state(context, instance_id)
|
||||
|
||||
@staticmethod
|
||||
def _update_state_callback(self, context, instance_id, result):
|
||||
"""Update instance state when async task completes."""
|
||||
self._update_state(context, instance_id)
|
||||
|
||||
@exception.wrap_exception
|
||||
def pause_instance(self, context, instance_id):
|
||||
"""Pause an instance on this server."""
|
||||
context = context.elevated()
|
||||
instance_ref = self.db.instance_get(context, instance_id)
|
||||
|
||||
logging.debug('instance %s: pausing',
|
||||
instance_ref['internal_id'])
|
||||
self.db.instance_set_state(context,
|
||||
instance_id,
|
||||
power_state.NOSTATE,
|
||||
'pausing')
|
||||
self.driver.pause(instance_ref,
|
||||
lambda result: self._update_state_callback(self,
|
||||
context,
|
||||
instance_id,
|
||||
result))
|
||||
|
||||
@exception.wrap_exception
|
||||
def unpause_instance(self, context, instance_id):
|
||||
"""Unpause a paused instance on this server."""
|
||||
context = context.elevated()
|
||||
instance_ref = self.db.instance_get(context, instance_id)
|
||||
|
||||
logging.debug('instance %s: unpausing',
|
||||
instance_ref['internal_id'])
|
||||
self.db.instance_set_state(context,
|
||||
instance_id,
|
||||
power_state.NOSTATE,
|
||||
'unpausing')
|
||||
self.driver.unpause(instance_ref,
|
||||
lambda result: self._update_state_callback(self,
|
||||
context,
|
||||
instance_id,
|
||||
result))
|
||||
|
||||
@exception.wrap_exception
|
||||
def get_console_output(self, context, instance_id):
|
||||
"""Send the console output for an instance."""
|
||||
|
@ -528,6 +528,8 @@ def fixed_ip_update(context, address, values):
|
||||
#TODO(gundlach): instance_create and volume_create are nearly identical
|
||||
#and should be refactored. I expect there are other copy-and-paste
|
||||
#functions between the two of them as well.
|
||||
|
||||
|
||||
@require_context
|
||||
def instance_create(context, values):
|
||||
"""Create a new Instance record in the database.
|
||||
@ -913,6 +915,8 @@ def network_get(context, network_id, session=None):
|
||||
# NOTE(vish): pylint complains because of the long method name, but
|
||||
# it fits with the names of the rest of the methods
|
||||
# pylint: disable-msg=C0103
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def network_get_associated_fixed_ips(context, network_id):
|
||||
session = get_session()
|
||||
|
@ -27,6 +27,7 @@ import traceback
|
||||
|
||||
|
||||
class ProcessExecutionError(IOError):
|
||||
|
||||
def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None,
|
||||
description=None):
|
||||
if description is None:
|
||||
@ -39,11 +40,13 @@ class ProcessExecutionError(IOError):
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
|
||||
def __init__(self, message=None):
|
||||
super(Error, self).__init__(message)
|
||||
|
||||
|
||||
class ApiError(Error):
|
||||
|
||||
def __init__(self, message='Unknown', code='Unknown'):
|
||||
self.message = message
|
||||
self.code = code
|
||||
|
@ -235,12 +235,11 @@ DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud',
|
||||
|
||||
DEFINE_string('default_image', 'ami-11111',
|
||||
'default image to use, testing only')
|
||||
DEFINE_string('default_kernel', 'aki-11111',
|
||||
'default kernel to use, testing only')
|
||||
DEFINE_string('default_ramdisk', 'ari-11111',
|
||||
'default ramdisk to use, testing only')
|
||||
DEFINE_string('default_instance_type', 'm1.small',
|
||||
'default instance type to use, testing only')
|
||||
DEFINE_string('null_kernel', 'nokernel',
|
||||
'kernel image that indicates not to use a kernel,'
|
||||
' but to use a raw disk image instead')
|
||||
|
||||
DEFINE_string('vpn_image_id', 'ami-CLOUDPIPE', 'AMI for cloudpipe vpn server')
|
||||
DEFINE_string('vpn_key_suffix',
|
||||
|
@ -56,11 +56,16 @@ def instance_address(context, instance_id):
|
||||
|
||||
|
||||
def stub_instance(id, user_id=1):
|
||||
return Instance(id=id + 123456, state=0, image_id=10, user_id=user_id,
|
||||
return Instance(id=int(id) + 123456, state=0, image_id=10, user_id=user_id,
|
||||
display_name='server%s' % id, internal_id=id)
|
||||
|
||||
|
||||
def fake_compute_api(cls, req, id):
|
||||
return True
|
||||
|
||||
|
||||
class ServersTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.auth_data = {}
|
||||
@ -82,9 +87,15 @@ class ServersTest(unittest.TestCase):
|
||||
instance_address)
|
||||
self.stubs.Set(nova.db.api, 'instance_get_floating_address',
|
||||
instance_address)
|
||||
self.stubs.Set(nova.compute.api.ComputeAPI, 'pause',
|
||||
fake_compute_api)
|
||||
self.stubs.Set(nova.compute.api.ComputeAPI, 'unpause',
|
||||
fake_compute_api)
|
||||
self.allow_admin = FLAGS.allow_admin_api
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
FLAGS.allow_admin_api = self.allow_admin
|
||||
|
||||
def test_get_server_by_id(self):
|
||||
req = webob.Request.blank('/v1.0/servers/1')
|
||||
@ -211,6 +222,30 @@ class ServersTest(unittest.TestCase):
|
||||
self.assertEqual(s['imageId'], 10)
|
||||
i += 1
|
||||
|
||||
def test_server_pause(self):
|
||||
FLAGS.allow_admin_api = True
|
||||
body = dict(server=dict(
|
||||
name='server_test', imageId=2, flavorId=2, metadata={},
|
||||
personality={}))
|
||||
req = webob.Request.blank('/v1.0/servers/1/pause')
|
||||
req.method = 'POST'
|
||||
req.content_type = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(nova.api.API('os'))
|
||||
self.assertEqual(res.status_int, 202)
|
||||
|
||||
def test_server_unpause(self):
|
||||
FLAGS.allow_admin_api = True
|
||||
body = dict(server=dict(
|
||||
name='server_test', imageId=2, flavorId=2, metadata={},
|
||||
personality={}))
|
||||
req = webob.Request.blank('/v1.0/servers/1/unpause')
|
||||
req.method = 'POST'
|
||||
req.content_type = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(nova.api.API('os'))
|
||||
self.assertEqual(res.status_int, 202)
|
||||
|
||||
def test_server_reboot(self):
|
||||
body = dict(server=dict(
|
||||
name='server_test', imageId=2, flavorId=2, metadata={},
|
||||
|
@ -127,6 +127,14 @@ class ComputeTestCase(test.TestCase):
|
||||
self.assert_(instance_ref['launched_at'] < terminate)
|
||||
self.assert_(instance_ref['deleted_at'] > terminate)
|
||||
|
||||
def test_pause(self):
|
||||
"""Ensure instance can be paused"""
|
||||
instance_id = self._create_instance()
|
||||
self.compute.run_instance(self.context, instance_id)
|
||||
self.compute.pause_instance(self.context, instance_id)
|
||||
self.compute.unpause_instance(self.context, instance_id)
|
||||
self.compute.terminate_instance(self.context, instance_id)
|
||||
|
||||
def test_reboot(self):
|
||||
"""Ensure instance can be rebooted"""
|
||||
instance_id = self._create_instance()
|
||||
|
@ -40,19 +40,51 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
self.network = utils.import_object(FLAGS.network_manager)
|
||||
FLAGS.instances_path = ''
|
||||
|
||||
def test_get_uri_and_template(self):
|
||||
ip = '10.11.12.13'
|
||||
test_ip = '10.11.12.13'
|
||||
test_instance = {'memory_kb': '1024000',
|
||||
'basepath': '/some/path',
|
||||
'bridge_name': 'br100',
|
||||
'mac_address': '02:12:34:46:56:67',
|
||||
'vcpus': 2,
|
||||
'project_id': 'fake',
|
||||
'bridge': 'br101',
|
||||
'instance_type': 'm1.small'}
|
||||
|
||||
instance = {'internal_id': 1,
|
||||
'memory_kb': '1024000',
|
||||
'basepath': '/some/path',
|
||||
'bridge_name': 'br100',
|
||||
'mac_address': '02:12:34:46:56:67',
|
||||
'vcpus': 2,
|
||||
'project_id': 'fake',
|
||||
'bridge': 'br101',
|
||||
'instance_type': 'm1.small'}
|
||||
def test_xml_and_uri_no_ramdisk_no_kernel(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
self.do_test_xml_and_uri(instance_data,
|
||||
expect_kernel=False, expect_ramdisk=False)
|
||||
|
||||
def test_xml_and_uri_no_ramdisk(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
instance_data['kernel_id'] = 'aki-deadbeef'
|
||||
self.do_test_xml_and_uri(instance_data,
|
||||
expect_kernel=True, expect_ramdisk=False)
|
||||
|
||||
def test_xml_and_uri_no_kernel(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
instance_data['ramdisk_id'] = 'ari-deadbeef'
|
||||
self.do_test_xml_and_uri(instance_data,
|
||||
expect_kernel=False, expect_ramdisk=False)
|
||||
|
||||
def test_xml_and_uri(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
instance_data['ramdisk_id'] = 'ari-deadbeef'
|
||||
instance_data['kernel_id'] = 'aki-deadbeef'
|
||||
self.do_test_xml_and_uri(instance_data,
|
||||
expect_kernel=True, expect_ramdisk=True)
|
||||
|
||||
def test_xml_and_uri_rescue(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
instance_data['ramdisk_id'] = 'ari-deadbeef'
|
||||
instance_data['kernel_id'] = 'aki-deadbeef'
|
||||
self.do_test_xml_and_uri(instance_data,
|
||||
expect_kernel=True, expect_ramdisk=True,
|
||||
rescue=True)
|
||||
|
||||
def do_test_xml_and_uri(self, instance,
|
||||
expect_ramdisk, expect_kernel,
|
||||
rescue=False):
|
||||
user_context = context.RequestContext(project=self.project,
|
||||
user=self.user)
|
||||
instance_ref = db.instance_create(user_context, instance)
|
||||
@ -60,13 +92,14 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
self.network.set_network_host(context.get_admin_context(),
|
||||
network_ref['id'])
|
||||
|
||||
fixed_ip = {'address': ip,
|
||||
fixed_ip = {'address': self.test_ip,
|
||||
'network_id': network_ref['id']}
|
||||
|
||||
ctxt = context.get_admin_context()
|
||||
fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip)
|
||||
db.fixed_ip_update(ctxt, ip, {'allocated': True,
|
||||
'instance_id': instance_ref['id']})
|
||||
db.fixed_ip_update(ctxt, self.test_ip,
|
||||
{'allocated': True,
|
||||
'instance_id': instance_ref['id']})
|
||||
|
||||
type_uri_map = {'qemu': ('qemu:///system',
|
||||
[(lambda t: t.find('.').get('type'), 'qemu'),
|
||||
@ -78,23 +111,71 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
(lambda t: t.find('./devices/emulator'), None)]),
|
||||
'uml': ('uml:///system',
|
||||
[(lambda t: t.find('.').get('type'), 'uml'),
|
||||
(lambda t: t.find('./os/type').text, 'uml')])}
|
||||
(lambda t: t.find('./os/type').text, 'uml')]),
|
||||
'xen': ('xen:///',
|
||||
[(lambda t: t.find('.').get('type'), 'xen'),
|
||||
(lambda t: t.find('./os/type').text, 'linux')]),
|
||||
}
|
||||
|
||||
for hypervisor_type in ['qemu', 'kvm', 'xen']:
|
||||
check_list = type_uri_map[hypervisor_type][1]
|
||||
|
||||
if rescue:
|
||||
check = (lambda t: t.find('./os/kernel').text.split('/')[1],
|
||||
'rescue-kernel')
|
||||
check_list.append(check)
|
||||
check = (lambda t: t.find('./os/initrd').text.split('/')[1],
|
||||
'rescue-ramdisk')
|
||||
check_list.append(check)
|
||||
else:
|
||||
if expect_kernel:
|
||||
check = (lambda t: t.find('./os/kernel').text.split('/'
|
||||
)[1], 'kernel')
|
||||
else:
|
||||
check = (lambda t: t.find('./os/kernel'), None)
|
||||
check_list.append(check)
|
||||
|
||||
if expect_ramdisk:
|
||||
check = (lambda t: t.find('./os/initrd').text.split('/'
|
||||
)[1], 'ramdisk')
|
||||
else:
|
||||
check = (lambda t: t.find('./os/initrd'), None)
|
||||
check_list.append(check)
|
||||
|
||||
common_checks = [
|
||||
(lambda t: t.find('.').tag, 'domain'),
|
||||
(lambda t: t.find('./devices/interface/filterref/parameter').\
|
||||
get('name'), 'IP'),
|
||||
(lambda t: t.find('./devices/interface/filterref/parameter').\
|
||||
get('value'), '10.11.12.13')]
|
||||
(lambda t: t.find('./devices/interface/filterref/parameter'
|
||||
).get('name'), 'IP'),
|
||||
(lambda t: t.find('./devices/interface/filterref/parameter'
|
||||
).get('value'), '10.11.12.13'),
|
||||
(lambda t: t.findall('./devices/interface/filterref/parameter'
|
||||
)[1].get('name'), 'DHCPSERVER'),
|
||||
(lambda t: t.findall('./devices/interface/filterref/parameter'
|
||||
)[1].get('value'), '10.0.0.1'),
|
||||
(lambda t: t.find('./devices/serial/source').get('path'
|
||||
).split('/')[1], 'console.log'),
|
||||
(lambda t: t.find('./memory').text, '2097152')]
|
||||
|
||||
if rescue:
|
||||
common_checks += [(lambda t: t.findall('./devices/disk/source'
|
||||
)[0].get('file').split('/')[1],
|
||||
'rescue-disk'),
|
||||
(lambda t: t.findall('./devices/disk/source'
|
||||
)[1].get('file').split('/')[1],
|
||||
'disk')]
|
||||
else:
|
||||
common_checks += [(lambda t: t.findall('./devices/disk/source'
|
||||
)[0].get('file').split('/')[1],
|
||||
'disk')]
|
||||
|
||||
for (libvirt_type, (expected_uri, checks)) in type_uri_map.iteritems():
|
||||
FLAGS.libvirt_type = libvirt_type
|
||||
conn = libvirt_conn.LibvirtConnection(True)
|
||||
|
||||
uri, _template, _rescue = conn.get_uri_and_templates()
|
||||
uri = conn.get_uri()
|
||||
self.assertEquals(uri, expected_uri)
|
||||
|
||||
xml = conn.to_xml(instance_ref)
|
||||
xml = conn.to_xml(instance_ref, rescue)
|
||||
tree = xml_to_tree(xml)
|
||||
for i, (check, expected_result) in enumerate(checks):
|
||||
self.assertEqual(check(tree),
|
||||
@ -106,6 +187,9 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
expected_result,
|
||||
'%s failed common check %d' % (xml, i))
|
||||
|
||||
# This test is supposed to make sure we don't override a specifically
|
||||
# set uri
|
||||
#
|
||||
# Deliberately not just assigning this string to FLAGS.libvirt_uri and
|
||||
# checking against that later on. This way we make sure the
|
||||
# implementation doesn't fiddle around with the FLAGS.
|
||||
@ -114,7 +198,7 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
for (libvirt_type, (expected_uri, checks)) in type_uri_map.iteritems():
|
||||
FLAGS.libvirt_type = libvirt_type
|
||||
conn = libvirt_conn.LibvirtConnection(True)
|
||||
uri, _template, _rescue = conn.get_uri_and_templates()
|
||||
uri = conn.get_uri()
|
||||
self.assertEquals(uri, testuri)
|
||||
|
||||
def tearDown(self):
|
||||
|
@ -43,7 +43,7 @@ else:
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('logdir', None, 'directory to keep log files in '
|
||||
flags.DEFINE_string('logdir', None, 'directory to keep log files in '
|
||||
'(will be prepended to $logfile)')
|
||||
|
||||
|
||||
|
@ -130,6 +130,18 @@ class FakeConnection(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
def pause(self, instance, callback):
|
||||
"""
|
||||
Pause the specified instance.
|
||||
"""
|
||||
pass
|
||||
|
||||
def unpause(self, instance, callback):
|
||||
"""
|
||||
Unpause the specified instance.
|
||||
"""
|
||||
pass
|
||||
|
||||
def destroy(self, instance):
|
||||
"""
|
||||
Destroy (shutdown and delete) the specified instance.
|
||||
@ -243,5 +255,6 @@ class FakeConnection(object):
|
||||
|
||||
|
||||
class FakeInstance(object):
|
||||
|
||||
def __init__(self):
|
||||
self._state = power_state.NOSTATE
|
||||
|
@ -1,33 +0,0 @@
|
||||
<domain type='%(type)s'>
|
||||
<name>%(name)s</name>
|
||||
<os>
|
||||
<type>hvm</type>
|
||||
<kernel>%(basepath)s/kernel</kernel>
|
||||
<initrd>%(basepath)s/ramdisk</initrd>
|
||||
<cmdline>root=/dev/vda1 console=ttyS0</cmdline>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
</features>
|
||||
<memory>%(memory_kb)s</memory>
|
||||
<vcpu>%(vcpus)s</vcpu>
|
||||
<devices>
|
||||
<disk type='file'>
|
||||
<source file='%(basepath)s/disk'/>
|
||||
<target dev='vda' bus='virtio'/>
|
||||
</disk>
|
||||
<interface type='bridge'>
|
||||
<source bridge='%(bridge_name)s'/>
|
||||
<mac address='%(mac_address)s'/>
|
||||
<!-- <model type='virtio'/> CANT RUN virtio network right now -->
|
||||
<filterref filter="nova-instance-%(name)s">
|
||||
<parameter name="IP" value="%(ip_address)s" />
|
||||
<parameter name="DHCPSERVER" value="%(dhcp_server)s" />
|
||||
</filterref>
|
||||
</interface>
|
||||
<serial type="file">
|
||||
<source path='%(basepath)s/console.log'/>
|
||||
<target port='1'/>
|
||||
</serial>
|
||||
</devices>
|
||||
</domain>
|
@ -1,37 +0,0 @@
|
||||
<domain type='%(type)s'>
|
||||
<name>%(name)s</name>
|
||||
<os>
|
||||
<type>hvm</type>
|
||||
<kernel>%(basepath)s/rescue-kernel</kernel>
|
||||
<initrd>%(basepath)s/rescue-ramdisk</initrd>
|
||||
<cmdline>root=/dev/vda1 console=ttyS0</cmdline>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
</features>
|
||||
<memory>%(memory_kb)s</memory>
|
||||
<vcpu>%(vcpus)s</vcpu>
|
||||
<devices>
|
||||
<disk type='file'>
|
||||
<source file='%(basepath)s/rescue-disk'/>
|
||||
<target dev='vda' bus='virtio'/>
|
||||
</disk>
|
||||
<disk type='file'>
|
||||
<source file='%(basepath)s/disk'/>
|
||||
<target dev='vdb' bus='virtio'/>
|
||||
</disk>
|
||||
<interface type='bridge'>
|
||||
<source bridge='%(bridge_name)s'/>
|
||||
<mac address='%(mac_address)s'/>
|
||||
<!-- <model type='virtio'/> CANT RUN virtio network right now -->
|
||||
<filterref filter="nova-instance-%(name)s">
|
||||
<parameter name="IP" value="%(ip_address)s" />
|
||||
<parameter name="DHCPSERVER" value="%(dhcp_server)s" />
|
||||
</filterref>
|
||||
</interface>
|
||||
<serial type="file">
|
||||
<source path='%(basepath)s/console.log'/>
|
||||
<target port='1'/>
|
||||
</serial>
|
||||
</devices>
|
||||
</domain>
|
@ -1,26 +0,0 @@
|
||||
<domain type='%(type)s'>
|
||||
<name>%(name)s</name>
|
||||
<memory>%(memory_kb)s</memory>
|
||||
<os>
|
||||
<type>%(type)s</type>
|
||||
<kernel>/usr/bin/linux</kernel>
|
||||
<root>/dev/ubda1</root>
|
||||
</os>
|
||||
<devices>
|
||||
<disk type='file'>
|
||||
<source file='%(basepath)s/rescue-disk'/>
|
||||
<target dev='ubd0' bus='uml'/>
|
||||
</disk>
|
||||
<disk type='file'>
|
||||
<source file='%(basepath)s/disk'/>
|
||||
<target dev='ubd1' bus='uml'/>
|
||||
</disk>
|
||||
<interface type='bridge'>
|
||||
<source bridge='%(bridge_name)s'/>
|
||||
<mac address='%(mac_address)s'/>
|
||||
</interface>
|
||||
<console type="file">
|
||||
<source path='%(basepath)s/console.log'/>
|
||||
</console>
|
||||
</devices>
|
||||
</domain>
|
@ -1,34 +0,0 @@
|
||||
<domain type='%(type)s'>
|
||||
<name>%(name)s</name>
|
||||
<os>
|
||||
<type>linux</type>
|
||||
<kernel>%(basepath)s/kernel</kernel>
|
||||
<initrd>%(basepath)s/ramdisk</initrd>
|
||||
<root>/dev/xvda1</root>
|
||||
<cmdline>ro</cmdline>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
</features>
|
||||
<memory>%(memory_kb)s</memory>
|
||||
<vcpu>%(vcpus)s</vcpu>
|
||||
<devices>
|
||||
<disk type='file'>
|
||||
<source file='%(basepath)s/rescue-disk'/>
|
||||
<target dev='sda' />
|
||||
</disk>
|
||||
<disk type='file'>
|
||||
<source file='%(basepath)s/disk'/>
|
||||
<target dev='sdb' />
|
||||
</disk>
|
||||
<interface type='bridge'>
|
||||
<source bridge='%(bridge_name)s'/>
|
||||
<mac address='%(mac_address)s'/>
|
||||
</interface>
|
||||
<console type="file">
|
||||
<source path='%(basepath)s/console.log'/>
|
||||
<target port='1'/>
|
||||
</console>
|
||||
</devices>
|
||||
</domain>
|
||||
|
@ -1,26 +0,0 @@
|
||||
<domain type='%(type)s'>
|
||||
<name>%(name)s</name>
|
||||
<memory>%(memory_kb)s</memory>
|
||||
<os>
|
||||
<type>%(type)s</type>
|
||||
<kernel>/usr/bin/linux</kernel>
|
||||
<root>/dev/ubda1</root>
|
||||
</os>
|
||||
<devices>
|
||||
<disk type='file'>
|
||||
<source file='%(basepath)s/disk'/>
|
||||
<target dev='ubd0' bus='uml'/>
|
||||
</disk>
|
||||
<interface type='bridge'>
|
||||
<source bridge='%(bridge_name)s'/>
|
||||
<mac address='%(mac_address)s'/>
|
||||
<filterref filter="nova-instance-%(name)s">
|
||||
<parameter name="IP" value="%(ip_address)s" />
|
||||
<parameter name="DHCPSERVER" value="%(dhcp_server)s" />
|
||||
</filterref>
|
||||
</interface>
|
||||
<console type="file">
|
||||
<source path='%(basepath)s/console.log'/>
|
||||
</console>
|
||||
</devices>
|
||||
</domain>
|
@ -1,30 +0,0 @@
|
||||
<domain type='%(type)s'>
|
||||
<name>%(name)s</name>
|
||||
<os>
|
||||
<type>linux</type>
|
||||
<kernel>%(basepath)s/kernel</kernel>
|
||||
<initrd>%(basepath)s/ramdisk</initrd>
|
||||
<root>/dev/xvda1</root>
|
||||
<cmdline>ro</cmdline>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
</features>
|
||||
<memory>%(memory_kb)s</memory>
|
||||
<vcpu>%(vcpus)s</vcpu>
|
||||
<devices>
|
||||
<disk type='file'>
|
||||
<source file='%(basepath)s/disk'/>
|
||||
<target dev='sda' />
|
||||
</disk>
|
||||
<interface type='bridge'>
|
||||
<source bridge='%(bridge_name)s'/>
|
||||
<mac address='%(mac_address)s'/>
|
||||
</interface>
|
||||
<console type="file">
|
||||
<source path='%(basepath)s/console.log'/>
|
||||
<target port='1'/>
|
||||
</console>
|
||||
</devices>
|
||||
</domain>
|
||||
|
76
nova/virt/libvirt.xml.template
Normal file
76
nova/virt/libvirt.xml.template
Normal file
@ -0,0 +1,76 @@
|
||||
<domain type='${type}'>
|
||||
<name>${name}</name>
|
||||
<memory>${memory_kb}</memory>
|
||||
<os>
|
||||
#if $type == 'uml'
|
||||
#set $disk_prefix = 'ubd'
|
||||
#set $disk_bus = 'uml'
|
||||
<type>uml</type>
|
||||
<kernel>/usr/bin/linux</kernel>
|
||||
<root>/dev/ubda1</root>
|
||||
#else
|
||||
#if $type == 'xen'
|
||||
#set $disk_prefix = 'sd'
|
||||
#set $disk_bus = 'scsi'
|
||||
<type>linux</type>
|
||||
<root>/dev/xvda1</root>
|
||||
#else
|
||||
#set $disk_prefix = 'vd'
|
||||
#set $disk_bus = 'virtio'
|
||||
<type>hvm</type>
|
||||
#end if
|
||||
#if $getVar('rescue', False)
|
||||
<kernel>${basepath}/rescue-kernel</kernel>
|
||||
<initrd>${basepath}/rescue-ramdisk</initrd>
|
||||
#else
|
||||
#if $getVar('kernel', None)
|
||||
<kernel>${kernel}</kernel>
|
||||
#if $type == 'xen'
|
||||
<cmdline>ro</cmdline>
|
||||
#else
|
||||
<cmdline>root=/dev/vda1 console=ttyS0</cmdline>
|
||||
#end if
|
||||
#if $getVar('ramdisk', None)
|
||||
<initrd>${ramdisk}</initrd>
|
||||
#end if
|
||||
#else
|
||||
<boot dev="hd" />
|
||||
#end if
|
||||
#end if
|
||||
#end if
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
</features>
|
||||
<vcpu>${vcpus}</vcpu>
|
||||
<devices>
|
||||
#if $getVar('rescue', False)
|
||||
<disk type='file'>
|
||||
<source file='${basepath}/rescue-disk'/>
|
||||
<target dev='${disk_prefix}a' bus='${disk_bus}'/>
|
||||
</disk>
|
||||
<disk type='file'>
|
||||
<source file='${basepath}/disk'/>
|
||||
<target dev='${disk_prefix}b' bus='${disk_bus}'/>
|
||||
</disk>
|
||||
#else
|
||||
<disk type='file'>
|
||||
<source file='${basepath}/disk'/>
|
||||
<target dev='${disk_prefix}a' bus='${disk_bus}'/>
|
||||
</disk>
|
||||
#end if
|
||||
<interface type='bridge'>
|
||||
<source bridge='${bridge_name}'/>
|
||||
<mac address='${mac_address}'/>
|
||||
<!-- <model type='virtio'/> CANT RUN virtio network right now -->
|
||||
<filterref filter="nova-instance-${name}">
|
||||
<parameter name="IP" value="${ip_address}" />
|
||||
<parameter name="DHCPSERVER" value="${dhcp_server}" />
|
||||
</filterref>
|
||||
</interface>
|
||||
<serial type="file">
|
||||
<source path='${basepath}/console.log'/>
|
||||
<target port='1'/>
|
||||
</serial>
|
||||
</devices>
|
||||
</domain>
|
@ -27,12 +27,7 @@ Supports KVM, QEMU, UML, and XEN.
|
||||
:libvirt_type: Libvirt domain type. Can be kvm, qemu, uml, xen
|
||||
(default: kvm).
|
||||
:libvirt_uri: Override for the default libvirt URI (depends on libvirt_type).
|
||||
:libvirt_xml_template: Libvirt XML Template (QEmu/KVM).
|
||||
:libvirt_xen_xml_template: Libvirt XML Template (Xen).
|
||||
:libvirt_uml_xml_template: Libvirt XML Template (User Mode Linux).
|
||||
:libvirt_rescue_xml_template: XML template for rescue mode (KVM & QEMU).
|
||||
:libvirt_rescue_xen_xml_template: XML templage for rescue mode (XEN).
|
||||
:libvirt_rescue_uml_xml_template: XML template for rescue mode (UML).
|
||||
:libvirt_xml_template: Libvirt XML Template.
|
||||
:rescue_image_id: Rescue ami image (default: ami-rescue).
|
||||
:rescue_kernel_id: Rescue aki image (default: aki-rescue).
|
||||
:rescue_ramdisk_id: Rescue ari image (default: ari-rescue).
|
||||
@ -62,36 +57,20 @@ from nova.compute import instance_types
|
||||
from nova.compute import power_state
|
||||
from nova.virt import images
|
||||
|
||||
from Cheetah.Template import Template
|
||||
|
||||
libvirt = None
|
||||
libxml2 = None
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('libvirt_rescue_xml_template',
|
||||
utils.abspath('virt/libvirt.rescue.qemu.xml.template'),
|
||||
'Libvirt RESCUE XML Template for QEmu/KVM')
|
||||
flags.DEFINE_string('libvirt_rescue_xen_xml_template',
|
||||
utils.abspath('virt/libvirt.rescue.xen.xml.template'),
|
||||
'Libvirt RESCUE XML Template for xen')
|
||||
flags.DEFINE_string('libvirt_rescue_uml_xml_template',
|
||||
utils.abspath('virt/libvirt.rescue.uml.xml.template'),
|
||||
'Libvirt RESCUE XML Template for user-mode-linux')
|
||||
# TODO(vish): These flags should probably go into a shared location
|
||||
flags.DEFINE_string('rescue_image_id', 'ami-rescue', 'Rescue ami image')
|
||||
flags.DEFINE_string('rescue_kernel_id', 'aki-rescue', 'Rescue aki image')
|
||||
flags.DEFINE_string('rescue_ramdisk_id', 'ari-rescue', 'Rescue ari image')
|
||||
flags.DEFINE_string('libvirt_xml_template',
|
||||
utils.abspath('virt/libvirt.qemu.xml.template'),
|
||||
'Libvirt XML Template for QEmu/KVM')
|
||||
flags.DEFINE_string('libvirt_xen_xml_template',
|
||||
utils.abspath('virt/libvirt.xen.xml.template'),
|
||||
'Libvirt XML Template for Xen')
|
||||
flags.DEFINE_string('libvirt_uml_xml_template',
|
||||
utils.abspath('virt/libvirt.uml.xml.template'),
|
||||
'Libvirt XML Template for user-mode-linux')
|
||||
flags.DEFINE_string('injected_network_template',
|
||||
utils.abspath('virt/interfaces.template'),
|
||||
'Template file for injected network')
|
||||
utils.abspath('virt/libvirt.xml.template'),
|
||||
'Libvirt XML Template')
|
||||
flags.DEFINE_string('libvirt_type',
|
||||
'kvm',
|
||||
'Libvirt domain type (valid options are: '
|
||||
@ -118,13 +97,11 @@ def get_connection(read_only):
|
||||
|
||||
|
||||
class LibvirtConnection(object):
|
||||
def __init__(self, read_only):
|
||||
(self.libvirt_uri,
|
||||
template_file,
|
||||
rescue_file) = self.get_uri_and_templates()
|
||||
|
||||
self.libvirt_xml = open(template_file).read()
|
||||
self.rescue_xml = open(rescue_file).read()
|
||||
def __init__(self, read_only):
|
||||
self.libvirt_uri = self.get_uri()
|
||||
|
||||
self.libvirt_xml = open(FLAGS.libvirt_xml_template).read()
|
||||
self._wrapped_conn = None
|
||||
self.read_only = read_only
|
||||
|
||||
@ -147,20 +124,14 @@ class LibvirtConnection(object):
|
||||
return False
|
||||
raise
|
||||
|
||||
def get_uri_and_templates(self):
|
||||
def get_uri(self):
|
||||
if FLAGS.libvirt_type == 'uml':
|
||||
uri = FLAGS.libvirt_uri or 'uml:///system'
|
||||
template_file = FLAGS.libvirt_uml_xml_template
|
||||
rescue_file = FLAGS.libvirt_rescue_uml_xml_template
|
||||
elif FLAGS.libvirt_type == 'xen':
|
||||
uri = FLAGS.libvirt_uri or 'xen:///'
|
||||
template_file = FLAGS.libvirt_xen_xml_template
|
||||
rescue_file = FLAGS.libvirt_rescue_xen_xml_template
|
||||
else:
|
||||
uri = FLAGS.libvirt_uri or 'qemu:///system'
|
||||
template_file = FLAGS.libvirt_xml_template
|
||||
rescue_file = FLAGS.libvirt_rescue_xml_template
|
||||
return uri, template_file, rescue_file
|
||||
return uri
|
||||
|
||||
def _connect(self, uri, read_only):
|
||||
auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_NOECHOPROMPT],
|
||||
@ -290,6 +261,14 @@ class LibvirtConnection(object):
|
||||
timer.f = _wait_for_reboot
|
||||
return timer.start(interval=0.5, now=True)
|
||||
|
||||
@exception.wrap_exception
|
||||
def pause(self, instance, callback):
|
||||
raise exception.APIError("pause not supported for libvirt.")
|
||||
|
||||
@exception.wrap_exception
|
||||
def unpause(self, instance, callback):
|
||||
raise exception.APIError("unpause not supported for libvirt.")
|
||||
|
||||
@exception.wrap_exception
|
||||
def rescue(self, instance):
|
||||
self.destroy(instance, False)
|
||||
@ -433,18 +412,28 @@ class LibvirtConnection(object):
|
||||
if not os.path.exists(basepath('disk')):
|
||||
images.fetch(inst.image_id, basepath('disk-raw'), user,
|
||||
project)
|
||||
if not os.path.exists(basepath('kernel')):
|
||||
images.fetch(inst.kernel_id, basepath('kernel'), user,
|
||||
project)
|
||||
if not os.path.exists(basepath('ramdisk')):
|
||||
images.fetch(inst.ramdisk_id, basepath('ramdisk'), user,
|
||||
project)
|
||||
|
||||
if inst['kernel_id']:
|
||||
if not os.path.exists(basepath('kernel')):
|
||||
images.fetch(inst['kernel_id'], basepath('kernel'),
|
||||
user, project)
|
||||
if inst['ramdisk_id']:
|
||||
if not os.path.exists(basepath('ramdisk')):
|
||||
images.fetch(inst['ramdisk_id'], basepath('ramdisk'),
|
||||
user, project)
|
||||
|
||||
def execute(cmd, process_input=None, check_exit_code=True):
|
||||
return utils.execute(cmd=cmd,
|
||||
process_input=process_input,
|
||||
check_exit_code=check_exit_code)
|
||||
|
||||
# For now, we assume that if we're not using a kernel, we're using a
|
||||
# partitioned disk image where the target partition is the first
|
||||
# partition
|
||||
target_partition = None
|
||||
if not inst['kernel_id']:
|
||||
target_partition = "1"
|
||||
|
||||
key = str(inst['key_data'])
|
||||
net = None
|
||||
network_ref = db.network_get_by_instance(context.get_admin_context(),
|
||||
@ -464,12 +453,20 @@ class LibvirtConnection(object):
|
||||
inst['name'], inst.image_id)
|
||||
if net:
|
||||
logging.info('instance %s: injecting net into image %s',
|
||||
inst['name'], inst.image_id)
|
||||
disk.inject_data(basepath('disk-raw'), key, net,
|
||||
execute=execute)
|
||||
inst['name'], inst.image_id)
|
||||
try:
|
||||
disk.inject_data(basepath('disk-raw'), key, net,
|
||||
partition=target_partition,
|
||||
execute=execute)
|
||||
except Exception as e:
|
||||
# This could be a windows image, or a vmdk format disk
|
||||
logging.warn('instance %s: ignoring error injecting data'
|
||||
' into image %s (%s)',
|
||||
inst['name'], inst.image_id, e)
|
||||
|
||||
if os.path.exists(basepath('disk')):
|
||||
utils.execute('rm -f %s' % basepath('disk'))
|
||||
if inst['kernel_id']:
|
||||
if os.path.exists(basepath('disk')):
|
||||
utils.execute('rm -f %s' % basepath('disk'))
|
||||
|
||||
local_bytes = (instance_types.INSTANCE_TYPES[inst.instance_type]
|
||||
['local_gb']
|
||||
@ -478,8 +475,13 @@ class LibvirtConnection(object):
|
||||
resize = True
|
||||
if inst['instance_type'] == 'm1.tiny' or prefix == 'rescue-':
|
||||
resize = False
|
||||
disk.partition(basepath('disk-raw'), basepath('disk'),
|
||||
local_bytes, resize, execute=execute)
|
||||
|
||||
if inst['kernel_id']:
|
||||
disk.partition(basepath('disk-raw'), basepath('disk'),
|
||||
local_bytes, resize, execute=execute)
|
||||
else:
|
||||
os.rename(basepath('disk-raw'), basepath('disk'))
|
||||
disk.extend(basepath('disk'), local_bytes, execute=execute)
|
||||
|
||||
if FLAGS.libvirt_type == 'uml':
|
||||
utils.execute('sudo chown root %s' % basepath('disk'))
|
||||
@ -505,14 +507,21 @@ class LibvirtConnection(object):
|
||||
'bridge_name': network['bridge'],
|
||||
'mac_address': instance['mac_address'],
|
||||
'ip_address': ip_address,
|
||||
'dhcp_server': dhcp_server}
|
||||
if rescue:
|
||||
libvirt_xml = self.rescue_xml % xml_info
|
||||
else:
|
||||
libvirt_xml = self.libvirt_xml % xml_info
|
||||
'dhcp_server': dhcp_server,
|
||||
'rescue': rescue}
|
||||
if not rescue:
|
||||
if instance['kernel_id']:
|
||||
xml_info['kernel'] = xml_info['basepath'] + "/kernel"
|
||||
|
||||
if instance['ramdisk_id']:
|
||||
xml_info['ramdisk'] = xml_info['basepath'] + "/ramdisk"
|
||||
|
||||
xml_info['disk'] = xml_info['basepath'] + "/disk"
|
||||
|
||||
xml = str(Template(self.libvirt_xml, searchList=[xml_info]))
|
||||
logging.debug('instance %s: finished toXML method', instance['name'])
|
||||
|
||||
return libvirt_xml
|
||||
return xml
|
||||
|
||||
def get_info(self, instance_name):
|
||||
try:
|
||||
|
@ -25,6 +25,7 @@ class NetworkHelper():
|
||||
"""
|
||||
The class that wraps the helper methods together.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
return
|
||||
|
||||
|
@ -47,6 +47,7 @@ class VMHelper():
|
||||
"""
|
||||
The class that wraps the helper methods together.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
return
|
||||
|
||||
|
@ -34,6 +34,7 @@ class VMOps(object):
|
||||
"""
|
||||
Management class for VM-related tasks
|
||||
"""
|
||||
|
||||
def __init__(self, session):
|
||||
global XenAPI
|
||||
if XenAPI is None:
|
||||
@ -117,6 +118,32 @@ class VMOps(object):
|
||||
except XenAPI.Failure, exc:
|
||||
logging.warn(exc)
|
||||
|
||||
def _wait_with_callback(self, task, callback):
|
||||
ret = None
|
||||
try:
|
||||
ret = self._session.wait_for_task(task)
|
||||
except XenAPI.Failure, exc:
|
||||
logging.warn(exc)
|
||||
callback(ret)
|
||||
|
||||
def pause(self, instance, callback):
|
||||
""" Pause VM instance """
|
||||
instance_name = instance.name
|
||||
vm = VMHelper.lookup(self._session, instance_name)
|
||||
if vm is None:
|
||||
raise Exception('instance not present %s' % instance_name)
|
||||
task = self._session.call_xenapi('Async.VM.pause', vm)
|
||||
self._wait_with_callback(task, callback)
|
||||
|
||||
def unpause(self, instance, callback):
|
||||
""" Unpause VM instance """
|
||||
instance_name = instance.name
|
||||
vm = VMHelper.lookup(self._session, instance_name)
|
||||
if vm is None:
|
||||
raise Exception('instance not present %s' % instance_name)
|
||||
task = self._session.call_xenapi('Async.VM.unpause', vm)
|
||||
self._wait_with_callback(task, callback)
|
||||
|
||||
def get_info(self, instance_id):
|
||||
""" Return data about VM instance """
|
||||
vm = VMHelper.lookup_blocking(self._session, instance_id)
|
||||
|
@ -20,6 +20,7 @@ Management class for Storage-related functions (attach, detach, etc).
|
||||
|
||||
|
||||
class VolumeOps(object):
|
||||
|
||||
def __init__(self, session):
|
||||
self._session = session
|
||||
|
||||
|
@ -102,6 +102,7 @@ def get_connection(_):
|
||||
|
||||
class XenAPIConnection(object):
|
||||
""" A connection to XenServer or Xen Cloud Platform """
|
||||
|
||||
def __init__(self, url, user, pw):
|
||||
session = XenAPISession(url, user, pw)
|
||||
self._vmops = VMOps(session)
|
||||
@ -123,6 +124,14 @@ class XenAPIConnection(object):
|
||||
""" Destroy VM instance """
|
||||
self._vmops.destroy(instance)
|
||||
|
||||
def pause(self, instance, callback):
|
||||
""" Pause VM instance """
|
||||
self._vmops.pause(instance, callback)
|
||||
|
||||
def unpause(self, instance, callback):
|
||||
""" Unpause paused VM instance """
|
||||
self._vmops.unpause(instance, callback)
|
||||
|
||||
def get_info(self, instance_id):
|
||||
""" Return data about VM instance """
|
||||
return self._vmops.get_info(instance_id)
|
||||
@ -148,6 +157,7 @@ class XenAPIConnection(object):
|
||||
|
||||
class XenAPISession(object):
|
||||
""" The session to invoke XenAPI SDK calls """
|
||||
|
||||
def __init__(self, url, user, pw):
|
||||
self._session = XenAPI.Session(url)
|
||||
self._session.login_with_password(user, pw)
|
||||
|
@ -2,6 +2,7 @@ SQLAlchemy==0.6.3
|
||||
pep8==0.5.0
|
||||
pylint==0.19
|
||||
IPy==0.70
|
||||
Cheetah==2.4.2.1
|
||||
M2Crypto==0.20.2
|
||||
amqplib==0.6.1
|
||||
anyjson==0.2.4
|
||||
|
Loading…
x
Reference in New Issue
Block a user