From d963e25906b75a48c75b6e589deb2a53f75d6ee3 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 22 Jul 2011 20:29:37 -0700 Subject: Config-Drive happiness, minus smoketest --- nova/api/openstack/create_instance_helper.py | 7 +- nova/api/openstack/views/servers.py | 4 + nova/compute/api.py | 21 +++- .../versions/035_add_config_drive_to_instances.py | 43 +++++++ nova/db/sqlalchemy/models.py | 1 + nova/scheduler/simple.py | 1 + nova/tests/api/openstack/test_servers.py | 135 ++++++++++++++++++++- nova/tests/test_compute.py | 14 +++ nova/virt/libvirt.xml.template | 7 ++ nova/virt/libvirt/connection.py | 51 ++++++-- 10 files changed, 263 insertions(+), 21 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py (limited to 'nova') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 2654e3c40..fe92bae2e 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -96,6 +96,7 @@ class CreateInstanceHelper(object): locals()) raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) + config_drive = body['server'].get('config_drive') personality = body['server'].get('personality') injected_files = [] @@ -130,6 +131,7 @@ class CreateInstanceHelper(object): extra_values = { 'instance_type': inst_type, 'image_ref': image_href, + 'config_drive': config_drive, 'password': password} return (extra_values, @@ -148,7 +150,8 @@ class CreateInstanceHelper(object): zone_blob=zone_blob, reservation_id=reservation_id, min_count=min_count, - max_count=max_count)) + max_count=max_count, + config_drive=config_drive,)) except quota.QuotaError as error: self._handle_quota_error(error) except exception.ImageNotFound as error: @@ -160,6 +163,8 @@ class CreateInstanceHelper(object): def _handle_quota_error(self, error): """ Reraise quota errors as api-specific http exceptions + + """ if error.code == "OnsetFileLimitExceeded": expl = _("Personality file limit exceeded") diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index ab7e8da61..961932a4e 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -164,6 +164,7 @@ class ViewBuilderV11(ViewBuilder): def _build_extra(self, response, inst): self._build_links(response, inst) + self._build_config_drive(response, inst) def _build_links(self, response, inst): href = self.generate_href(inst["id"]) @@ -182,6 +183,9 @@ class ViewBuilderV11(ViewBuilder): response["server"]["links"] = links + def _build_config_drive(self, response, inst): + response['server']['config_drive'] = inst.get('config_drive') + def generate_href(self, server_id): """Create an url that refers to a specific server id.""" return os.path.join(self.base_url, "servers", str(server_id)) diff --git a/nova/compute/api.py b/nova/compute/api.py index 67aa3c20f..8adcbbef6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -150,7 +150,7 @@ class API(base.Base): key_name=None, key_data=None, security_group='default', availability_zone=None, user_data=None, metadata={}, injected_files=None, admin_password=None, zone_blob=None, - reservation_id=None): + reservation_id=None, config_drive=None,): """Verify all the input parameters regardless of the provisioning strategy being performed.""" @@ -181,6 +181,11 @@ class API(base.Base): (image_service, image_id) = nova.image.get_image_service(image_href) image = image_service.show(context, image_id) + config_drive_id = None + if config_drive and config_drive is not True: + # config_drive is volume id + config_drive, config_drive_id = None, config_drive + os_type = None if 'properties' in image and 'os_type' in image['properties']: os_type = image['properties']['os_type'] @@ -208,6 +213,8 @@ class API(base.Base): image_service.show(context, kernel_id) if ramdisk_id: image_service.show(context, ramdisk_id) + if config_drive_id: + image_service.show(context, config_drive_id) self.ensure_default_security_group(context) @@ -226,6 +233,8 @@ class API(base.Base): 'image_ref': image_href, 'kernel_id': kernel_id or '', 'ramdisk_id': ramdisk_id or '', + 'config_drive_id': config_drive_id or '', + 'config_drive': config_drive or '', 'state': 0, 'state_description': 'scheduling', 'user_id': context.user_id, @@ -397,7 +406,8 @@ class API(base.Base): key_name=None, key_data=None, security_group='default', availability_zone=None, user_data=None, metadata={}, injected_files=None, admin_password=None, zone_blob=None, - reservation_id=None, block_device_mapping=None): + reservation_id=None, block_device_mapping=None, + config_drive=None): """Provision the instances by passing the whole request to the Scheduler for execution. Returns a Reservation ID related to the creation of all of these instances.""" @@ -409,7 +419,7 @@ class API(base.Base): key_name, key_data, security_group, availability_zone, user_data, metadata, injected_files, admin_password, zone_blob, - reservation_id) + reservation_id, config_drive) self._ask_scheduler_to_create_instance(context, base_options, instance_type, zone_blob, @@ -426,7 +436,8 @@ class API(base.Base): key_name=None, key_data=None, security_group='default', availability_zone=None, user_data=None, metadata={}, injected_files=None, admin_password=None, zone_blob=None, - reservation_id=None, block_device_mapping=None): + reservation_id=None, block_device_mapping=None, + config_drive=None,): """ Provision the instances by sending off a series of single instance requests to the Schedulers. This is fine for trival @@ -447,7 +458,7 @@ class API(base.Base): key_name, key_data, security_group, availability_zone, user_data, metadata, injected_files, admin_password, zone_blob, - reservation_id) + reservation_id, config_drive) block_device_mapping = block_device_mapping or [] instances = [] diff --git a/nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py new file mode 100644 index 000000000..65ea012dd --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/035_add_config_drive_to_instances.py @@ -0,0 +1,43 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# +# 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 sqlalchemy import Column, Integer, MetaData, String, Table + +from nova import utils + + +meta = MetaData() + +instances = Table("instances", meta, + Column("id", Integer(), primary_key=True, nullable=False)) +config_drive_column = Column("config_drive", String(255)) # matches image_ref + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + instances.create_column(config_drive_column) + + rows = migrate_engine.execute(instances.select()) + for row in rows: + instance_config_drive = None # pre-existing instances don't have one. + migrate_engine.execute(instances.update()\ + .where(instances.c.id == row[0])\ + .values(config_drive=instance_config_drive)) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + instances.drop_column(config_drive_column) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index c1150f7ca..73ad1f011 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -238,6 +238,7 @@ class Instance(BASE, NovaBase): uuid = Column(String(36)) root_device_name = Column(String(255)) + config_drive = Column(String(255)) # TODO(vish): see Ewan's email about state improvements, probably # should be in a driver base class or some such diff --git a/nova/scheduler/simple.py b/nova/scheduler/simple.py index fc1b3142a..61c76b35d 100644 --- a/nova/scheduler/simple.py +++ b/nova/scheduler/simple.py @@ -41,6 +41,7 @@ class SimpleScheduler(chance.ChanceScheduler): def _schedule_instance(self, context, instance_id, *_args, **_kwargs): """Picks a host that is up and has the fewest running instances.""" + instance_ref = db.instance_get(context, instance_id) if (instance_ref['availability_zone'] and ':' in instance_ref['availability_zone'] diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 3fc38b73c..6a1eb8c87 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -775,11 +775,14 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 400) self.assertTrue(res.body.find('marker param') > -1) - def _setup_for_create_instance(self): + def _setup_for_create_instance(self, instance_db_stub=None): """Shared implementation for tests below that create instance""" def instance_create(context, inst): - return {'id': 1, 'display_name': 'server_test', - 'uuid': FAKE_UUID} + if instance_db_stub: + return instance_db_stub + else: + return {'id': 1, 'display_name': 'server_test', + 'uuid': FAKE_UUID,} def server_update(context, id, params): return instance_create(context, id) @@ -997,6 +1000,132 @@ class ServersTest(test.TestCase): self.assertEqual(flavor_ref, server['flavorRef']) self.assertEqual(image_href, server['imageRef']) + def test_create_instance_with_config_drive_v1_1(self): + db_stub = {'id': 100, 'display_name': 'config_drive_test', + 'uuid': FAKE_UUID, 'config_drive': True} + self._setup_for_create_instance(instance_db_stub=db_stub) + + image_href = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': True, + }, + } + + req = webob.Request.blank('/v1.1/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + server = json.loads(res.body)['server'] + self.assertEqual(100, server['id']) + self.assertTrue(server['config_drive']) + + def test_create_instance_with_config_drive_as_id_v1_1(self): + db_stub = {'id': 100, 'display_name': 'config_drive_test', + 'uuid': FAKE_UUID, 'config_drive': 2} + self._setup_for_create_instance(instance_db_stub=db_stub) + + image_href = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': 2, + }, + } + + req = webob.Request.blank('/v1.1/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + server = json.loads(res.body)['server'] + self.assertEqual(100, server['id']) + self.assertTrue(server['config_drive']) + self.assertEqual(2, server['config_drive']) + + def test_create_instance_with_bad_config_drive_v1_1(self): + db_stub = {'id': 100, 'display_name': 'config_drive_test', + 'uuid': FAKE_UUID, 'config_drive': 'asdf'} + self._setup_for_create_instance(instance_db_stub=db_stub) + + image_href = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': 'asdf', + }, + } + + req = webob.Request.blank('/v1.1/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) + + def test_create_instance_without_config_drive_v1_1(self): + db_stub = {'id': 100, 'display_name': 'config_drive_test', + 'uuid': FAKE_UUID, 'config_drive': None} + self._setup_for_create_instance(instance_db_stub=db_stub) + + image_href = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': True, + }, + } + + req = webob.Request.blank('/v1.1/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + server = json.loads(res.body)['server'] + self.assertEqual(100, server['id']) + self.assertFalse(server['config_drive']) + def test_create_instance_v1_1_bad_href(self): self._setup_for_create_instance() diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 5d59b628a..bc681706d 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -162,6 +162,20 @@ class ComputeTestCase(test.TestCase): db.security_group_destroy(self.context, group['id']) db.instance_destroy(self.context, ref[0]['id']) + def test_create_instance_associates_config_drive(self): + """Make sure create associates a config drive.""" + + instance_id = self._create_instance(params={'config_drive': True,}) + + try: + self.compute.run_instance(self.context, instance_id) + instances = db.instance_get_all(context.get_admin_context()) + instance = instances[0] + + self.assertTrue(instance.config_drive) + finally: + db.instance_destroy(self.context, instance_id) + def test_default_hostname_generator(self): cases = [(None, 'server_1'), ('Hello, Server!', 'hello_server'), ('<}\x1fh\x10e\x08l\x02l\x05o\x12!{>', 'hello')] diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index e1a683da8..4422f349f 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -55,6 +55,13 @@ #else + #if $getVar('config', False) + + + + + + #end if #if $getVar('rescue', False) diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 342dea98f..ad97dc796 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -123,6 +123,8 @@ flags.DEFINE_string('qemu_img', 'qemu-img', 'binary to use for qemu-img commands') flags.DEFINE_bool('start_guests_on_host_boot', False, 'Whether to restart guests when the host reboots') +flags.DEFINE_string('default_local_format', None, + 'Default filesystem format for local drives') def get_connection(read_only): @@ -586,6 +588,8 @@ class LibvirtConnection(driver.ComputeDriver): block_device_mapping = block_device_mapping or [] self.firewall_driver.setup_basic_filtering(instance, network_info) self.firewall_driver.prepare_instance_filter(instance, network_info) + + # This is where things actually get built. self._create_image(instance, xml, network_info=network_info, block_device_mapping=block_device_mapping) domain = self._create_new_domain(xml) @@ -763,10 +767,15 @@ class LibvirtConnection(driver.ComputeDriver): if size: disk.extend(target, size) - def _create_local(self, target, local_gb): + def _create_local(self, target, local_size, prefix='G', fs_format=None): """Create a blank image of specified size""" - utils.execute('truncate', target, '-s', "%dG" % local_gb) - # TODO(vish): should we format disk by default? + + if not fs_format: + fs_format = FLAGS.default_local_format + + utils.execute('truncate', target, '-s', "%d%c" % (local_size, prefix)) + if fs_format: + utils.execute('mkfs', '-t', fs_format, target) def _create_image(self, inst, libvirt_xml, suffix='', disk_images=None, network_info=None, block_device_mapping=None): @@ -859,6 +868,18 @@ class LibvirtConnection(driver.ComputeDriver): if FLAGS.libvirt_type == 'lxc': target_partition = None + else: + if inst['config_drive_id']: + fname = '%08x' % int(inst['config_drive_id']) + self._cache_image(fn=self._fetch_image, + target=basepath('config'), + fname=fname, + image_id=inst['config_drive_id'], + user=user, + project=project) + elif inst['config_drive']: + self._create_local(basepath('config'), 64, prefix="M", + fs_format='msdos') # 64MB if inst['key_data']: key = str(inst['key_data']) @@ -903,17 +924,23 @@ class LibvirtConnection(driver.ComputeDriver): searchList=[{'interfaces': nets, 'use_ipv6': FLAGS.use_ipv6}])) - if key or net: + if any(key, net, inst['metadata']): inst_name = inst['name'] - img_id = inst.image_ref - if key: - LOG.info(_('instance %(inst_name)s: injecting key into' - ' image %(img_id)s') % locals()) - if net: - LOG.info(_('instance %(inst_name)s: injecting net into' - ' image %(img_id)s') % locals()) + + if inst['config_drive']: # Should be True or None by now. + injection_path = basepath('config') + img_id = 'config-drive' + else: + injection_path = basepath('disk') + img_id = inst.image_ref + + for injection in ('metadata', 'key', 'net'): + if locals()[injection]: + LOG.info(_('instance %(inst_name)s: injecting ' + '%(injection)s into image %(img_id)s' + % locals())) try: - disk.inject_data(basepath('disk'), key, net, + disk.inject_data(injection_path, key, net, inst['metadata'], partition=target_partition, nbd=FLAGS.use_cow_images) -- cgit From f22cfa05f7c796fbda3d832e4bfadc325f8af6f5 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Thu, 11 Aug 2011 17:40:13 -0700 Subject: Updates to libvirt, write metadata, net, and key to the config drive --- nova/network/manager.py | 3 ++- nova/virt/disk.py | 33 +++++++++++++++++++++----- nova/virt/libvirt.xml.template | 14 +++++------ nova/virt/libvirt/connection.py | 51 ++++++++++++++++++++++++++--------------- 4 files changed, 68 insertions(+), 33 deletions(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index 75c3f668d..44bf662ce 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -893,7 +893,6 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): def _setup_network(self, context, network_ref): """Sets up network on this host.""" - network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) if not network_ref['vpn_public_address']: net = {} address = FLAGS.vpn_ip @@ -901,6 +900,8 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): network_ref = db.network_update(context, network_ref['id'], net) else: address = network_ref['vpn_public_address'] + + network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge'], network_ref['bridge_interface'], diff --git a/nova/virt/disk.py b/nova/virt/disk.py index f8aea1f34..fda3f5f29 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -2,6 +2,9 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# +# Copyright 2011, Piston Cloud Computing, Inc. +# # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -22,6 +25,7 @@ Includes injection of SSH PGP keys into authorized_keys file. """ +import json import os import tempfile import time @@ -60,7 +64,8 @@ def extend(image, size): utils.execute('resize2fs', image, check_exit_code=False) -def inject_data(image, key=None, net=None, partition=None, nbd=False): +def inject_data(image, key=None, net=None, metadata=None, + partition=None, nbd=False, tune2fs=True): """Injects a ssh key and optionally net data into a disk image. it will mount the image as a fully partitioned disk and attempt to inject @@ -89,9 +94,10 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): ' only inject raw disk images): %s' % mapped_device) - # Configure ext2fs so that it doesn't auto-check every N boots - out, err = utils.execute('sudo', 'tune2fs', - '-c', 0, '-i', 0, mapped_device) + if tune2fs: + # Configure ext2fs so that it doesn't auto-check every N boots + out, err = utils.execute('sudo', 'tune2fs', + '-c', 0, '-i', 0, mapped_device) tmpdir = tempfile.mkdtemp() try: @@ -103,7 +109,8 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): % err) try: - inject_data_into_fs(tmpdir, key, net, utils.execute) + inject_data_into_fs(tmpdir, key, net, metadata, + utils.execute) finally: # unmount device utils.execute('sudo', 'umount', mapped_device) @@ -155,6 +162,7 @@ def destroy_container(target, instance, nbd=False): def _link_device(image, nbd): """Link image to device using loopback or nbd""" + if nbd: device = _allocate_device() utils.execute('sudo', 'qemu-nbd', '-c', device, image) @@ -189,6 +197,7 @@ def _allocate_device(): # NOTE(vish): This assumes no other processes are allocating nbd devices. # It may race cause a race condition if multiple # workers are running on a given machine. + while True: if not _DEVICES: raise exception.Error(_('No free nbd devices')) @@ -202,7 +211,7 @@ def _free_device(device): _DEVICES.append(device) -def inject_data_into_fs(fs, key, net, execute): +def inject_data_into_fs(fs, key, net, metadata, execute): """Injects data into a filesystem already mounted by the caller. Virt connections can call this directly if they mount their fs in a different way to inject_data @@ -211,7 +220,19 @@ def inject_data_into_fs(fs, key, net, execute): _inject_key_into_fs(key, fs, execute=execute) if net: _inject_net_into_fs(net, fs, execute=execute) + if metadata: + _inject_metadata_into_fs(metadata, fs, execute=execute) + +def _inject_file_into_fs(injected_files, fs, execute=None): + for path, data in injected_files: + + +def _inject_metadata_into_fs(metadata, fs, execute=None): + metadata_path = os.path.join(fs, "meta.js") + metadata = dict([(m.key, m.value) for m in metadata]) + utils.execute('sudo', 'tee', '-a', metadata_path, + process_input=json.dumps(metadata)) def _inject_key_into_fs(key, fs, execute=None): """Add the given public ssh key to root's authorized_keys. diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index 4422f349f..1fa536ad1 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -55,13 +55,6 @@ #else - #if $getVar('config', False) - - - - - - #end if #if $getVar('rescue', False) @@ -96,6 +89,13 @@ #end for #end if + #if $getVar('config_drive', False) + + + + + + #end if #end if #for $nic in $nics diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index ad97dc796..35ce0dcde 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -781,6 +781,7 @@ class LibvirtConnection(driver.ComputeDriver): network_info=None, block_device_mapping=None): block_device_mapping = block_device_mapping or [] + if not suffix: suffix = '' @@ -857,7 +858,7 @@ class LibvirtConnection(driver.ComputeDriver): target=basepath('disk.local'), fname="local_%s" % inst_type['local_gb'], cow=FLAGS.use_cow_images, - local_gb=inst_type['local_gb']) + local_size=inst_type['local_gb']) # 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 @@ -866,20 +867,23 @@ class LibvirtConnection(driver.ComputeDriver): if not inst['kernel_id']: target_partition = "1" - if FLAGS.libvirt_type == 'lxc': + config_drive_id = inst.get('config_drive_id') + config_drive = inst.get('config_drive') + + if any((FLAGS.libvirt_type == 'lxc', config_drive, config_drive_id)): target_partition = None - else: - if inst['config_drive_id']: - fname = '%08x' % int(inst['config_drive_id']) - self._cache_image(fn=self._fetch_image, - target=basepath('config'), - fname=fname, - image_id=inst['config_drive_id'], - user=user, - project=project) - elif inst['config_drive']: - self._create_local(basepath('config'), 64, prefix="M", - fs_format='msdos') # 64MB + + if config_drive_id: + fname = '%08x' % int(config_drive_id) + self._cache_image(fn=self._fetch_image, + target=basepath('disk.config'), + fname=fname, + image_id=config_drive_id, + user=user, + project=project) + elif config_drive: + self._create_local(basepath('disk.config'), 64, prefix="M", + fs_format='msdos') # 64MB if inst['key_data']: key = str(inst['key_data']) @@ -924,15 +928,18 @@ class LibvirtConnection(driver.ComputeDriver): searchList=[{'interfaces': nets, 'use_ipv6': FLAGS.use_ipv6}])) - if any(key, net, inst['metadata']): + metadata = inst.get('metadata') + if any((key, net, metadata)): inst_name = inst['name'] - if inst['config_drive']: # Should be True or None by now. - injection_path = basepath('config') + if config_drive: # Should be True or None by now. + injection_path = basepath('disk.config') img_id = 'config-drive' + tune2fs = False else: injection_path = basepath('disk') img_id = inst.image_ref + tune2fs = True for injection in ('metadata', 'key', 'net'): if locals()[injection]: @@ -940,9 +947,10 @@ class LibvirtConnection(driver.ComputeDriver): '%(injection)s into image %(img_id)s' % locals())) try: - disk.inject_data(injection_path, key, net, inst['metadata'], + disk.inject_data(injection_path, key, net, metadata, partition=target_partition, - nbd=FLAGS.use_cow_images) + nbd=FLAGS.use_cow_images, + tune2fs=tune2fs) if FLAGS.libvirt_type == 'lxc': disk.setup_container(basepath('disk'), @@ -1043,6 +1051,11 @@ class LibvirtConnection(driver.ComputeDriver): 'ebs_root': ebs_root, 'volumes': block_device_mapping} + + config_drive = False + if instance.get('config_drive') or instance.get('config_drive_id'): + xml_info['config_drive'] = xml_info['basepath'] + "/disk.config" + if FLAGS.vnc_enabled and FLAGS.libvirt_type not in ('lxc', 'uml'): xml_info['vncserver_host'] = FLAGS.vncserver_host xml_info['vnc_keymap'] = FLAGS.vnc_keymap -- cgit From b776f19c21d1a56ac851435182c0c267166d49dd Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Thu, 11 Aug 2011 17:59:41 -0700 Subject: Accidentally added inject_files to merge --- nova/virt/disk.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/virt/disk.py b/nova/virt/disk.py index fda3f5f29..63e09d014 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -223,9 +223,6 @@ def inject_data_into_fs(fs, key, net, metadata, execute): if metadata: _inject_metadata_into_fs(metadata, fs, execute=execute) -def _inject_file_into_fs(injected_files, fs, execute=None): - for path, data in injected_files: - def _inject_metadata_into_fs(metadata, fs, execute=None): metadata_path = os.path.join(fs, "meta.js") @@ -234,6 +231,7 @@ def _inject_metadata_into_fs(metadata, fs, execute=None): utils.execute('sudo', 'tee', '-a', metadata_path, process_input=json.dumps(metadata)) + def _inject_key_into_fs(key, fs, execute=None): """Add the given public ssh key to root's authorized_keys. -- cgit From 91eaa647506a2e343e8c689289529eafea0bc9d3 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 12 Aug 2011 14:33:27 -0700 Subject: Fix ugly little violations before someone says anything --- nova/api/openstack/create_instance_helper.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 0ec455167..d776ae92d 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -178,8 +178,6 @@ class CreateInstanceHelper(object): def _handle_quota_error(self, error): """ Reraise quota errors as api-specific http exceptions - - """ if error.code == "OnsetFileLimitExceeded": expl = _("Personality file limit exceeded") -- cgit From 90650e5becb541790a8949edebaf0bff0ceb8f5b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 19:31:01 -0700 Subject: make admin context the default, clean up pipelib --- nova/api/auth.py | 18 ++++++++++++++++++ nova/api/ec2/admin.py | 4 +++- nova/auth/manager.py | 3 +++ nova/cloudpipe/pipelib.py | 7 +++---- 4 files changed, 27 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/api/auth.py b/nova/api/auth.py index cd3e3e8a0..050216fd7 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -45,6 +45,24 @@ class InjectContext(wsgi.Middleware): return self.application +class AdminContext(wsgi.Middleware): + """Return an admin context no matter what""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + # Build a context, including the auth_token... + remote_address = req.remote_addr + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext('admin', + 'admin', + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application + + class KeystoneContext(wsgi.Middleware): """Make a request context from keystone headers""" diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index df7876b9d..dfbbc0a2b 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -283,8 +283,10 @@ class AdminController(object): # NOTE(vish) import delayed because of __init__.py from nova.cloudpipe import pipelib pipe = pipelib.CloudPipe() + proj = manager.AuthManager().get_project(project) + user_id = proj.project_manager_id try: - pipe.launch_vpn_instance(project) + pipe.launch_vpn_instance(project, user_id) except db.NoMoreNetworks: raise exception.ApiError("Unable to claim IP for VPN instance" ", ensure it isn't running, and try " diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 6205cfb56..c9178c0dd 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -17,6 +17,9 @@ # under the License. """ +WARNING: This code is deprecated and will be removed. +Keystone is recommended is the recommended solution for auth management. + Nova authentication management """ diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py index 2c4673f9e..1b742384c 100644 --- a/nova/cloudpipe/pipelib.py +++ b/nova/cloudpipe/pipelib.py @@ -93,11 +93,10 @@ class CloudPipe(object): zippy.close() return encoded - def launch_vpn_instance(self, project_id): + def launch_vpn_instance(self, project_id, user_id): LOG.debug(_("Launching VPN for %s") % (project_id)) - project = self.manager.get_project(project_id) - ctxt = context.RequestContext(user=project.project_manager_id, - project=project.id) + ctxt = context.RequestContext(user_id=user_id, + project_id=project_id) key_name = self.setup_key_pair(ctxt) group_name = self.setup_security_group(ctxt) -- cgit From 41819d8d048b889f2e7f5e4ee0ff2873bfdef904 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 20:22:30 -0700 Subject: fix integration tests --- nova/api/openstack/auth.py | 17 +++++ nova/tests/integrated/integrated_helpers.py | 109 ++++------------------------ nova/tests/integrated/test_login.py | 33 --------- nova/tests/integrated/test_servers.py | 2 +- 4 files changed, 31 insertions(+), 130 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index d42abe1f8..f4a50fc46 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -34,6 +34,23 @@ LOG = logging.getLogger('nova.api.openstack') FLAGS = flags.FLAGS +class NoAuthMiddleware(wsgi.Middleware): + """Return a fake token if one isn't specified.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + if 'X-Auth-Token' in req.headers: + return self.application + logging.debug("Got no auth token, returning fake info.") + res = webob.Response() + res.headers['X-Auth-Token'] = 'fake' + res.headers['X-Server-Management-Url'] = req.url + res.headers['X-Storage-Url'] = '' + res.headers['X-CDN-Management-Url'] = '' + res.content_type = 'text/plain' + res.status = '204' + return res + class AuthMiddleware(wsgi.Middleware): """Authorize the openstack API request or return an HTTP Forbidden.""" diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py index fb2f88502..343190427 100644 --- a/nova/tests/integrated/integrated_helpers.py +++ b/nova/tests/integrated/integrated_helpers.py @@ -22,10 +22,8 @@ Provides common functionality for integrated unit tests import random import string -from nova import exception from nova import service from nova import test # For the flags -from nova.auth import manager import nova.image.glance from nova.log import logging from nova.tests.integrated.api import client @@ -58,90 +56,6 @@ def generate_new_element(items, prefix, numeric=False): LOG.debug("Random collision on %s" % candidate) -class TestUser(object): - def __init__(self, name, secret, auth_url): - self.name = name - self.secret = secret - self.auth_url = auth_url - - if not auth_url: - raise exception.Error("auth_url is required") - self.openstack_api = client.TestOpenStackClient(self.name, - self.secret, - self.auth_url) - - def get_unused_server_name(self): - servers = self.openstack_api.get_servers() - server_names = [server['name'] for server in servers] - return generate_new_element(server_names, 'server') - - def get_invalid_image(self): - images = self.openstack_api.get_images() - image_ids = [image['id'] for image in images] - return generate_new_element(image_ids, '', numeric=True) - - def get_valid_image(self, create=False): - images = self.openstack_api.get_images() - if create and not images: - # TODO(justinsb): No way currently to create an image through API - #created_image = self.openstack_api.post_image(image) - #images.append(created_image) - raise exception.Error("No way to create an image through API") - - if images: - return images[0] - return None - - -class IntegratedUnitTestContext(object): - def __init__(self, auth_url): - self.auth_manager = manager.AuthManager() - - self.auth_url = auth_url - self.project_name = None - - self.test_user = None - - self.setup() - - def setup(self): - self._create_test_user() - - def _create_test_user(self): - self.test_user = self._create_unittest_user() - - # No way to currently pass this through the OpenStack API - self.project_name = 'openstack' - self._configure_project(self.project_name, self.test_user) - - def cleanup(self): - self.test_user = None - - def _create_unittest_user(self): - users = self.auth_manager.get_users() - user_names = [user.name for user in users] - auth_name = generate_new_element(user_names, 'unittest_user_') - auth_key = generate_random_alphanumeric(16) - - # Right now there's a bug where auth_name and auth_key are reversed - # bug732907 - auth_key = auth_name - - self.auth_manager.create_user(auth_name, auth_name, auth_key, False) - return TestUser(auth_name, auth_key, self.auth_url) - - def _configure_project(self, project_name, user): - projects = self.auth_manager.get_projects() - project_names = [project.name for project in projects] - if not project_name in project_names: - project = self.auth_manager.create_project(project_name, - user.name, - description=None, - member_users=None) - else: - self.auth_manager.add_to_project(user.name, project_name) - - class _IntegratedTestBase(test.TestCase): def setUp(self): super(_IntegratedTestBase, self).setUp() @@ -163,10 +77,7 @@ class _IntegratedTestBase(test.TestCase): self._start_api_service() - self.context = IntegratedUnitTestContext(self.auth_url) - - self.user = self.context.test_user - self.api = self.user.openstack_api + self.api = client.TestOpenStackClient('fake', 'fake', self.auth_url) def _start_api_service(self): osapi = service.WSGIService("osapi") @@ -174,10 +85,6 @@ class _IntegratedTestBase(test.TestCase): self.auth_url = 'http://%s:%s/v1.1' % (osapi.host, osapi.port) LOG.warn(self.auth_url) - def tearDown(self): - self.context.cleanup() - super(_IntegratedTestBase, self).tearDown() - def _get_flags(self): """An opportunity to setup flags, before the services are started.""" f = {} @@ -190,10 +97,20 @@ class _IntegratedTestBase(test.TestCase): f['fake_network'] = True return f + def get_unused_server_name(self): + servers = self.api.get_servers() + server_names = [server['name'] for server in servers] + return generate_new_element(server_names, 'server') + + def get_invalid_image(self): + images = self.api.get_images() + image_ids = [image['id'] for image in images] + return generate_new_element(image_ids, '', numeric=True) + def _build_minimal_create_server_request(self): server = {} - image = self.user.get_valid_image(create=True) + image = self.api.get_images()[0] LOG.debug("Image: %s" % image) if 'imageRef' in image: @@ -211,7 +128,7 @@ class _IntegratedTestBase(test.TestCase): server['flavorRef'] = 'http://fake.server/%s' % flavor['id'] # Set a valid server name - server_name = self.user.get_unused_server_name() + server_name = self.get_unused_server_name() server['name'] = server_name return server diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py index 06359a52f..3a863d0f9 100644 --- a/nova/tests/integrated/test_login.py +++ b/nova/tests/integrated/test_login.py @@ -15,11 +15,9 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest from nova.log import logging from nova.tests.integrated import integrated_helpers -from nova.tests.integrated.api import client LOG = logging.getLogger('nova.tests.integrated') @@ -31,34 +29,3 @@ class LoginTest(integrated_helpers._IntegratedTestBase): flavors = self.api.get_flavors() for flavor in flavors: LOG.debug(_("flavor: %s") % flavor) - - def test_bad_login_password(self): - """Test that I get a 401 with a bad username.""" - bad_credentials_api = client.TestOpenStackClient(self.user.name, - "notso_password", - self.user.auth_url) - - self.assertRaises(client.OpenStackApiAuthenticationException, - bad_credentials_api.get_flavors) - - def test_bad_login_username(self): - """Test that I get a 401 with a bad password.""" - bad_credentials_api = client.TestOpenStackClient("notso_username", - self.user.secret, - self.user.auth_url) - - self.assertRaises(client.OpenStackApiAuthenticationException, - bad_credentials_api.get_flavors) - - def test_bad_login_both_bad(self): - """Test that I get a 401 with both bad username and bad password.""" - bad_credentials_api = client.TestOpenStackClient("notso_username", - "notso_password", - self.user.auth_url) - - self.assertRaises(client.OpenStackApiAuthenticationException, - bad_credentials_api.get_flavors) - - -if __name__ == "__main__": - unittest.main() diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py index 725f6d529..c2f800689 100644 --- a/nova/tests/integrated/test_servers.py +++ b/nova/tests/integrated/test_servers.py @@ -51,7 +51,7 @@ class ServersTest(integrated_helpers._IntegratedTestBase): self.api.post_server, post) # With an invalid imageRef, this throws 500. - server['imageRef'] = self.user.get_invalid_image() + server['imageRef'] = self.get_invalid_image() # TODO(justinsb): Check whatever the spec says should be thrown here self.assertRaises(client.OpenStackApiException, self.api.post_server, post) -- cgit From a1ceed43d6ab871d3dea721b855bd7eabec48433 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 20:24:52 -0700 Subject: clean up fake auth from server actions test --- nova/tests/api/openstack/test_server_actions.py | 8 -------- 1 file changed, 8 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_server_actions.py b/nova/tests/api/openstack/test_server_actions.py index 687a19390..3b419dec5 100644 --- a/nova/tests/api/openstack/test_server_actions.py +++ b/nova/tests/api/openstack/test_server_actions.py @@ -1,17 +1,13 @@ import base64 import json -import unittest -from xml.dom import minidom import stubout import webob from nova import context -from nova import db from nova import utils from nova import flags from nova.api.openstack import create_instance_helper -from nova.compute import instance_types from nova.compute import power_state import nova.db.api from nova import test @@ -103,8 +99,6 @@ class ServerActionsTest(test.TestCase): super(ServerActionsTest, self).setUp() self.flags(verbose=True) self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} fakes.stub_out_auth(self.stubs) self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id) self.stubs.Set(nova.db.api, 'instance_update', instance_update) @@ -468,8 +462,6 @@ class ServerActionsTestV11(test.TestCase): self.maxDiff = None super(ServerActionsTestV11, self).setUp() self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} fakes.stub_out_auth(self.stubs) self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id) self.stubs.Set(nova.db.api, 'instance_update', instance_update) -- cgit From 6c256a9a5c013f8674776adb2005b4f541f705b5 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 20:26:33 -0700 Subject: remove extra reference in pipelib --- nova/cloudpipe/pipelib.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova') diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py index 1b742384c..3eb372844 100644 --- a/nova/cloudpipe/pipelib.py +++ b/nova/cloudpipe/pipelib.py @@ -34,7 +34,6 @@ from nova import exception from nova import flags from nova import log as logging from nova import utils -from nova.auth import manager # TODO(eday): Eventually changes these to something not ec2-specific from nova.api.ec2 import cloud @@ -57,7 +56,6 @@ LOG = logging.getLogger('nova.cloudpipe') class CloudPipe(object): def __init__(self): self.controller = cloud.CloudController() - self.manager = manager.AuthManager() def get_encoded_zip(self, project_id): # Make a payload.zip -- cgit From 6d87608cf835e1c27f3b6b6b31e6b41b0aa90b90 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 20:35:54 -0700 Subject: pep8 --- nova/api/openstack/auth.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova') diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index f4a50fc46..b37f9aade 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -51,6 +51,7 @@ class NoAuthMiddleware(wsgi.Middleware): res.status = '204' return res + class AuthMiddleware(wsgi.Middleware): """Authorize the openstack API request or return an HTTP Forbidden.""" -- cgit From 794d73a916a6bf97cbad4d0cfd5cf359e0418af0 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 19 Aug 2011 09:53:31 +0200 Subject: Move documentation from nova.virt.fake to nova.virt.driver. --- nova/virt/driver.py | 247 +++++++++++++++++++++++++++++++++++++++++++--- nova/virt/fake.py | 278 +--------------------------------------------------- 2 files changed, 238 insertions(+), 287 deletions(-) (limited to 'nova') diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 20af2666d..72fe118b4 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -62,11 +62,41 @@ def block_device_info_get_mapping(block_device_info): class ComputeDriver(object): """Base class for compute drivers. - Lots of documentation is currently on fake.py. + The interface to this class talks in terms of 'instances' (Amazon EC2 and + internal Nova terminology), by which we mean 'running virtual machine' + (XenAPI terminology) or domain (Xen or libvirt terminology). + + An instance has an ID, which is the identifier chosen by Nova to represent + the instance further up the stack. This is unfortunately also called a + 'name' elsewhere. As far as this layer is concerned, 'instance ID' and + 'instance name' are synonyms. + + Note that the instance ID or name is not human-readable or + customer-controlled -- it's an internal ID chosen by Nova. At the + nova.virt layer, instances do not have human-readable names at all -- such + things are only known higher up the stack. + + Most virtualization platforms will also have their own identity schemes, + to uniquely identify a VM or domain. These IDs must stay internal to the + platform-specific layer, and never escape the connection interface. The + platform-specific layer is responsible for keeping track of which instance + ID maps to which platform-specific ID, and vice versa. + + In contrast, the list_disks and list_interfaces calls may return + platform-specific IDs. These identify a specific virtual disk or specific + virtual network interface, and these IDs are opaque to the rest of Nova. + + Some methods here take an instance of nova.compute.service.Instance. This + is the datastructure used by nova.compute to store details regarding an + instance, and pass them into this layer. This layer is responsible for + translating that generic datastructure into terms that are specific to the + virtualization platform. + """ def init_host(self, host): - """Adopt existing VM's running here""" + """Initialize anything that is necessary for the driver to function, + including catching up with currently running VM's on the given host.""" # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -84,6 +114,10 @@ class ComputeDriver(object): raise NotImplementedError() def list_instances(self): + """ + Return the names of all the instances known to the virtualization + layer, as a list. + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -94,7 +128,20 @@ class ComputeDriver(object): def spawn(self, context, instance, network_info=None, block_device_info=None): - """Launch a VM for the specified instance""" + """ + Create a new instance/VM/domain on the virtualization platform. + + The given parameter is an instance of nova.compute.service.Instance. + This function should use the data there to guide the creation of + the new instance. + + Once this successfully completes, the instance should be + running (power_state.RUNNING). + + If this fails, any partial instance should be completely + cleaned up, and the virtualization platform should be in the state + that it was before this call began. + """ raise NotImplementedError() def destroy(self, instance, network_info, cleanup=True): @@ -115,7 +162,11 @@ class ComputeDriver(object): raise NotImplementedError() def reboot(self, instance, network_info): - """Reboot specified VM""" + """Reboot the specified instance. + + The given parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -140,26 +191,40 @@ class ComputeDriver(object): raise NotImplementedError() def get_host_ip_addr(self): + """ + Retrieves the IP address of the dom0 + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() def attach_volume(self, context, instance_id, volume_id, mountpoint): + """Attach the disk at device_path to the instance at mountpoint""" raise NotImplementedError() def detach_volume(self, context, instance_id, volume_id): + """Detach the disk attached to the instance at mountpoint""" raise NotImplementedError() - def compare_cpu(self, context, cpu_info): + def compare_cpu(self, cpu_info): raise NotImplementedError() def migrate_disk_and_power_off(self, instance, dest): - """Transfers the VHD of a running instance to another host, then shuts - off the instance copies over the COW disk""" + """ + Transfers the disk of a running instance in multiple phases, turning + off the instance before the end. + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() def snapshot(self, context, instance, image_id): - """Create snapshot from a running VM instance.""" + """ + Snapshots the specified instance. + + The given parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. + + The second parameter is the name of the snapshot. + """ raise NotImplementedError() def finish_migration(self, context, instance, disk_info, network_info, @@ -173,7 +238,7 @@ class ComputeDriver(object): raise NotImplementedError() def pause(self, instance, callback): - """Pause VM instance""" + """Pause the specified instance.""" # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -235,15 +300,69 @@ class ComputeDriver(object): raise NotImplementedError() def refresh_security_group_rules(self, security_group_id): + """This method is called after a change to security groups. + + All security groups and their associated rules live in the datastore, + and calling this method should apply the updated rules to instances + running the specified security group. + + An error should be raised if the operation cannot complete. + + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() def refresh_security_group_members(self, security_group_id): + """This method is called when a security group is added to an instance. + + This message is sent to the virtualization drivers on hosts that are + running an instance that belongs to a security group that has a rule + that references the security group identified by `security_group_id`. + It is the responsiblity of this method to make sure any rules + that authorize traffic flow with members of the security group are + updated and any new members can communicate, and any removed members + cannot. + + Scenario: + * we are running on host 'H0' and we have an instance 'i-0'. + * instance 'i-0' is a member of security group 'speaks-b' + * group 'speaks-b' has an ingress rule that authorizes group 'b' + * another host 'H1' runs an instance 'i-1' + * instance 'i-1' is a member of security group 'b' + + When 'i-1' launches or terminates we will recieve the message + to update members of group 'b', at which time we will make + any changes needed to the rules for instance 'i-0' to allow + or deny traffic coming from 'i-1', depending on if it is being + added or removed from the group. + + In this scenario, 'i-1' could just as easily have been running on our + host 'H0' and this method would still have been called. The point was + that this method isn't called on the host where instances of that + group are running (as is the case with + :method:`refresh_security_group_rules`) but is called where references + are made to authorizing those instances. + + An error should be raised if the operation cannot complete. + + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() def refresh_provider_fw_rules(self, security_group_id): - """See: nova/virt/fake.py for docs.""" + """This triggers a firewall update based on database changes. + + When this is called, rules have either been added or removed from the + datastore. You can retrieve rules with + :method:`nova.db.api.provider_fw_rule_get_all`. + + Provider rules take precedence over security group rules. If an IP + would be allowed by a security group ingress rule, but blocked by + a provider rule, then packets from the IP are dropped. This includes + intra-project traffic in the case of the allow_project_net_traffic + flag for the libvirt-derived classes. + + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -284,18 +403,38 @@ class ComputeDriver(object): raise NotImplementedError() def set_admin_password(self, context, instance_id, new_pass=None): - """Set the root/admin password for an instance on this server.""" + """ + Set the root password on the specified instance. + + The first parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. The second + parameter is the value of the new password. + """ raise NotImplementedError() def inject_file(self, instance, b64_path, b64_contents): - """Create a file on the VM instance. The file path and contents - should be base64-encoded. + """ + Writes a file on the specified instance. + + The first parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. The second + parameter is the base64-encoded path to which the file is to be + written on the instance; the third is the contents of the file, also + base64-encoded. """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() def agent_update(self, instance, url, md5hash): - """Update agent on the VM instance.""" + """ + Update agent on the specified instance. + + The first parameter is an instance of nova.compute.service.Instance, + and so the instance is being specified as instance.name. The second + parameter is the URL of the agent to be fetched and updated on the + instance; the third is the md5 hash of the file for verification + purposes. + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -322,3 +461,83 @@ class ComputeDriver(object): """Plugs in VIFs to networks.""" # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() + + def update_host_status(self): + """Refresh host stats""" + raise NotImplementedError() + + def get_host_stats(self, refresh=False): + """Return currently known host stats""" + raise NotImplementedError() + + def list_disks(self, instance_name): + """ + Return the IDs of all the virtual disks attached to the specified + instance, as a list. These IDs are opaque to the caller (they are + only useful for giving back to this layer as a parameter to + disk_stats). These IDs only need to be unique for a given instance. + + Note that this function takes an instance ID. + """ + raise NotImplementedError() + + def list_interfaces(self, instance_name): + """ + Return the IDs of all the virtual network interfaces attached to the + specified instance, as a list. These IDs are opaque to the caller + (they are only useful for giving back to this layer as a parameter to + interface_stats). These IDs only need to be unique for a given + instance. + + Note that this function takes an instance ID. + """ + raise NotImplementedError() + + def resize(self, instance, flavor): + """ + Resizes/Migrates the specified instance. + + The flavor parameter determines whether or not the instance RAM and + disk space are modified, and if so, to what size. + """ + raise NotImplementedError() + + def block_stats(self, instance_name, disk_id): + """ + Return performance counters associated with the given disk_id on the + given instance_name. These are returned as [rd_req, rd_bytes, wr_req, + wr_bytes, errs], where rd indicates read, wr indicates write, req is + the total number of I/O requests made, bytes is the total number of + bytes transferred, and errs is the number of requests held up due to a + full pipeline. + + All counters are long integers. + + This method is optional. On some platforms (e.g. XenAPI) performance + statistics can be retrieved directly in aggregate form, without Nova + having to do the aggregation. On those platforms, this method is + unused. + + Note that this function takes an instance ID. + """ + raise NotImplementedError() + + def interface_stats(self, instance_name, iface_id): + """ + Return performance counters associated with the given iface_id on the + given instance_id. These are returned as [rx_bytes, rx_packets, + rx_errs, rx_drop, tx_bytes, tx_packets, tx_errs, tx_drop], where rx + indicates receive, tx indicates transmit, bytes and packets indicate + the total number of bytes or packets transferred, and errs and dropped + is the total number of packets failed / dropped. + + All counters are long integers. + + This method is optional. On some platforms (e.g. XenAPI) performance + statistics can be retrieved directly in aggregate form, without Nova + having to do the aggregation. On those platforms, this method is + unused. + + Note that this function takes an instance ID. + """ + raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index dc0628772..67cc1ea29 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -48,37 +48,7 @@ class FakeInstance(object): class FakeConnection(driver.ComputeDriver): - """ - The interface to this class talks in terms of 'instances' (Amazon EC2 and - internal Nova terminology), by which we mean 'running virtual machine' - (XenAPI terminology) or domain (Xen or libvirt terminology). - - An instance has an ID, which is the identifier chosen by Nova to represent - the instance further up the stack. This is unfortunately also called a - 'name' elsewhere. As far as this layer is concerned, 'instance ID' and - 'instance name' are synonyms. - - Note that the instance ID or name is not human-readable or - customer-controlled -- it's an internal ID chosen by Nova. At the - nova.virt layer, instances do not have human-readable names at all -- such - things are only known higher up the stack. - - Most virtualization platforms will also have their own identity schemes, - to uniquely identify a VM or domain. These IDs must stay internal to the - platform-specific layer, and never escape the connection interface. The - platform-specific layer is responsible for keeping track of which instance - ID maps to which platform-specific ID, and vice versa. - - In contrast, the list_disks and list_interfaces calls may return - platform-specific IDs. These identify a specific virtual disk or specific - virtual network interface, and these IDs are opaque to the rest of Nova. - - Some methods here take an instance of nova.compute.service.Instance. This - is the datastructure used by nova.compute to store details regarding an - instance, and pass them into this layer. This layer is responsible for - translating that generic datastructure into terms that are specific to the - virtualization platform. - """ + """Fake hypervisor driver""" def __init__(self): self.instances = {} @@ -105,17 +75,9 @@ class FakeConnection(driver.ComputeDriver): return cls._instance def init_host(self, host): - """ - Initialize anything that is necessary for the driver to function, - including catching up with currently running VM's on the given host. - """ return def list_instances(self): - """ - Return the names of all the instances known to the virtualization - layer, as a list. - """ return self.instances.keys() def _map_to_instance_info(self, instance): @@ -131,167 +93,54 @@ class FakeConnection(driver.ComputeDriver): def spawn(self, context, instance, network_info=None, block_device_info=None): - """ - Create a new instance/VM/domain on the virtualization platform. - - The given parameter is an instance of nova.compute.service.Instance. - This function should use the data there to guide the creation of - the new instance. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - - Once this successfully completes, the instance should be - running (power_state.RUNNING). - - If this fails, any partial instance should be completely - cleaned up, and the virtualization platform should be in the state - that it was before this call began. - """ - name = instance.name state = power_state.RUNNING fake_instance = FakeInstance(name, state) self.instances[name] = fake_instance def snapshot(self, context, instance, name): - """ - Snapshots the specified instance. - - The given parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. - - The second parameter is the name of the snapshot. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - """ pass def reboot(self, instance, network_info): - """ - Reboot the specified instance. - - The given parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - """ pass def get_host_ip_addr(self): - """ - Retrieves the IP address of the dom0 - """ - pass + return '192.168.0.1' def resize(self, instance, flavor): - """ - Resizes/Migrates the specified instance. - - The flavor parameter determines whether or not the instance RAM and - disk space are modified, and if so, to what size. - - The work will be done asynchronously. This function returns a task - that allows the caller to detect when it is complete. - """ pass def set_admin_password(self, instance, new_pass): - """ - Set the root password on the specified instance. - - The first parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. The second - parameter is the value of the new password. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - """ pass def inject_file(self, instance, b64_path, b64_contents): - """ - Writes a file on the specified instance. - - The first parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. The second - parameter is the base64-encoded path to which the file is to be - written on the instance; the third is the contents of the file, also - base64-encoded. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - """ pass def agent_update(self, instance, url, md5hash): - """ - Update agent on the specified instance. - - The first parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. The second - parameter is the URL of the agent to be fetched and updated on the - instance; the third is the md5 hash of the file for verification - purposes. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. - """ pass def rescue(self, context, instance, callback, network_info): - """ - Rescue the specified instance. - """ pass def unrescue(self, instance, callback, network_info): - """ - Unrescue the specified instance. - """ pass def poll_rescued_instances(self, timeout): - """Poll for rescued instances""" pass def migrate_disk_and_power_off(self, instance, dest): - """ - Transfers the disk of a running instance in multiple phases, turning - off the instance before the end. - """ - pass - - def attach_disk(self, instance, disk_info): - """ - Attaches the disk to an instance given the metadata disk_info - """ pass def pause(self, instance, callback): - """ - Pause the specified instance. - """ pass def unpause(self, instance, callback): - """ - Unpause the specified instance. - """ pass def suspend(self, instance, callback): - """ - suspend the specified instance - """ pass def resume(self, instance, callback): - """ - resume the specified instance - """ pass def destroy(self, instance, network_info, cleanup=True): @@ -303,25 +152,14 @@ class FakeConnection(driver.ComputeDriver): (key, self.instances)) def attach_volume(self, instance_name, device_path, mountpoint): - """Attach the disk at device_path to the instance at mountpoint""" + self._mounts[mountpoint] = device_path return True def detach_volume(self, instance_name, mountpoint): - """Detach the disk attached to the instance at mountpoint""" + del self._mounts[mountpoint] return True def get_info(self, instance_name): - """ - Get a block of information about the given instance. This is returned - as a dictionary containing 'state': The power_state of the instance, - 'max_mem': The maximum memory for the instance, in KiB, 'mem': The - current memory the instance has, in KiB, 'num_cpu': The current number - of virtual CPUs the instance has, 'cpu_time': The total CPU time used - by the instance, in nanoseconds. - - This method should raise exception.NotFound if the hypervisor has no - knowledge of the instance - """ if instance_name not in self.instances: raise exception.InstanceNotFound(instance_id=instance_name) i = self.instances[instance_name] @@ -332,69 +170,18 @@ class FakeConnection(driver.ComputeDriver): 'cpu_time': 0} def get_diagnostics(self, instance_name): - pass + return {} def list_disks(self, instance_name): - """ - Return the IDs of all the virtual disks attached to the specified - instance, as a list. These IDs are opaque to the caller (they are - only useful for giving back to this layer as a parameter to - disk_stats). These IDs only need to be unique for a given instance. - - Note that this function takes an instance ID. - """ return ['A_DISK'] def list_interfaces(self, instance_name): - """ - Return the IDs of all the virtual network interfaces attached to the - specified instance, as a list. These IDs are opaque to the caller - (they are only useful for giving back to this layer as a parameter to - interface_stats). These IDs only need to be unique for a given - instance. - - Note that this function takes an instance ID. - """ return ['A_VIF'] def block_stats(self, instance_name, disk_id): - """ - Return performance counters associated with the given disk_id on the - given instance_name. These are returned as [rd_req, rd_bytes, wr_req, - wr_bytes, errs], where rd indicates read, wr indicates write, req is - the total number of I/O requests made, bytes is the total number of - bytes transferred, and errs is the number of requests held up due to a - full pipeline. - - All counters are long integers. - - This method is optional. On some platforms (e.g. XenAPI) performance - statistics can be retrieved directly in aggregate form, without Nova - having to do the aggregation. On those platforms, this method is - unused. - - Note that this function takes an instance ID. - """ return [0L, 0L, 0L, 0L, None] def interface_stats(self, instance_name, iface_id): - """ - Return performance counters associated with the given iface_id on the - given instance_id. These are returned as [rx_bytes, rx_packets, - rx_errs, rx_drop, tx_bytes, tx_packets, tx_errs, tx_drop], where rx - indicates receive, tx indicates transmit, bytes and packets indicate - the total number of bytes or packets transferred, and errs and dropped - is the total number of packets failed / dropped. - - All counters are long integers. - - This method is optional. On some platforms (e.g. XenAPI) performance - statistics can be retrieved directly in aggregate form, without Nova - having to do the aggregation. On those platforms, this method is - unused. - - Note that this function takes an instance ID. - """ return [0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L] def get_console_output(self, instance): @@ -416,67 +203,12 @@ class FakeConnection(driver.ComputeDriver): 'password': 'fakepassword'} def refresh_security_group_rules(self, security_group_id): - """This method is called after a change to security groups. - - All security groups and their associated rules live in the datastore, - and calling this method should apply the updated rules to instances - running the specified security group. - - An error should be raised if the operation cannot complete. - - """ return True def refresh_security_group_members(self, security_group_id): - """This method is called when a security group is added to an instance. - - This message is sent to the virtualization drivers on hosts that are - running an instance that belongs to a security group that has a rule - that references the security group identified by `security_group_id`. - It is the responsiblity of this method to make sure any rules - that authorize traffic flow with members of the security group are - updated and any new members can communicate, and any removed members - cannot. - - Scenario: - * we are running on host 'H0' and we have an instance 'i-0'. - * instance 'i-0' is a member of security group 'speaks-b' - * group 'speaks-b' has an ingress rule that authorizes group 'b' - * another host 'H1' runs an instance 'i-1' - * instance 'i-1' is a member of security group 'b' - - When 'i-1' launches or terminates we will recieve the message - to update members of group 'b', at which time we will make - any changes needed to the rules for instance 'i-0' to allow - or deny traffic coming from 'i-1', depending on if it is being - added or removed from the group. - - In this scenario, 'i-1' could just as easily have been running on our - host 'H0' and this method would still have been called. The point was - that this method isn't called on the host where instances of that - group are running (as is the case with - :method:`refresh_security_group_rules`) but is called where references - are made to authorizing those instances. - - An error should be raised if the operation cannot complete. - - """ return True def refresh_provider_fw_rules(self): - """This triggers a firewall update based on database changes. - - When this is called, rules have either been added or removed from the - datastore. You can retrieve rules with - :method:`nova.db.api.provider_fw_rule_get_all`. - - Provider rules take precedence over security group rules. If an IP - would be allowed by a security group ingress rule, but blocked by - a provider rule, then packets from the IP are dropped. This includes - intra-project traffic in the case of the allow_project_net_traffic - flag for the libvirt-derived classes. - - """ pass def update_available_resource(self, ctxt, host): -- cgit From a2c7e0f7fc6e25828ab32f133965b300e37d9264 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 19 Aug 2011 13:44:32 +0200 Subject: Start improving documentation. --- nova/virt/driver.py | 57 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 16 deletions(-) (limited to 'nova') diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 72fe118b4..6d3af7e0a 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -104,6 +104,7 @@ class ComputeDriver(object): """Get the current status of an instance, by name (not ID!) Returns a dict containing: + :state: the running state, one of the power_state codes :max_mem: (int) the maximum memory in KBytes allowed :mem: (int) the memory in KBytes used by the domain @@ -131,16 +132,20 @@ class ComputeDriver(object): """ Create a new instance/VM/domain on the virtualization platform. - The given parameter is an instance of nova.compute.service.Instance. - This function should use the data there to guide the creation of - the new instance. - Once this successfully completes, the instance should be running (power_state.RUNNING). If this fails, any partial instance should be completely cleaned up, and the virtualization platform should be in the state that it was before this call began. + + :param context: security context + :param instance: Instance of {nova.compute.service.Instance}. + This function should use the data there to guide + the creation of the new instance. + :param network_info: + :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` + :param block_device_info: """ raise NotImplementedError() @@ -148,15 +153,17 @@ class ComputeDriver(object): """Destroy (shutdown and delete) the specified instance. The given parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. - - The work will be done asynchronously. This function returns a - task that allows the caller to detect when it is complete. If the instance is not found (for example if networking failed), this function should still succeed. It's probably a good idea to log a warning in that case. + :param instance: Instance of {nova.compute.service.Instance} and so + the instance is being specified as instance.name. + :param network_info: + :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` + :param cleanup: + """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -164,8 +171,10 @@ class ComputeDriver(object): def reboot(self, instance, network_info): """Reboot the specified instance. - The given parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. + :param instance: Instance of {nova.compute.service.Instance} and so + the instance is being specified as instance.name. + :param network_info: + :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() @@ -206,6 +215,17 @@ class ComputeDriver(object): raise NotImplementedError() def compare_cpu(self, cpu_info): + """Compares given cpu info against host + + Before attempting to migrate a VM to this host, + compare_cpu is called to ensure that the VM will + actually run here. + + :param cpu_info: (str) JSON structure describing the source CPU. + :returns: None if migration is acceptable + :raises: :py:class:`~nova.exception.InvalidCPUInfo` if migration + is not acceptable. + """ raise NotImplementedError() def migrate_disk_and_power_off(self, instance, dest): @@ -229,7 +249,11 @@ class ComputeDriver(object): def finish_migration(self, context, instance, disk_info, network_info, resize_instance): - """Completes a resize, turning on the migrated instance""" + """Completes a resize, turning on the migrated instance + + :param network_info: + :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` + """ raise NotImplementedError() def revert_migration(self, instance): @@ -283,15 +307,15 @@ class ComputeDriver(object): post_method, recover_method): """Spawning live_migration operation for distributing high-load. - :params ctxt: security context - :params instance_ref: + :param ctxt: security context + :param instance_ref: nova.db.sqlalchemy.models.Instance object instance object that is migrated. - :params dest: destination host - :params post_method: + :param dest: destination host + :param post_method: post operation method. expected nova.compute.manager.post_live_migration. - :params recover_method: + :param recover_method: recovery method when any exception occurs. expected nova.compute.manager.recover_live_migration. @@ -541,3 +565,4 @@ class ComputeDriver(object): Note that this function takes an instance ID. """ raise NotImplementedError() + -- cgit From d1f5d1fae10bb8a1d0e445ec2b9153542eb025f4 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 19 Aug 2011 13:54:18 +0200 Subject: pep8 --- nova/virt/driver.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 6d3af7e0a..93290aba7 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -565,4 +565,3 @@ class ComputeDriver(object): Note that this function takes an instance ID. """ raise NotImplementedError() - -- cgit From 83d4c5b9b1f7ed9b75ae04464423b7ca4b5d627d Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 19 Aug 2011 08:08:23 -0700 Subject: Fix config_drive migration, per Matt Dietz. --- .../versions/037_add_config_drive_to_instances.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py index 65ea012dd..36a6af16f 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py @@ -23,20 +23,15 @@ meta = MetaData() instances = Table("instances", meta, Column("id", Integer(), primary_key=True, nullable=False)) -config_drive_column = Column("config_drive", String(255)) # matches image_ref + +# matches the size of an image_ref +config_drive_column = Column("config_drive", String(255), nullable=True) def upgrade(migrate_engine): meta.bind = migrate_engine instances.create_column(config_drive_column) - rows = migrate_engine.execute(instances.select()) - for row in rows: - instance_config_drive = None # pre-existing instances don't have one. - migrate_engine.execute(instances.update()\ - .where(instances.c.id == row[0])\ - .values(config_drive=instance_config_drive)) - def downgrade(migrate_engine): meta.bind = migrate_engine -- cgit From 276403dcb6a8c7802c456b88f8dad249b7513e64 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 19 Aug 2011 08:16:17 -0700 Subject: Define FLAGS.default_local_format. By default it's None, to match current expected _create_local --- nova/virt/libvirt/connection.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova') diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 1b48fd795..a715da66e 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -126,6 +126,10 @@ flags.DEFINE_string('libvirt_vif_type', 'bridge', flags.DEFINE_string('libvirt_vif_driver', 'nova.virt.libvirt.vif.LibvirtBridgeDriver', 'The libvirt VIF driver to configure the VIFs.') +flags.DEFINE_string('default_local_format', + None, + 'The default format a local_volume will be formatted with ' + 'on creation.') def get_connection(read_only): -- cgit From c4fc9f0737ec9f8d5c950b850fed9930a68164f4 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 19 Aug 2011 08:44:14 -0700 Subject: Add copyright notices --- nova/api/openstack/create_instance_helper.py | 1 + nova/api/openstack/views/servers.py | 1 + nova/compute/api.py | 1 + .../migrate_repo/versions/037_add_config_drive_to_instances.py | 4 ++-- nova/db/sqlalchemy/models.py | 1 + nova/scheduler/simple.py | 1 - nova/tests/api/openstack/test_servers.py | 1 + nova/tests/test_compute.py | 1 + nova/virt/libvirt/connection.py | 1 + nova/virt/xenapi/vm_utils.py | 1 + 10 files changed, 10 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index d776ae92d..563ef1c42 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -1,4 +1,5 @@ # Copyright 2011 OpenStack LLC. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index c7bc03bcb..19acb0899 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010-2011 OpenStack LLC. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/compute/api.py b/nova/compute/api.py index e42a5bbad..ccf29bd1a 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -2,6 +2,7 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py index 36a6af16f..d3058f00d 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_config_drive_to_instances.py @@ -1,6 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. +# +# Copyright 2011 Piston Cloud Computing, Inc. # # 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 diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 8a6e2f673..c454cfcc3 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -2,6 +2,7 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/scheduler/simple.py b/nova/scheduler/simple.py index 61c76b35d..fc1b3142a 100644 --- a/nova/scheduler/simple.py +++ b/nova/scheduler/simple.py @@ -41,7 +41,6 @@ class SimpleScheduler(chance.ChanceScheduler): def _schedule_instance(self, context, instance_id, *_args, **_kwargs): """Picks a host that is up and has the fewest running instances.""" - instance_ref = db.instance_get(context, instance_id) if (instance_ref['availability_zone'] and ':' in instance_ref['availability_zone'] diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 7d8b222cc..0a46c3fd1 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010-2011 OpenStack LLC. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 227b42fd7..8d1b95f74 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -2,6 +2,7 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index a715da66e..ea47fa99d 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -4,6 +4,7 @@ # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # Copyright (c) 2010 Citrix Systems, Inc. +# Copyright (c) 2011 Piston Cloud Computing, Inc # # 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 diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 206f522c9..3861f6bd8 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2010 Citrix Systems, Inc. +# Copyright 2011 Piston Cloud Computing, Inc. # # 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 -- cgit From 25ec08a208474e1cf5827c1eb8b231d631c489e9 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 19 Aug 2011 20:09:31 +0200 Subject: Revert irrelevant changes that accidentally crept into this patch :( --- nova/virt/fake.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 67cc1ea29..13b7aeab5 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -152,11 +152,9 @@ class FakeConnection(driver.ComputeDriver): (key, self.instances)) def attach_volume(self, instance_name, device_path, mountpoint): - self._mounts[mountpoint] = device_path return True def detach_volume(self, instance_name, mountpoint): - del self._mounts[mountpoint] return True def get_info(self, instance_name): -- cgit From c06bbe99734f2fea35cfb4bdd854814c9119b617 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Fri, 19 Aug 2011 12:30:55 -0700 Subject: Added monkey patching notification code function w --- nova/flags.py | 8 ++++++++ nova/notifier/api.py | 18 ++++++++++++++++++ nova/utils.py | 24 +++++++++++++++++++++++- 3 files changed, 49 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/flags.py b/nova/flags.py index 48d5e8168..09c30c7c0 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -402,3 +402,11 @@ DEFINE_bool('resume_guests_state_on_host_boot', False, DEFINE_string('root_helper', 'sudo', 'Command prefix to use for running commands as root') + +DEFINE_bool('monkey_patch', False, + 'Whether to monkey patch') + +DEFINE_list('monkey_patch_modules', + ['nova.api.ec2.cloud:nova.notifier.api.notify_decorator', + 'nova.compute.api:nova.notifier.api.notify_decorator'], + 'Module list representng monkey patched module and decorator') diff --git a/nova/notifier/api.py b/nova/notifier/api.py index e18f3e280..ca0ff60a4 100644 --- a/nova/notifier/api.py +++ b/nova/notifier/api.py @@ -39,6 +39,24 @@ class BadPriorityException(Exception): pass +def notify_decorator(name, fn): + def wrapped_func(*args, **kwarg): + body = {} + body['args'] = [] + body['kwarg'] = {} + for arg in args: + body['args'].append(arg) + for key in kwarg: + body['kwarg'][key] = kwarg[key] + LOG.debug("Notify Decorator: %s %r" % (name, body)) + notify(FLAGS.host, + name, + DEBUG, + body) + fn(*args, **kwarg) + return wrapped_func + + def publisher_id(service, host=None): if not host: host = FLAGS.host diff --git a/nova/utils.py b/nova/utils.py index 7276b6bd5..8024a3517 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -35,6 +35,7 @@ import sys import time import types import uuid +import pyclbr from xml.sax import saxutils from eventlet import event @@ -634,7 +635,7 @@ def synchronized(name, external=False): Different methods can share the same lock: @synchronized('mylock') - def foo(self, *args): + Gdef foo(self, *args): ... @synchronized('mylock') @@ -873,3 +874,24 @@ class Bootstrapper(object): for key in FLAGS: value = FLAGS.get(key, None) logging.audit(_("%(key)s : %(value)s" % locals())) + + +def monkey_patch(): + if not FLAGS.monkey_patch: + return + for module_and_decorator in FLAGS.monkey_patch_modules: + module, decorator_name = module_and_decorator.split(':') + decorator = import_class(decorator_name) + __import__(module) + module_data = pyclbr.readmodule_ex(module) + for key in module_data.keys(): + if isinstance(module_data[key], pyclbr.Class): + clz = import_class("%s.%s" % (module, key)) + for method, func in inspect.getmembers(clz, inspect.ismethod): + setattr(clz, method,\ + decorator("%s.%s" % (module, key), func)) + if isinstance(module_data[key], pyclbr.Function): + func = import_class("%s.%s" % (module, key)) + setattr(sys.modules[module], key,\ + setattr(sys.modules[module], key, \ + decorator("%s.%s" % (module, key), func))) -- cgit From 75eb485c3a0e53380b9247d45e2a66159928dcd2 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Fri, 19 Aug 2011 14:04:58 -0700 Subject: Fixed NoneType returned bugw --- nova/notifier/api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/notifier/api.py b/nova/notifier/api.py index ca0ff60a4..99b8b0102 100644 --- a/nova/notifier/api.py +++ b/nova/notifier/api.py @@ -48,12 +48,11 @@ def notify_decorator(name, fn): body['args'].append(arg) for key in kwarg: body['kwarg'][key] = kwarg[key] - LOG.debug("Notify Decorator: %s %r" % (name, body)) notify(FLAGS.host, name, DEBUG, body) - fn(*args, **kwarg) + return fn(*args, **kwarg) return wrapped_func -- cgit From 27723b95f7b3a64226ebe431e17cbf681b40303b Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Fri, 19 Aug 2011 14:13:39 -0700 Subject: Fixed typo --- nova/flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/flags.py b/nova/flags.py index 09c30c7c0..4e6e3ad40 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -409,4 +409,4 @@ DEFINE_bool('monkey_patch', False, DEFINE_list('monkey_patch_modules', ['nova.api.ec2.cloud:nova.notifier.api.notify_decorator', 'nova.compute.api:nova.notifier.api.notify_decorator'], - 'Module list representng monkey patched module and decorator') + 'Module list representing monkey patched module and decorator') -- cgit From e10aa40bd6c2f96b2f5bba8b38b9605f019328e9 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Fri, 19 Aug 2011 14:22:53 -0700 Subject: Fixed typo --- nova/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/utils.py b/nova/utils.py index 171035068..d7e14b1b0 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -636,7 +636,7 @@ def synchronized(name, external=False): Different methods can share the same lock: @synchronized('mylock') - Gdef foo(self, *args): + def foo(self, *args): ... @synchronized('mylock') -- cgit From 34e310eff24b96bcc27df176bfecbd02ac863e7c Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Sun, 21 Aug 2011 22:59:46 -0400 Subject: fixing bug lp:830817 --- nova/api/openstack/views/addresses.py | 26 +++++++++++++++++++++++--- nova/api/openstack/wsgi.py | 6 +++--- nova/tests/api/openstack/test_servers.py | 21 +++++++++++++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py index ddbf7a144..05028db41 100644 --- a/nova/api/openstack/views/addresses.py +++ b/nova/api/openstack/views/addresses.py @@ -15,11 +15,15 @@ # License for the specific language governing permissions and limitations # under the License. +import traceback + from nova import flags from nova import utils +from nova import log as logging from nova.api.openstack import common FLAGS = flags.FLAGS +LOG = logging.getLogger('nova.api.openstack.views.addresses') class ViewBuilder(object): @@ -48,7 +52,11 @@ class ViewBuilderV11(ViewBuilder): def build(self, interfaces): networks = {} for interface in interfaces: - network_label = interface['network']['label'] + try: + network_label = self._extract_network_label(interface) + except TypeError: + LOG.error(traceback.format_exc()) + continue if network_label not in networks: networks[network_label] = [] @@ -64,9 +72,14 @@ class ViewBuilderV11(ViewBuilder): return networks - def build_network(self, interfaces, network_label): + def build_network(self, interfaces, requested_network): for interface in interfaces: - if interface['network']['label'] == network_label: + try: + network_label = self._extract_network_label(interface) + except TypeError: + continue + + if network_label == requested_network: ips = list(self._extract_ipv4_addresses(interface)) ipv6 = self._extract_ipv6_address(interface) if ipv6 is not None: @@ -74,6 +87,13 @@ class ViewBuilderV11(ViewBuilder): return {network_label: ips} return None + def _extract_network_label(self, interface): + try: + return interface['network']['label'] + except (TypeError, KeyError): + LOG.error(traceback.format_exc()) + raise TypeError + def _extract_ipv4_addresses(self, interface): for fixed_ip in interface['fixed_ips']: yield self._build_ip_entity(fixed_ip['address'], 4) diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index 0eb47044e..c2185ebfd 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -516,6 +516,6 @@ class Resource(wsgi.Application): controller_method = getattr(self.controller, action) try: return controller_method(req=request, **action_args) - except TypeError, exc: - LOG.debug(str(exc)) - return webob.exc.HTTPBadRequest() + except TypeError: + LOG.debug(traceback.format_exc()) + return faults.Fault(webob.exc.HTTPBadRequest()) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 437620854..8167c4ace 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -752,6 +752,27 @@ class ServersTest(test.TestCase): (ip,) = private_node.getElementsByTagName('ip') self.assertEquals(ip.getAttribute('addr'), private) + # NOTE(bcwaldon): lp830817 + def test_get_server_by_id_malformed_networks_v1_1(self): + ifaces = [ + { + 'network': None, + 'fixed_ips': [ + {'address': '192.168.0.3'}, + {'address': '192.168.0.4'}, + ], + }, + ] + new_return_server = return_server_with_attributes(interfaces=ifaces) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + + req = webob.Request.blank('/v1.1/servers/1') + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) + self.assertEqual(res_dict['server']['id'], 1) + self.assertEqual(res_dict['server']['name'], 'server1') + def test_get_server_by_id_with_addresses_v1_1(self): self.flags(use_ipv6=True) interfaces = [ -- cgit From 9fc23f1055be435e8a21b999f748a8461552bd13 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Sun, 21 Aug 2011 23:16:26 -0400 Subject: adding import --- nova/api/openstack/wsgi.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova') diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index c2185ebfd..572aba993 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -1,5 +1,6 @@ import json +import traceback import webob from xml.dom import minidom from xml.parsers import expat -- cgit From 269b4e00e82b8f99d2fc24f935ff165d62f19891 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Mon, 22 Aug 2011 16:23:48 -0400 Subject: logging as exception rather than error --- nova/api/openstack/views/addresses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py index 05028db41..d54013d61 100644 --- a/nova/api/openstack/views/addresses.py +++ b/nova/api/openstack/views/addresses.py @@ -55,7 +55,7 @@ class ViewBuilderV11(ViewBuilder): try: network_label = self._extract_network_label(interface) except TypeError: - LOG.error(traceback.format_exc()) + LOG.exception(traceback.format_exc()) continue if network_label not in networks: @@ -91,7 +91,7 @@ class ViewBuilderV11(ViewBuilder): try: return interface['network']['label'] except (TypeError, KeyError): - LOG.error(traceback.format_exc()) + LOG.exception(traceback.format_exc()) raise TypeError def _extract_ipv4_addresses(self, interface): -- cgit From 77f15157c5ca7013df397abc22a8866cce02976d Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Mon, 22 Aug 2011 17:08:11 -0400 Subject: Ensure that reserve and unreserve exit when an address is not found --- nova/tests/test_nova_manage.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'nova') diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 3a6e48dec..f5ea68a03 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -55,8 +55,18 @@ class FixedIpCommandsTestCase(test.TestCase): '192.168.0.100') self.assertEqual(address['reserved'], True) + def test_reserve_nonexistent_address(self): + self.assertRaises(SystemExit, + self.commands.reserve, + '55.55.55.55') + def test_unreserve(self): self.commands.unreserve('192.168.0.100') address = db.fixed_ip_get_by_address(context.get_admin_context(), '192.168.0.100') self.assertEqual(address['reserved'], False) + + def test_unreserve_nonexistent_address(self): + self.assertRaises(SystemExit, + self.commands.unreserve, + '55.55.55.55') -- cgit From 5a288485215a13f3892ae17a46b9644ed84fc089 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Mon, 22 Aug 2011 14:24:37 -0700 Subject: Added Test Code, doc string, and fixed pip-requiresw --- nova/flags.py | 5 +++-- nova/notifier/api.py | 8 ++++++++ nova/tests/test_notifier.py | 22 ++++++++++++++++++++++ nova/tests/test_utils.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ nova/utils.py | 26 +++++++++++++++++++++++--- 5 files changed, 100 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/flags.py b/nova/flags.py index 4e6e3ad40..e9bf6e334 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -404,9 +404,10 @@ DEFINE_string('root_helper', 'sudo', 'Command prefix to use for running commands as root') DEFINE_bool('monkey_patch', False, - 'Whether to monkey patch') + 'Whether to log monkey patching') DEFINE_list('monkey_patch_modules', ['nova.api.ec2.cloud:nova.notifier.api.notify_decorator', 'nova.compute.api:nova.notifier.api.notify_decorator'], - 'Module list representing monkey patched module and decorator') + 'Module list representing monkey\ + patched module and decorator') diff --git a/nova/notifier/api.py b/nova/notifier/api.py index 99b8b0102..a98f17dbe 100644 --- a/nova/notifier/api.py +++ b/nova/notifier/api.py @@ -40,6 +40,14 @@ class BadPriorityException(Exception): def notify_decorator(name, fn): + """ decorator for notify which is used from utils.monkey_patch() + Parameters: + + name - name of the function + function - object of the function + + + """ def wrapped_func(*args, **kwarg): body = {} body['args'] = [] diff --git a/nova/tests/test_notifier.py b/nova/tests/test_notifier.py index 64b799a2c..ab5dfb692 100644 --- a/nova/tests/test_notifier.py +++ b/nova/tests/test_notifier.py @@ -134,3 +134,25 @@ class NotifierTestCase(test.TestCase): self.assertEqual(msg['event_type'], 'error_notification') self.assertEqual(msg['priority'], 'ERROR') self.assertEqual(msg['payload']['error'], 'foo') + + def test_send_notification_by_decorator(self): + self.notify_called = False + + def example_api(arg1, arg2): + return arg1 + arg2 + + example_api =\ + nova.notifier.api.notify_decorator( + 'example_api', + example_api) + + def mock_notify(cls, *args): + self.notify_called = True + + self.stubs.Set(nova.notifier.no_op_notifier, 'notify', + mock_notify) + + class Mock(object): + pass + self.assertEqual(3, example_api(1, 2)) + self.assertEqual(self.notify_called, True) diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py index ec5098a37..317ec46d0 100644 --- a/nova/tests/test_utils.py +++ b/nova/tests/test_utils.py @@ -18,6 +18,7 @@ import datetime import os import tempfile +import nova from nova import exception from nova import test from nova import utils @@ -384,3 +385,46 @@ class ToPrimitiveTestCase(test.TestCase): def test_typeerror(self): x = bytearray # Class, not instance self.assertEquals(utils.to_primitive(x), u"") + + +class MonkeyPatchTestCase(test.TestCase): + """Unit test for utils.monkey_patch().""" + def setUp(self): + super(MonkeyPatchTestCase, self).setUp() + self.flags( + monkey_patch=True, + monkey_patch_modules=['nova.tests.example.example_a' + ':' + + 'nova.tests.example.example_decorator']) + + def test_monkey_patch(self): + utils.monkey_patch() + nova.tests.example.CALLED_FUNCTION = [] + from nova.tests.example import example_a, example_b + + self.assertEqual('Example function', example_a.example_function_a()) + exampleA = example_a.ExampleClassA() + exampleA.example_method() + ret_a = exampleA.example_method_add(3, 5) + self.assertEqual(ret_a, 8) + + self.assertEqual('Example function', example_b.example_function_b()) + exampleB = example_b.ExampleClassB() + exampleB.example_method() + ret_b = exampleB.example_method_add(3, 5) + + self.assertEqual(ret_b, 8) + package_a = 'nova.tests.example.example_a.' + self.assertTrue(package_a + 'example_function_a' + in nova.tests.example.CALLED_FUNCTION) + + self.assertTrue(package_a + 'ExampleClassA.example_method' + in nova.tests.example.CALLED_FUNCTION) + self.assertTrue(package_a + 'ExampleClassA.example_method_add' + in nova.tests.example.CALLED_FUNCTION) + package_b = 'nova.tests.example.example_b.' + self.assertFalse(package_b + 'example_function_b' + in nova.tests.example.CALLED_FUNCTION) + self.assertFalse(package_b + 'ExampleClassB.example_method' + in nova.tests.example.CALLED_FUNCTION) + self.assertFalse(package_b + 'ExampleClassB.example_method_add' + in nova.tests.example.CALLED_FUNCTION) diff --git a/nova/utils.py b/nova/utils.py index d7e14b1b0..be2ba68f9 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -842,21 +842,41 @@ def bool_from_str(val): def monkey_patch(): + """ If the Flags.monkey_patch set as True, + this functuion patches a decorator + for all functions in specified modules. + You can set decorators for each modules + using FLAGS.monkey_patch_modules. + The format is "Module path:Decorator function". + Example: 'nova.api.ec2.cloud:nova.notifier.api.notify_decorator' + + Parameters of the decorator is as follows. + (See nova.notifier.api.notify_decorator) + + name - name of the function + function - object of the function + """ + + # If FLAGS.monkey_patch is not True, this function do nothing. if not FLAGS.monkey_patch: return + # Get list of moudles and decorators for module_and_decorator in FLAGS.monkey_patch_modules: module, decorator_name = module_and_decorator.split(':') + # import decorator function decorator = import_class(decorator_name) __import__(module) + # Retrive module information using pyclbr module_data = pyclbr.readmodule_ex(module) for key in module_data.keys(): + # set the decorator for the class methods if isinstance(module_data[key], pyclbr.Class): clz = import_class("%s.%s" % (module, key)) for method, func in inspect.getmembers(clz, inspect.ismethod): setattr(clz, method,\ - decorator("%s.%s" % (module, key), func)) + decorator("%s.%s.%s" % (module, key, method), func)) + # set the decorator for the function if isinstance(module_data[key], pyclbr.Function): func = import_class("%s.%s" % (module, key)) setattr(sys.modules[module], key,\ - setattr(sys.modules[module], key, \ - decorator("%s.%s" % (module, key), func))) + decorator("%s.%s" % (module, key), func)) -- cgit From c3ed01d7d53dbade412122743078d60131adbf9f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 22 Aug 2011 14:24:59 -0700 Subject: change NoAuth to actually use a tenant and user --- nova/api/auth.py | 19 +---------------- nova/api/ec2/__init__.py | 21 +++++++++++++++++++ nova/api/openstack/auth.py | 52 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 31 deletions(-) (limited to 'nova') diff --git a/nova/api/auth.py b/nova/api/auth.py index 050216fd7..cd0d38b3f 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -45,24 +45,6 @@ class InjectContext(wsgi.Middleware): return self.application -class AdminContext(wsgi.Middleware): - """Return an admin context no matter what""" - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - # Build a context, including the auth_token... - remote_address = req.remote_addr - if FLAGS.use_forwarded_for: - remote_address = req.headers.get('X-Forwarded-For', remote_address) - ctx = context.RequestContext('admin', - 'admin', - is_admin=True, - remote_address=remote_address) - - req.environ['nova.context'] = ctx - return self.application - - class KeystoneContext(wsgi.Middleware): """Make a request context from keystone headers""" @@ -80,6 +62,7 @@ class KeystoneContext(wsgi.Middleware): req.headers.get('X_STORAGE_TOKEN')) # Build a context, including the auth_token... + remote_address = getattr(req, 'remote_address', '127.0.0.1') remote_address = req.remote_addr if FLAGS.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 17969099d..5430f443d 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -183,6 +183,27 @@ class ToToken(wsgi.Middleware): return self.application +class NoAuth(wsgi.Middleware): + """Add user:project as 'nova.context' to WSGI environ.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + if 'AWSAccessKeyId' not in req.params: + raise webob.exc.HTTPBadRequest() + user_id, _sep, project_id = req.params['AWSAccessKeyId'].partition(':') + project_id = project_id or user_id + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application + + class Authenticate(wsgi.Middleware): """Authenticate an EC2 request and add 'nova.context' to WSGI environ.""" diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 0d9c7562a..f2dc89094 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -33,6 +33,7 @@ from nova.api.openstack import faults LOG = logging.getLogger('nova.api.openstack') FLAGS = flags.FLAGS +flags.DECLARE('use_forwarded_for', 'nova.api.auth') class NoAuthMiddleware(wsgi.Middleware): @@ -40,17 +41,36 @@ class NoAuthMiddleware(wsgi.Middleware): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): - if 'X-Auth-Token' in req.headers: + if 'X-Auth-Token' not in req.headers: + os_url = req.url + version = common.get_version_from_href(os_url) + user_id = req.headers.get('X-Auth-User', 'admin') + project_id = req.headers.get('X-Auth-Project-Id', 'admin') + if version == '1.1': + os_url += '/' + project_id + res = webob.Response() + res.headers['X-Auth-Token'] = '%s:%s' % (user_id, project_id) + res.headers['X-Server-Management-Url'] = os_url + res.headers['X-Storage-Url'] = '' + res.headers['X-CDN-Management-Url'] = '' + res.content_type = 'text/plain' + res.status = '204' + return res + else: + token = req.headers['X-Auth-Token'] + user_id, _sep, project_id = token.partition(':') + project_id = project_id or user_id + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', + remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx return self.application - logging.debug("Got no auth token, returning fake info.") - res = webob.Response() - res.headers['X-Auth-Token'] = 'fake' - res.headers['X-Server-Management-Url'] = req.url - res.headers['X-Storage-Url'] = '' - res.headers['X-CDN-Management-Url'] = '' - res.content_type = 'text/plain' - res.status = '204' - return res class AuthMiddleware(wsgi.Middleware): @@ -103,9 +123,15 @@ class AuthMiddleware(wsgi.Middleware): project_id = projects[0].id is_admin = self.auth.is_admin(user_id) - req.environ['nova.context'] = context.RequestContext(user_id, - project_id, - is_admin) + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=is_admin, + remote_address=remote_address) + req.environ['nova.context'] = ctx + if not is_admin and not self.auth.is_project_member(user_id, project_id): msg = _("%(user_id)s must be an admin or a " -- cgit From 43add36446e6b4172dc8ed5043e11187a9992474 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 22 Aug 2011 14:26:41 -0700 Subject: fix comment --- nova/auth/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/auth/manager.py b/nova/auth/manager.py index c9178c0dd..85227bea0 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -18,7 +18,7 @@ """ WARNING: This code is deprecated and will be removed. -Keystone is recommended is the recommended solution for auth management. +Keystone is the recommended solution for auth management. Nova authentication management """ -- cgit From d994b06f65af9d4c523a4123f915c6147ada7c05 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Mon, 22 Aug 2011 22:00:13 -0400 Subject: fixing exception logging --- nova/api/openstack/views/addresses.py | 9 +++------ nova/api/openstack/wsgi.py | 5 ++--- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py index d54013d61..8fccc690f 100644 --- a/nova/api/openstack/views/addresses.py +++ b/nova/api/openstack/views/addresses.py @@ -15,8 +15,6 @@ # License for the specific language governing permissions and limitations # under the License. -import traceback - from nova import flags from nova import utils from nova import log as logging @@ -54,8 +52,7 @@ class ViewBuilderV11(ViewBuilder): for interface in interfaces: try: network_label = self._extract_network_label(interface) - except TypeError: - LOG.exception(traceback.format_exc()) + except TypeError as exc: continue if network_label not in networks: @@ -90,8 +87,8 @@ class ViewBuilderV11(ViewBuilder): def _extract_network_label(self, interface): try: return interface['network']['label'] - except (TypeError, KeyError): - LOG.exception(traceback.format_exc()) + except (TypeError, KeyError) as exc: + LOG.exception(exc) raise TypeError def _extract_ipv4_addresses(self, interface): diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index 572aba993..3616c9ec6 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -1,6 +1,5 @@ import json -import traceback import webob from xml.dom import minidom from xml.parsers import expat @@ -517,6 +516,6 @@ class Resource(wsgi.Application): controller_method = getattr(self.controller, action) try: return controller_method(req=request, **action_args) - except TypeError: - LOG.debug(traceback.format_exc()) + except TypeError as exc: + LOG.exception(exc) return faults.Fault(webob.exc.HTTPBadRequest()) -- cgit From 6f3610042452cc1cb6b1e0c204a127c0c48794f0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 22 Aug 2011 19:25:22 -0700 Subject: unindented per review, added a note about auth v2 --- nova/api/openstack/auth.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index f2dc89094..6754fea27 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -49,6 +49,9 @@ class NoAuthMiddleware(wsgi.Middleware): if version == '1.1': os_url += '/' + project_id res = webob.Response() + # NOTE(vish): This is expecting and returning Auth(1.1), whereas + # keystone uses 2.0 auth. We should probably allow + # 2.0 auth here as well. res.headers['X-Auth-Token'] = '%s:%s' % (user_id, project_id) res.headers['X-Server-Management-Url'] = os_url res.headers['X-Storage-Url'] = '' @@ -56,21 +59,20 @@ class NoAuthMiddleware(wsgi.Middleware): res.content_type = 'text/plain' res.status = '204' return res - else: - token = req.headers['X-Auth-Token'] - user_id, _sep, project_id = token.partition(':') - project_id = project_id or user_id - remote_address = getattr(req, 'remote_address', '127.0.0.1') - if FLAGS.use_forwarded_for: - remote_address = req.headers.get('X-Forwarded-For', - remote_address) - ctx = context.RequestContext(user_id, - project_id, - is_admin=True, - remote_address=remote_address) - - req.environ['nova.context'] = ctx - return self.application + + token = req.headers['X-Auth-Token'] + user_id, _sep, project_id = token.partition(':') + project_id = project_id or user_id + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application class AuthMiddleware(wsgi.Middleware): -- cgit From 5ad22e341e0ad5ff62e97906edf7822ee53b4ae9 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Mon, 22 Aug 2011 23:30:12 -0400 Subject: removing unnecessary tthing --- nova/api/openstack/views/addresses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py index 8fccc690f..8f07a2289 100644 --- a/nova/api/openstack/views/addresses.py +++ b/nova/api/openstack/views/addresses.py @@ -52,7 +52,7 @@ class ViewBuilderV11(ViewBuilder): for interface in interfaces: try: network_label = self._extract_network_label(interface) - except TypeError as exc: + except TypeError: continue if network_label not in networks: -- cgit From 4c2674516897b6cce0441efe4ebb005c01cb3411 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Mon, 22 Aug 2011 21:06:47 -0700 Subject: Added the fixes suggested by Eric Windisch from cloudscaling... --- nova/virt/disk.py | 2 +- nova/virt/xenapi/vm_utils.py | 2 +- nova/virt/xenapi/vmops.py | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 54b191fa9..809d3323c 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -228,7 +228,7 @@ def _inject_metadata_into_fs(metadata, fs, execute=None): metadata_path = os.path.join(fs, "meta.js") metadata = dict([(m.key, m.value) for m in metadata]) - utils.execute('sudo', 'tee', '-a', metadata_path, + utils.execute('sudo', 'tee', metadata_path, process_input=json.dumps(metadata)) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 3861f6bd8..e517dcf28 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -641,7 +641,7 @@ class VMHelper(HelperBase): # everything mount_required = False key, net, metadata = _prepare_injectables(instance, network_info) - mount_required = key or net + mount_required = key or net or metadata if not mount_required: return diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index b1522729a..606041d12 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -251,8 +251,9 @@ class VMOps(object): bootable=False) userdevice += 1 - # Alter the image before VM start for, e.g. network injection - if FLAGS.flat_injected: + # Alter the image before VM start for, e.g. network injection also + # alter the image if there's metadata. + if FLAGS.flat_injected or instance['metadata']: VMHelper.preconfigure_instance(self._session, instance, first_vdi_ref, network_info) -- cgit From 7f1adb50cfab91a553f2d129b9b2eef1e5b2145b Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Mon, 22 Aug 2011 22:17:51 -0700 Subject: Moved migration and fixed tests from upstream --- nova/compute/api.py | 2 +- .../versions/041_add_config_drive_to_instances.py | 38 ++++++++++++++++++++++ nova/tests/api/openstack/test_servers.py | 28 +++++++++------- nova/tests/test_compute.py | 2 +- nova/virt/disk.py | 2 +- nova/virt/libvirt/connection.py | 7 ++-- nova/virt/xenapi/vm_utils.py | 2 +- 7 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 74149f17d..69f76bf40 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -526,7 +526,7 @@ class API(base.Base): availability_zone, user_data, metadata, injected_files, admin_password, zone_blob, reservation_id, access_ip_v4, access_ip_v6, - requested_networks, config_drive + requested_networks, config_drive) block_device_mapping = block_device_mapping or [] instances = [] diff --git a/nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py new file mode 100644 index 000000000..d3058f00d --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/041_add_config_drive_to_instances.py @@ -0,0 +1,38 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 Piston Cloud Computing, Inc. +# +# 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 sqlalchemy import Column, Integer, MetaData, String, Table + +from nova import utils + + +meta = MetaData() + +instances = Table("instances", meta, + Column("id", Integer(), primary_key=True, nullable=False)) + +# matches the size of an image_ref +config_drive_column = Column("config_drive", String(255), nullable=True) + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + instances.create_column(config_drive_column) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + instances.drop_column(config_drive_column) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index f854a500c..aec2ad947 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1776,8 +1776,8 @@ class ServersTest(test.TestCase): self.config_drive = True self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { 'name': 'config_drive_test', @@ -1792,12 +1792,13 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/123/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) + print res self.assertEqual(res.status_int, 202) server = json.loads(res.body)['server'] self.assertEqual(1, server['id']) @@ -1807,8 +1808,8 @@ class ServersTest(test.TestCase): self.config_drive = 2 self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { 'name': 'config_drive_test', @@ -1823,7 +1824,7 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/123/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -1840,8 +1841,8 @@ class ServersTest(test.TestCase): self.config_drive = "asdf" self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { 'name': 'config_drive_test', @@ -1856,7 +1857,7 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/123/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -1867,8 +1868,8 @@ class ServersTest(test.TestCase): def test_create_instance_without_config_drive_v1_1(self): self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' body = { 'server': { 'name': 'config_drive_test', @@ -1883,7 +1884,7 @@ class ServersTest(test.TestCase): }, } - req = webob.Request.blank('/v1.1/servers') + req = webob.Request.blank('/v1.1/123/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" @@ -3588,6 +3589,7 @@ class ServersViewBuilderV11Test(test.TestCase): "id": 1, "uuid": self.instance['uuid'], "name": "test_server", + "config_drive": None, "links": [ { "rel": "self", @@ -3747,6 +3749,7 @@ class ServersViewBuilderV11Test(test.TestCase): }, "addresses": {}, "metadata": {}, + "config_drive": None, "accessIPv4": "1.2.3.4", "accessIPv6": "", "links": [ @@ -3801,6 +3804,7 @@ class ServersViewBuilderV11Test(test.TestCase): }, "addresses": {}, "metadata": {}, + "config_drive": None, "accessIPv4": "", "accessIPv6": "fead::1234", "links": [ diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index b75e25dda..0523d73b6 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -163,7 +163,7 @@ class ComputeTestCase(test.TestCase): def test_create_instance_associates_config_drive(self): """Make sure create associates a config drive.""" - instance_id = self._create_instance(params={'config_drive': True,}) + instance_id = self._create_instance(params={'config_drive': True, }) try: self.compute.run_instance(self.context, instance_id) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 809d3323c..52b2881e8 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -228,7 +228,7 @@ def _inject_metadata_into_fs(metadata, fs, execute=None): metadata_path = os.path.join(fs, "meta.js") metadata = dict([(m.key, m.value) for m in metadata]) - utils.execute('sudo', 'tee', metadata_path, + utils.execute('sudo', 'tee', metadata_path, process_input=json.dumps(metadata)) diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 23fa86f65..4388291db 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -131,8 +131,8 @@ flags.DEFINE_string('libvirt_vif_type', 'bridge', flags.DEFINE_string('libvirt_vif_driver', 'nova.virt.libvirt.vif.LibvirtBridgeDriver', 'The libvirt VIF driver to configure the VIFs.') -flags.DEFINE_string('default_local_format', - None, +flags.DEFINE_string('default_local_format', + None, 'The default format a local_volume will be formatted with ' 'on creation.') @@ -970,7 +970,7 @@ class LibvirtConnection(driver.ComputeDriver): for injection in ('metadata', 'key', 'net'): if locals()[injection]: LOG.info(_('instance %(inst_name)s: injecting ' - '%(injection)s into image %(img_id)s' + '%(injection)s into image %(img_id)s' % locals())) try: disk.inject_data(injection_path, key, net, metadata, @@ -1106,7 +1106,6 @@ class LibvirtConnection(driver.ComputeDriver): block_device_info)): xml_info['swap_device'] = self.default_swap_device - config_drive = False if instance.get('config_drive') or instance.get('config_drive_id'): xml_info['config_drive'] = xml_info['basepath'] + "/disk.config" diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 18fe84e6c..efbea7076 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -741,7 +741,7 @@ class VMHelper(HelperBase): # if at all, so determine whether it's required first, and then do # everything mount_required = False - key, net, metadata = _prepare_injectables(instance, network_info) + key, net, metadata = _prepare_injectables(instance, network_info) mount_required = key or net or metadata if not mount_required: return -- cgit From 909e0ea5c61ba66e5c07b91ff225d64adf60f960 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 23 Aug 2011 10:21:07 -0400 Subject: Move use_ipv6 into flags. Its used in multiple places (network manager and the OSAPI) and should be defined at the top level. --- nova/flags.py | 2 ++ nova/network/manager.py | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/flags.py b/nova/flags.py index 48d5e8168..f822ae61a 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -402,3 +402,5 @@ DEFINE_bool('resume_guests_state_on_host_boot', False, DEFINE_string('root_helper', 'sudo', 'Command prefix to use for running commands as root') + +DEFINE_bool('use_ipv6', False, 'use the ipv6') diff --git a/nova/network/manager.py b/nova/network/manager.py index aa2a3700c..404a3180e 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -106,8 +106,6 @@ flags.DEFINE_integer('create_unique_mac_address_attempts', 5, 'Number of attempts to create unique mac address') flags.DEFINE_bool('auto_assign_floating_ip', False, 'Autoassigning floating ip to VM') -flags.DEFINE_bool('use_ipv6', False, - 'use the ipv6') flags.DEFINE_string('network_host', socket.gethostname(), 'Network host to use for ip allocation in flat modes') flags.DEFINE_bool('fake_call', False, -- cgit From e1c27761863a50bf33a2dcfffa96e911ae9b5b55 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 23 Aug 2011 10:31:34 -0400 Subject: 'use the ipv6' -- 'use ipv6' --- nova/flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/flags.py b/nova/flags.py index f822ae61a..ce5356723 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -403,4 +403,4 @@ DEFINE_bool('resume_guests_state_on_host_boot', False, DEFINE_string('root_helper', 'sudo', 'Command prefix to use for running commands as root') -DEFINE_bool('use_ipv6', False, 'use the ipv6') +DEFINE_bool('use_ipv6', False, 'use ipv6') -- cgit From 5ae44219fd82d843cc5e715c318d9e80ab20b1a2 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Tue, 23 Aug 2011 08:07:25 -0700 Subject: Fixed typo and docstring and example class name --- nova/notifier/api.py | 7 +++--- nova/tests/example/__init__.py | 34 ---------------------------- nova/tests/example/example_a.py | 32 -------------------------- nova/tests/example/example_b.py | 32 -------------------------- nova/tests/monkey_patch_example/__init__.py | 34 ++++++++++++++++++++++++++++ nova/tests/monkey_patch_example/example_a.py | 32 ++++++++++++++++++++++++++ nova/tests/monkey_patch_example/example_b.py | 32 ++++++++++++++++++++++++++ nova/tests/test_utils.py | 25 ++++++++++---------- nova/utils.py | 4 ++-- 9 files changed, 116 insertions(+), 116 deletions(-) delete mode 100644 nova/tests/example/__init__.py delete mode 100644 nova/tests/example/example_a.py delete mode 100644 nova/tests/example/example_b.py create mode 100644 nova/tests/monkey_patch_example/__init__.py create mode 100644 nova/tests/monkey_patch_example/example_a.py create mode 100644 nova/tests/monkey_patch_example/example_b.py (limited to 'nova') diff --git a/nova/notifier/api.py b/nova/notifier/api.py index a98f17dbe..f5cf95d2a 100644 --- a/nova/notifier/api.py +++ b/nova/notifier/api.py @@ -41,11 +41,10 @@ class BadPriorityException(Exception): def notify_decorator(name, fn): """ decorator for notify which is used from utils.monkey_patch() - Parameters: - - name - name of the function - function - object of the function + :param name: name of the function + :param function: - object of the function + :returns: function -- decorated function """ def wrapped_func(*args, **kwarg): diff --git a/nova/tests/example/__init__.py b/nova/tests/example/__init__.py deleted file mode 100644 index 1cfdf8a7e..000000000 --- a/nova/tests/example/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# 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. -"""Example Module for testing utils.monkey_patch() -""" - - -CALLED_FUNCTION = [] - - -def example_decorator(name, function): - """ decorator for notify which is used from utils.monkey_patch() - Parameters: - - name - name of the function - function - object of the function - - - """ - def wrapped_func(*args, **kwarg): - CALLED_FUNCTION.append(name) - return function(*args, **kwarg) - return wrapped_func diff --git a/nova/tests/example/example_a.py b/nova/tests/example/example_a.py deleted file mode 100644 index 91bf048e4..000000000 --- a/nova/tests/example/example_a.py +++ /dev/null @@ -1,32 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -"""Example Module A for testing utils.monkey_patch() -""" - - -def example_function_a(): - return 'Example function' - - -class ExampleClassA(): - def example_method(self): - return 'Example method' - - def example_method_add(self, arg1, arg2): - return arg1 + arg2 diff --git a/nova/tests/example/example_b.py b/nova/tests/example/example_b.py deleted file mode 100644 index edd267c4f..000000000 --- a/nova/tests/example/example_b.py +++ /dev/null @@ -1,32 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -"""Example Module B for testing utils.monkey_patch() -""" - - -def example_function_b(): - return 'Example function' - - -class ExampleClassB(): - def example_method(self): - return 'Example method' - - def example_method_add(self, arg1, arg2): - return arg1 + arg2 diff --git a/nova/tests/monkey_patch_example/__init__.py b/nova/tests/monkey_patch_example/__init__.py new file mode 100644 index 000000000..1cfdf8a7e --- /dev/null +++ b/nova/tests/monkey_patch_example/__init__.py @@ -0,0 +1,34 @@ +# Copyright 2011 OpenStack LLC. +# 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. +"""Example Module for testing utils.monkey_patch() +""" + + +CALLED_FUNCTION = [] + + +def example_decorator(name, function): + """ decorator for notify which is used from utils.monkey_patch() + Parameters: + + name - name of the function + function - object of the function + + + """ + def wrapped_func(*args, **kwarg): + CALLED_FUNCTION.append(name) + return function(*args, **kwarg) + return wrapped_func diff --git a/nova/tests/monkey_patch_example/example_a.py b/nova/tests/monkey_patch_example/example_a.py new file mode 100644 index 000000000..91bf048e4 --- /dev/null +++ b/nova/tests/monkey_patch_example/example_a.py @@ -0,0 +1,32 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +"""Example Module A for testing utils.monkey_patch() +""" + + +def example_function_a(): + return 'Example function' + + +class ExampleClassA(): + def example_method(self): + return 'Example method' + + def example_method_add(self, arg1, arg2): + return arg1 + arg2 diff --git a/nova/tests/monkey_patch_example/example_b.py b/nova/tests/monkey_patch_example/example_b.py new file mode 100644 index 000000000..edd267c4f --- /dev/null +++ b/nova/tests/monkey_patch_example/example_b.py @@ -0,0 +1,32 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +"""Example Module B for testing utils.monkey_patch() +""" + + +def example_function_b(): + return 'Example function' + + +class ExampleClassB(): + def example_method(self): + return 'Example method' + + def example_method_add(self, arg1, arg2): + return arg1 + arg2 diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py index f80ffb179..1ba794a1a 100644 --- a/nova/tests/test_utils.py +++ b/nova/tests/test_utils.py @@ -401,15 +401,16 @@ class MonkeyPatchTestCase(test.TestCase): """Unit test for utils.monkey_patch().""" def setUp(self): super(MonkeyPatchTestCase, self).setUp() + self.example_package = 'nova.tests.monkey_patch_example.' self.flags( monkey_patch=True, - monkey_patch_modules=['nova.tests.example.example_a' + ':' - + 'nova.tests.example.example_decorator']) + monkey_patch_modules=[self.example_package + 'example_a' + ':' + + self.example_package + 'example_decorator']) def test_monkey_patch(self): utils.monkey_patch() - nova.tests.example.CALLED_FUNCTION = [] - from nova.tests.example import example_a, example_b + nova.tests.monkey_patch_example.CALLED_FUNCTION = [] + from nova.tests.monkey_patch_example import example_a, example_b self.assertEqual('Example function', example_a.example_function_a()) exampleA = example_a.ExampleClassA() @@ -423,18 +424,18 @@ class MonkeyPatchTestCase(test.TestCase): ret_b = exampleB.example_method_add(3, 5) self.assertEqual(ret_b, 8) - package_a = 'nova.tests.example.example_a.' + package_a = self.example_package + 'example_a.' self.assertTrue(package_a + 'example_function_a' - in nova.tests.example.CALLED_FUNCTION) + in nova.tests.monkey_patch_example.CALLED_FUNCTION) self.assertTrue(package_a + 'ExampleClassA.example_method' - in nova.tests.example.CALLED_FUNCTION) + in nova.tests.monkey_patch_example.CALLED_FUNCTION) self.assertTrue(package_a + 'ExampleClassA.example_method_add' - in nova.tests.example.CALLED_FUNCTION) - package_b = 'nova.tests.example.example_b.' + in nova.tests.monkey_patch_example.CALLED_FUNCTION) + package_b = self.example_package + 'example_b.' self.assertFalse(package_b + 'example_function_b' - in nova.tests.example.CALLED_FUNCTION) + in nova.tests.monkey_patch_example.CALLED_FUNCTION) self.assertFalse(package_b + 'ExampleClassB.example_method' - in nova.tests.example.CALLED_FUNCTION) + in nova.tests.monkey_patch_example.CALLED_FUNCTION) self.assertFalse(package_b + 'ExampleClassB.example_method_add' - in nova.tests.example.CALLED_FUNCTION) + in nova.tests.monkey_patch_example.CALLED_FUNCTION) diff --git a/nova/utils.py b/nova/utils.py index 44a0d4398..edf67384d 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -866,13 +866,13 @@ def monkey_patch(): # If FLAGS.monkey_patch is not True, this function do nothing. if not FLAGS.monkey_patch: return - # Get list of moudles and decorators + # Get list of modules and decorators for module_and_decorator in FLAGS.monkey_patch_modules: module, decorator_name = module_and_decorator.split(':') # import decorator function decorator = import_class(decorator_name) __import__(module) - # Retrive module information using pyclbr + # Retrieve module information using pyclbr module_data = pyclbr.readmodule_ex(module) for key in module_data.keys(): # set the decorator for the class methods -- cgit From f380b65cdce439d440b68b0f4a65be45d13ce453 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Tue, 23 Aug 2011 08:51:44 -0700 Subject: Removed blank line --- nova/utils.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/utils.py b/nova/utils.py index 5c7d52c70..21e6221b2 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -851,7 +851,6 @@ def is_valid_ipv4(address): """valid the address strictly as per format xxx.xxx.xxx.xxx. where xxx is a value between 0 and 255. """ - parts = address.split(".") if len(parts) != 4: return False -- cgit From 76f02277a3677d40a13a8b05a12f9d83053808c3 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Tue, 23 Aug 2011 09:46:49 -0700 Subject: Fixed some docstring Added default publisher_id flagw --- nova/notifier/api.py | 7 +++++-- nova/tests/monkey_patch_example/__init__.py | 13 ++++++------- nova/tests/monkey_patch_example/example_a.py | 7 ++----- nova/tests/monkey_patch_example/example_b.py | 6 ++---- nova/tests/test_notifier.py | 3 +-- 5 files changed, 16 insertions(+), 20 deletions(-) (limited to 'nova') diff --git a/nova/notifier/api.py b/nova/notifier/api.py index f5cf95d2a..6ef4a050e 100644 --- a/nova/notifier/api.py +++ b/nova/notifier/api.py @@ -25,6 +25,9 @@ FLAGS = flags.FLAGS flags.DEFINE_string('default_notification_level', 'INFO', 'Default notification level for outgoing notifications') +flags.DEFINE_string('default_publisher_id', FLAGS.host, + 'Default publisher_id for outgoing notifications') + WARN = 'WARN' INFO = 'INFO' @@ -55,9 +58,9 @@ def notify_decorator(name, fn): body['args'].append(arg) for key in kwarg: body['kwarg'][key] = kwarg[key] - notify(FLAGS.host, + notify(FLAGS.default_publisher_id, name, - DEBUG, + FLAGS.default_notification_level, body) return fn(*args, **kwarg) return wrapped_func diff --git a/nova/tests/monkey_patch_example/__init__.py b/nova/tests/monkey_patch_example/__init__.py index 1cfdf8a7e..25cf9ccfe 100644 --- a/nova/tests/monkey_patch_example/__init__.py +++ b/nova/tests/monkey_patch_example/__init__.py @@ -1,3 +1,5 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + # Copyright 2011 OpenStack LLC. # All Rights Reserved. # @@ -12,8 +14,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -"""Example Module for testing utils.monkey_patch() -""" +"""Example Module for testing utils.monkey_patch().""" CALLED_FUNCTION = [] @@ -21,12 +22,10 @@ CALLED_FUNCTION = [] def example_decorator(name, function): """ decorator for notify which is used from utils.monkey_patch() - Parameters: - - name - name of the function - function - object of the function - + :param name: name of the function + :param function: - object of the function + :returns: function -- decorated function """ def wrapped_func(*args, **kwarg): CALLED_FUNCTION.append(name) diff --git a/nova/tests/monkey_patch_example/example_a.py b/nova/tests/monkey_patch_example/example_a.py index 91bf048e4..21e79bcb0 100644 --- a/nova/tests/monkey_patch_example/example_a.py +++ b/nova/tests/monkey_patch_example/example_a.py @@ -1,7 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -15,9 +14,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. - -"""Example Module A for testing utils.monkey_patch() -""" +"""Example Module A for testing utils.monkey_patch().""" def example_function_a(): diff --git a/nova/tests/monkey_patch_example/example_b.py b/nova/tests/monkey_patch_example/example_b.py index edd267c4f..9d8f6d339 100644 --- a/nova/tests/monkey_patch_example/example_b.py +++ b/nova/tests/monkey_patch_example/example_b.py @@ -1,7 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -16,8 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. -"""Example Module B for testing utils.monkey_patch() -""" +"""Example Module B for testing utils.monkey_patch().""" def example_function_b(): diff --git a/nova/tests/test_notifier.py b/nova/tests/test_notifier.py index ab5dfb692..7de3a4a99 100644 --- a/nova/tests/test_notifier.py +++ b/nova/tests/test_notifier.py @@ -141,8 +141,7 @@ class NotifierTestCase(test.TestCase): def example_api(arg1, arg2): return arg1 + arg2 - example_api =\ - nova.notifier.api.notify_decorator( + example_api = nova.notifier.api.notify_decorator( 'example_api', example_api) -- cgit From 295bcc8ef70d767bf1539defe1a79a67bdf555ff Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Aug 2011 13:44:39 -0400 Subject: updating tests --- nova/tests/api/openstack/test_servers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index cec6eecc0..e5c1f2c34 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -785,7 +785,7 @@ class ServersTest(test.TestCase): new_return_server = return_server_with_attributes(interfaces=ifaces) self.stubs.Set(nova.db.api, 'instance_get', new_return_server) - req = webob.Request.blank('/v1.1/servers/1') + req = webob.Request.blank('/v1.1/fake/servers/1') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) res_dict = json.loads(res.body) -- cgit From 6bbef7627200f6c6ef27b5ae5c9b114e8e6d0f52 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Tue, 23 Aug 2011 12:06:25 -0700 Subject: Fixed doc string --- nova/flags.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/flags.py b/nova/flags.py index 05d0db8af..95000df1b 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -409,7 +409,7 @@ DEFINE_bool('monkey_patch', False, 'Whether to log monkey patching') DEFINE_list('monkey_patch_modules', - ['nova.api.ec2.cloud:nova.notifier.api.notify_decorator', - 'nova.compute.api:nova.notifier.api.notify_decorator'], - 'Module list representing monkey\ - patched module and decorator') + ['nova.api.ec2.cloud:nova.notifier.api.notify_decorator', + 'nova.compute.api:nova.notifier.api.notify_decorator'], + 'Module list representing monkey ' + 'patched module and decorator') -- cgit From a5fd82841bfada1b59066d82094f41ffa9389dec Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 23 Aug 2011 12:21:52 -0700 Subject: fix for rc generation using noauth. --- nova/auth/manager.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 85227bea0..44e6e11ac 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -41,10 +41,13 @@ from nova.auth import signer FLAGS = flags.FLAGS +flags.DEFINE_bool('use_deprecated_auth', + False, + 'This flag must be set to use old style auth') + flags.DEFINE_list('allowed_roles', ['cloudadmin', 'itsec', 'sysadmin', 'netadmin', 'developer'], 'Allowed roles for project') - # NOTE(vish): a user with one of these roles will be a superuser and # have access to all api commands flags.DEFINE_list('superuser_roles', ['cloudadmin'], @@ -814,7 +817,13 @@ class AuthManager(object): s3_host = host ec2_host = host rc = open(FLAGS.credentials_template).read() - rc = rc % {'access': user.access, + # NOTE(vish): Deprecated auth uses an access key, no auth uses a + # the user_id in place of it. + if FLAGS.use_deprecated_auth: + access = user.access + else: + access = user.id + rc = rc % {'access': access, 'project': pid, 'secret': user.secret, 'ec2': '%s://%s:%s%s' % (FLAGS.ec2_scheme, -- cgit From 8c30e3e4b1847e6f44790fc4b614fe56de84cbfb Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 23 Aug 2011 13:44:21 -0700 Subject: Forgot to set the flag for the test --- nova/tests/test_auth.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova') diff --git a/nova/tests/test_auth.py b/nova/tests/test_auth.py index 4561eb7f2..1b3166af7 100644 --- a/nova/tests/test_auth.py +++ b/nova/tests/test_auth.py @@ -147,6 +147,7 @@ class _AuthManagerBaseTestCase(test.TestCase): '/services/Cloud')) def test_can_get_credentials(self): + self.flags(use_deprecated_auth=True) st = {'access': 'access', 'secret': 'secret'} with user_and_project_generator(self.manager, user_state=st) as (u, p): credentials = self.manager.get_environment_rc(u, p) -- cgit