From 005a4e645f8e913c673c6ba07e7b0c8c54f33e1c Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Tue, 21 Dec 2010 14:17:29 -0600 Subject: Refactored duplicate rpc.cast() calls in nova/compute/api.py. Cleaned up some formatting issues. --- nova/compute/api.py | 47 +++++++++++++++++------------------------------ nova/compute/manager.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 30 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index c740814da..ae7c84bc5 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -206,8 +206,7 @@ class ComputeAPI(base.Base): def delete_instance(self, context, instance_id): logging.debug("Going to try and terminate %d" % instance_id) try: - instance = self.db.instance_get_by_internal_id(context, - instance_id) + instance = self.get_instance(context, instance_id) except exception.NotFound as e: logging.warning("Instance %d was not found during terminate", instance_id) @@ -271,50 +270,38 @@ class ComputeAPI(base.Base): def get_instance(self, context, instance_id): return self.db.instance_get_by_internal_id(context, instance_id) - def reboot(self, context, instance_id): - """Reboot the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) + def _cast_compute_message(method, context, instance_id): + """Generic handler for RPC calls.""" + instance = self.get_instance(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "reboot_instance", + {"method": method, "args": {"instance_id": instance['id']}}) + def reboot(self, context, instance_id): + """Reboot the given instance.""" + self._cast_compute_message("reboot_instance", context, instance_id) + def pause(self, context, instance_id): """Pause the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "pause_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message("pause_instance", context, instance_id) def unpause(self, context, instance_id): """Unpause the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "unpause_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message("unpause_instance", context, instance_id) def rescue(self, context, instance_id): """Rescue the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "rescue_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message("rescue_instance", context, instance_id) def unrescue(self, context, instance_id): """Unrescue the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "unrescue_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message("unrescue_instance", context, instance_id) + + def reset_root_password(self, context, instance_id): + """Reset the root/admin pw for the given instance.""" + self._cast_compute_message("reset_root_password", context, instance_id) def _get_network_topic(self, context): """Retrieves the network host for a project""" diff --git a/nova/compute/manager.py b/nova/compute/manager.py index a84af6bb9..cb64cd39d 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -156,6 +156,38 @@ class ComputeManager(manager.Manager): self.driver.reboot(instance_ref) self._update_state(context, instance_id) + + + + # WORKING CODE + @exception.wrap_exception + def reset_root_password(self, context, instance_id): + """Reset the root/admin password for an instance on this server.""" + context = context.elevated() + instance_ref = self.db.instance_get(context, instance_id) + self._update_state(context, instance_id) + + if instance_ref['state'] != power_state.RUNNING: + logging.warn('trying to reset the password on a non-running ' + 'instance: %s (state: %s excepted: %s)', + instance_ref['internal_id'], + instance_ref['state'], + power_state.RUNNING) + + logging.debug('instance %s: resetting root password', + instance_ref['name']) + self.db.instance_set_state(context, + instance_id, + power_state.NOSTATE, + 'resetting_password') + #TODO: (dabo) not sure how we will implement this yet. + self.driver.reset_root_password(instance_ref) + self._update_state(context, instance_id) + + + + + @exception.wrap_exception def rescue_instance(self, context, instance_id): """Rescue an instance on this server.""" -- cgit From 269ab03f74ea94a586f6af5b7d61847443522ba1 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Wed, 22 Dec 2010 11:20:30 -0600 Subject: committing so that I can merge trunk changes --- nova/compute/manager.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index cb64cd39d..86f8d9216 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -156,10 +156,6 @@ class ComputeManager(manager.Manager): self.driver.reboot(instance_ref) self._update_state(context, instance_id) - - - - # WORKING CODE @exception.wrap_exception def reset_root_password(self, context, instance_id): """Reset the root/admin password for an instance on this server.""" @@ -180,14 +176,10 @@ class ComputeManager(manager.Manager): instance_id, power_state.NOSTATE, 'resetting_password') - #TODO: (dabo) not sure how we will implement this yet. + #### TODO: (dabo) not sure how we will implement this yet. self.driver.reset_root_password(instance_ref) self._update_state(context, instance_id) - - - - @exception.wrap_exception def rescue_instance(self, context, instance_id): """Rescue an instance on this server.""" -- cgit From 70d254c626e925f6de8408f0ca70f3de28a7307a Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Wed, 22 Dec 2010 17:53:42 -0800 Subject: added tests to ensure the easy api works as a backend for Compute API --- nova/compute/api.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 4953fe559..5f18539a3 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -36,10 +36,19 @@ from nova.db import base FLAGS = flags.FLAGS -def generate_default_hostname(internal_id): +def id_to_default_hostname(internal_id): """Default function to generate a hostname given an instance reference.""" return str(internal_id) +def id_to_ec2_hostname(internal_id): + digits = [] + while internal_id != 0: + internal_id, remainder = divmod(internal_id, 36) + digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[remainder]) + return "i-%s" % ''.join(reversed(digits)) + +HOSTNAME_FORMATTERS = {'default': id_to_default_hostname, + 'ec2': id_to_ec2_hostname} class ComputeAPI(base.Base): """API for interacting with the compute manager.""" @@ -75,7 +84,7 @@ class ComputeAPI(base.Base): display_name='', description='', key_name=None, key_data=None, security_group='default', user_data=None, - generate_hostname=generate_default_hostname): + hostname_format='default'): """Create the number of instances requested if quote and other arguments check out ok.""" @@ -144,6 +153,7 @@ class ComputeAPI(base.Base): elevated = context.elevated() instances = [] + generate_hostname = HOSTNAME_FORMATTERS[hostname_format] logging.debug(_("Going to run %s instances..."), num_instances) for num in range(num_instances): instance = dict(mac_address=utils.generate_mac(), @@ -177,7 +187,7 @@ class ComputeAPI(base.Base): "args": {"topic": FLAGS.compute_topic, "instance_id": instance_id}}) - return instances + return [dict(x.iteritems()) for x in instances] def ensure_default_security_group(self, context): """ Create security group for the security context if it @@ -254,7 +264,8 @@ class ComputeAPI(base.Base): return self.db.instance_get_all(context) def get_instance(self, context, instance_id): - return self.db.instance_get_by_internal_id(context, instance_id) + rv = self.db.instance_get_by_internal_id(context, instance_id) + return dict(rv.iteritems()) def reboot(self, context, instance_id): """Reboot the given instance.""" -- cgit From 8e1b74aa1c5a2f9113473eedc8e35b38b41445ea Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Mon, 27 Dec 2010 15:15:24 -0800 Subject: Added stack command-line tool --- nova/compute/api.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 5f18539a3..005ed7a68 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -40,6 +40,7 @@ def id_to_default_hostname(internal_id): """Default function to generate a hostname given an instance reference.""" return str(internal_id) + def id_to_ec2_hostname(internal_id): digits = [] while internal_id != 0: @@ -47,9 +48,11 @@ def id_to_ec2_hostname(internal_id): digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[remainder]) return "i-%s" % ''.join(reversed(digits)) + HOSTNAME_FORMATTERS = {'default': id_to_default_hostname, 'ec2': id_to_ec2_hostname} + class ComputeAPI(base.Base): """API for interacting with the compute manager.""" @@ -63,6 +66,7 @@ class ComputeAPI(base.Base): super(ComputeAPI, self).__init__(**kwargs) def get_network_topic(self, context, instance_id): + """Get the network topic for an instance.""" try: instance = self.db.instance_get_by_internal_id(context, instance_id) @@ -221,6 +225,7 @@ class ComputeAPI(base.Base): return self.db.instance_update(context, instance_id, kwargs) def delete_instance(self, context, instance_id): + """Terminate and remove an instance.""" logging.debug("Going to try and terminate %d" % instance_id) try: instance = self.db.instance_get_by_internal_id(context, @@ -264,6 +269,7 @@ class ComputeAPI(base.Base): return self.db.instance_get_all(context) def get_instance(self, context, instance_id): + """Get information about a specific instance.""" rv = self.db.instance_get_by_internal_id(context, instance_id) return dict(rv.iteritems()) -- cgit From b50433d77207c542ee63b7858eb465bb51ba56ea Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 31 Dec 2010 05:37:30 -0600 Subject: Before merge with xenstore-plugin code --- nova/compute/manager.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index c46587adc..4d8c5e1a5 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -36,6 +36,7 @@ terminating it. import datetime import logging +import string from nova import exception from nova import flags @@ -239,15 +240,21 @@ class ComputeManager(manager.Manager): power_state.RUNNING) logging.debug('instance %s: resetting root password', - instance_ref['name']) - self.db.instance_set_state(context, - instance_id, - power_state.NOSTATE, - 'resetting_password') + instance_ref['name']) + self.db.instance_set_state(context, instance_id, + power_state.NOSTATE, 'resetting_password') #### TODO: (dabo) not sure how we will implement this yet. - self.driver.reset_root_password(instance_ref) + new_pass = self._generate_password(12) + self.driver.reset_root_password(instance_ref, new_pass) self._update_state(context, instance_id) + def _generate_password(self, length=20): + """Generate a random sequence of letters and digits + to be used as a password. + """ + chrs = string.letters + string.digits + return "".join([random.choice(chrs) for i in xrange(length)]) + @exception.wrap_exception def rescue_instance(self, context, instance_id): """Rescue an instance on this server.""" -- cgit From 108352d5c132f6accc79974d8c646a2bc7d4f127 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 31 Dec 2010 12:21:04 -0600 Subject: Updated the password generation code --- nova/compute/manager.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 8ebd87f28..583fabe42 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -36,6 +36,7 @@ terminating it. import datetime import logging +import random import string from nova import exception @@ -247,7 +248,7 @@ class ComputeManager(manager.Manager): self.driver.snapshot(instance_ref, name) @exception.wrap_exception - def reset_root_password(self, context, instance_id): + def reset_root_password(self, context, instance_id, new_pass=None): """Reset the root/admin password for an instance on this server.""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) @@ -264,8 +265,9 @@ class ComputeManager(manager.Manager): instance_ref['name']) self.db.instance_set_state(context, instance_id, power_state.NOSTATE, 'resetting_password') - #### TODO: (dabo) not sure how we will implement this yet. - new_pass = self._generate_password(12) + if new_pass is None: + # Generate a random, 12-character password + new_pass = self._generate_password(12) self.driver.reset_root_password(instance_ref, new_pass) self._update_state(context, instance_id) -- cgit From 1cc5e933ccc29a88d09d2050e5224ee27eda767c Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 5 Jan 2011 08:05:11 +0000 Subject: stop using partitions and first pass at cow images --- nova/compute/disk.py | 108 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 39 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/disk.py b/nova/compute/disk.py index 814a258cd..766f27d35 100644 --- a/nova/compute/disk.py +++ b/nova/compute/disk.py @@ -28,6 +28,7 @@ import tempfile from nova import exception from nova import flags +from nova import utils FLAGS = flags.FLAGS @@ -37,8 +38,7 @@ flags.DEFINE_integer('block_size', 1024 * 1024 * 256, 'block_size to use for dd') -def partition(infile, outfile, local_bytes=0, resize=True, - local_type='ext2', execute=None): +def partition(infile, outfile, local_bytes=0, resize=True, local_type='ext2'): """ Turns a partition (infile) into a bootable drive image (outfile). @@ -61,10 +61,10 @@ def partition(infile, outfile, local_bytes=0, resize=True, file_size = os.path.getsize(infile) if resize and file_size < FLAGS.minimum_root_size: last_sector = FLAGS.minimum_root_size / sector_size - 1 - execute('dd if=/dev/zero of=%s count=1 seek=%d bs=%d' + utils.execute('dd if=/dev/zero of=%s count=1 seek=%d bs=%d' % (infile, last_sector, sector_size)) - execute('e2fsck -fp %s' % infile, check_exit_code=False) - execute('resize2fs %s' % infile) + utils.execute('e2fsck -fp %s' % infile, check_exit_code=False) + utils.execute('resize2fs %s' % infile) file_size = FLAGS.minimum_root_size elif file_size % sector_size != 0: logging.warn(_("Input partition size not evenly divisible by" @@ -83,37 +83,37 @@ def partition(infile, outfile, local_bytes=0, resize=True, last_sector = local_last # e # create an empty file - execute('dd if=/dev/zero of=%s count=1 seek=%d bs=%d' + utils.execute('dd if=/dev/zero of=%s count=1 seek=%d bs=%d' % (outfile, mbr_last, sector_size)) # make mbr partition - execute('parted --script %s mklabel msdos' % outfile) + utils.execute('parted --script %s mklabel msdos' % outfile) # append primary file - execute('dd if=%s of=%s bs=%s conv=notrunc,fsync oflag=append' + utils.execute('dd if=%s of=%s bs=%s conv=notrunc,fsync oflag=append' % (infile, outfile, FLAGS.block_size)) # make primary partition - execute('parted --script %s mkpart primary %ds %ds' + utils.execute('parted --script %s mkpart primary %ds %ds' % (outfile, primary_first, primary_last)) if local_bytes > 0: # make the file bigger - execute('dd if=/dev/zero of=%s count=1 seek=%d bs=%d' + utils.execute('dd if=/dev/zero of=%s count=1 seek=%d bs=%d' % (outfile, last_sector, sector_size)) # make and format local partition - execute('parted --script %s mkpartfs primary %s %ds %ds' + utils.execute('parted --script %s mkpartfs primary %s %ds %ds' % (outfile, local_type, local_first, local_last)) -def extend(image, size, execute): +def extend(image, size): file_size = os.path.getsize(image) if file_size >= size: return - return execute('truncate -s size %s' % (image,)) + return utils.execute('truncate -s size %s' % (image,)) -def inject_data(image, key=None, net=None, partition=None, execute=None): +def inject_data(image, key=None, net=None, partition=None): """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 @@ -122,15 +122,11 @@ def inject_data(image, key=None, net=None, partition=None, execute=None): If partition is not specified it mounts the image as a single partition. """ - out, err = execute('sudo losetup --find --show %s' % image) - if err: - raise exception.Error(_('Could not attach image to loopback: %s') - % err) - device = out.strip() + device = _link_device(image) try: if not partition is None: # create partition - out, err = execute('sudo kpartx -a %s' % device) + out, err = utils.execute('sudo kpartx -a %s' % device) if err: raise exception.Error(_('Failed to load partition: %s') % err) mapped_device = '/dev/mapper/%sp%s' % (device.split('/')[-1], @@ -146,12 +142,12 @@ def inject_data(image, key=None, net=None, partition=None, execute=None): mapped_device) # Configure ext2fs so that it doesn't auto-check every N boots - out, err = execute('sudo tune2fs -c 0 -i 0 %s' % mapped_device) + out, err = utils.execute('sudo tune2fs -c 0 -i 0 %s' % mapped_device) tmpdir = tempfile.mkdtemp() try: # mount loopback to dir - out, err = execute( + out, err = utils.execute( 'sudo mount %s %s' % (mapped_device, tmpdir)) if err: raise exception.Error(_('Failed to mount filesystem: %s') @@ -160,45 +156,79 @@ def inject_data(image, key=None, net=None, partition=None, execute=None): try: if key: # inject key file - _inject_key_into_fs(key, tmpdir, execute=execute) + _inject_key_into_fs(key, tmpdir) if net: - _inject_net_into_fs(net, tmpdir, execute=execute) + _inject_net_into_fs(net, tmpdir) finally: # unmount device - execute('sudo umount %s' % mapped_device) + utils.execute('sudo umount %s' % mapped_device) finally: # remove temporary directory - execute('rmdir %s' % tmpdir) + utils.execute('rmdir %s' % tmpdir) if not partition is None: # remove partitions - execute('sudo kpartx -d %s' % device) + utils.execute('sudo kpartx -d %s' % device) finally: - # remove loopback - execute('sudo losetup --detach %s' % device) + _unlink_device(image, device) -def _inject_key_into_fs(key, fs, execute=None): +def _link_device(image): + if FLAGS.use_cow_images: + device = _allocate_device() + utils.execute('sudo qemu-nbd --connect=%s %s' % (device, image)) + else: + out, err = utils.execute('sudo losetup --find --show %s' % image) + if err: + raise exception.Error(_('Could not attach image to loopback: %s') + % err) + return out.strip() + + +def _unlink_device(image, device): + if FLAGS.use_cow_images: + utils.execute('sudo qemu-nbd --disconnect %s' % image) + _free_device(device) + else: + utils.execute('sudo losetup --detach %s' % device) + + +_DEVICES = ['/dev/nbd%s' % i for i in xrange(16)] + +def _allocate_device(): + # NOTE(vish): This assumes no other processes are using nbd devices. + # It will race cause a race condition if multiple + # workers are running on a given machine. + if not _DEVICES: + raise exception.Error(_('No free nbd devices')) + return _DEVICES.pop() + + +def _free_device(device): + _DEVICES.append(device) + + +def _inject_key_into_fs(key, fs): """Add the given public ssh key to root's authorized_keys. key is an ssh key string. fs is the path to the base of the filesystem into which to inject the key. """ sshdir = os.path.join(fs, 'root', '.ssh') - execute('sudo mkdir -p %s' % sshdir) # existing dir doesn't matter - execute('sudo chown root %s' % sshdir) - execute('sudo chmod 700 %s' % sshdir) + utils.execute('sudo mkdir -p %s' % sshdir) # existing dir doesn't matter + utils.execute('sudo chown root %s' % sshdir) + utils.execute('sudo chmod 700 %s' % sshdir) keyfile = os.path.join(sshdir, 'authorized_keys') - execute('sudo tee -a %s' % keyfile, '\n' + key.strip() + '\n') + utils.execute('sudo tee -a %s' % keyfile, '\n' + key.strip() + '\n') -def _inject_net_into_fs(net, fs, execute=None): +def _inject_net_into_fs(net, fs): """Inject /etc/network/interfaces into the filesystem rooted at fs. net is the contents of /etc/network/interfaces. """ netdir = os.path.join(os.path.join(fs, 'etc'), 'network') - execute('sudo mkdir -p %s' % netdir) # existing dir doesn't matter - execute('sudo chown root:root %s' % netdir) - execute('sudo chmod 755 %s' % netdir) + utils.execute('sudo mkdir -p %s' % netdir) # existing dir doesn't matter + utils.execute('sudo chown root:root %s' % netdir) + utils.execute('sudo chmod 755 %s' % netdir) netfile = os.path.join(netdir, 'interfaces') - execute('sudo tee %s' % netfile, net) + utils.execute('sudo tee %s' % netfile, net) -- cgit From b5f8ab0e913c121a80ff0efe358960099e7c87f8 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 5 Jan 2011 19:16:17 +0000 Subject: fix injection and xml --- nova/compute/disk.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/disk.py b/nova/compute/disk.py index 766f27d35..f640bdbbb 100644 --- a/nova/compute/disk.py +++ b/nova/compute/disk.py @@ -110,7 +110,7 @@ def extend(image, size): file_size = os.path.getsize(image) if file_size >= size: return - return utils.execute('truncate -s size %s' % (image,)) + return utils.execute('truncate -s %s %s' % (size, image)) def inject_data(image, key=None, net=None, partition=None): @@ -175,7 +175,8 @@ def inject_data(image, key=None, net=None, partition=None): def _link_device(image): if FLAGS.use_cow_images: device = _allocate_device() - utils.execute('sudo qemu-nbd --connect=%s %s' % (device, image)) + utils.execute('sudo qemu-nbd -c %s %s' % (device, image)) + return device else: out, err = utils.execute('sudo losetup --find --show %s' % image) if err: @@ -184,9 +185,9 @@ def _link_device(image): return out.strip() -def _unlink_device(image, device): +def _unlink_device(device): if FLAGS.use_cow_images: - utils.execute('sudo qemu-nbd --disconnect %s' % image) + utils.execute('sudo qemu-nbd -d %s' % device) _free_device(device) else: utils.execute('sudo losetup --detach %s' % device) -- cgit From f1f292a787ba20134c007da087bd9585d1875e86 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 5 Jan 2011 19:50:39 +0000 Subject: more fixes, docstrings --- nova/compute/disk.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/disk.py b/nova/compute/disk.py index f640bdbbb..ac0d689d5 100644 --- a/nova/compute/disk.py +++ b/nova/compute/disk.py @@ -107,13 +107,15 @@ def partition(infile, outfile, local_bytes=0, resize=True, local_type='ext2'): def extend(image, size): + """Increase image to size""" file_size = os.path.getsize(image) if file_size >= size: return + # TODO(vish): attempt to resize filesystem return utils.execute('truncate -s %s %s' % (size, image)) -def inject_data(image, key=None, net=None, partition=None): +def inject_data(image, key=None, net=None, partition=None, nbd=False): """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 @@ -122,7 +124,7 @@ def inject_data(image, key=None, net=None, partition=None): If partition is not specified it mounts the image as a single partition. """ - device = _link_device(image) + device = _link_device(image, nbd) try: if not partition is None: # create partition @@ -169,11 +171,12 @@ def inject_data(image, key=None, net=None, partition=None): # remove partitions utils.execute('sudo kpartx -d %s' % device) finally: - _unlink_device(image, device) + _unlink_device(device, nbd) -def _link_device(image): - if FLAGS.use_cow_images: +def _link_device(image, nbd): + """Link image to device using loopback or nbd""" + if nbd: device = _allocate_device() utils.execute('sudo qemu-nbd -c %s %s' % (device, image)) return device @@ -185,8 +188,9 @@ def _link_device(image): return out.strip() -def _unlink_device(device): - if FLAGS.use_cow_images: +def _unlink_device(device, nbd): + """Unlink image from device using loopback or nbd""" + if nbd: utils.execute('sudo qemu-nbd -d %s' % device) _free_device(device) else: -- cgit From 3d30bb1706812c4e6f9c1e01b373bb076a9f7ee3 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 5 Jan 2011 19:52:55 +0000 Subject: pep8 cleanup --- nova/compute/disk.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/compute') diff --git a/nova/compute/disk.py b/nova/compute/disk.py index ac0d689d5..bbcd55678 100644 --- a/nova/compute/disk.py +++ b/nova/compute/disk.py @@ -199,6 +199,7 @@ def _unlink_device(device, nbd): _DEVICES = ['/dev/nbd%s' % i for i in xrange(16)] + def _allocate_device(): # NOTE(vish): This assumes no other processes are using nbd devices. # It will race cause a race condition if multiple -- cgit From a6b82b3015a64922a0733bd0dd5463b1a49ca080 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 5 Jan 2011 20:46:18 +0000 Subject: simplify decorator into a wrapper fn --- nova/compute/disk.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/disk.py b/nova/compute/disk.py index bbcd55678..8b7453f32 100644 --- a/nova/compute/disk.py +++ b/nova/compute/disk.py @@ -25,6 +25,7 @@ Includes injection of SSH PGP keys into authorized_keys file. import logging import os import tempfile +import time from nova import exception from nova import flags @@ -111,8 +112,10 @@ def extend(image, size): file_size = os.path.getsize(image) if file_size >= size: return - # TODO(vish): attempt to resize filesystem - return utils.execute('truncate -s %s %s' % (size, image)) + utils.execute('truncate -s %s %s' % (size, image)) + # NOTE(vish): attempts to resize filesystem + utils.execute('e2fsck -fp %s' % image, check_exit_code=False) + utils.execute('resize2fs %s' % image, check_exit_code=False) def inject_data(image, key=None, net=None, partition=None, nbd=False): @@ -179,6 +182,9 @@ def _link_device(image, nbd): if nbd: device = _allocate_device() utils.execute('sudo qemu-nbd -c %s %s' % (device, image)) + # NOTE(vish): this forks into another process, so give it a chance + # to set up before continuuing + time.sleep(1) return device else: out, err = utils.execute('sudo losetup --find --show %s' % image) -- cgit From f67802d62ee530b4e81aaf108dfd3813c84550b2 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Wed, 5 Jan 2011 16:41:50 -0600 Subject: intermediate work --- nova/compute/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 76a571d61..1d21a4668 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -257,7 +257,7 @@ class ComputeAPI(base.Base): def get_instance(self, context, instance_id): return self.db.instance_get_by_internal_id(context, instance_id) - def _cast_compute_message(method, context, instance_id): + def _cast_compute_message(self, method, context, instance_id): """Generic handler for RPC calls.""" instance = self.get_instance(context, instance_id) host = instance['host'] -- cgit From a3e12f5eb92921acc622ea7bd9097edeea0d40fd Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 6 Jan 2011 06:45:14 -0600 Subject: Renamed 'set_root_password' to 'set_admin_password' globally. --- nova/compute/api.py | 50 +++++++++++++++++-------------------------------- nova/compute/manager.py | 24 +++++++++++++----------- 2 files changed, 30 insertions(+), 44 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 1d21a4668..0a7b802d6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -190,7 +190,7 @@ class ComputeAPI(base.Base): """ try: db.security_group_get_by_name(context, context.project_id, - 'default') + 'default') except exception.NotFound: values = {'name': 'default', 'description': 'default', @@ -258,70 +258,54 @@ class ComputeAPI(base.Base): return self.db.instance_get_by_internal_id(context, instance_id) def _cast_compute_message(self, method, context, instance_id): - """Generic handler for RPC calls.""" + """Generic handler for RPC calls to compute.""" instance = self.get_instance(context, instance_id) host = instance['host'] rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": method, - "args": {"instance_id": instance['id']}}) + self.db.queue_get_for(context, FLAGS.compute_topic, host), + {'method': method, 'args': {'instance_id': instance['id']}}) def snapshot(self, context, instance_id, name): """Snapshot the given instance.""" - self._cast_compute_message("snapshot_instance", context, instance_id) + self._cast_compute_message('snapshot_instance', context, instance_id) def reboot(self, context, instance_id): """Reboot the given instance.""" - self._cast_compute_message("reboot_instance", context, instance_id) + self._cast_compute_message('reboot_instance', context, instance_id) def pause(self, context, instance_id): """Pause the given instance.""" - self._cast_compute_message("pause_instance", context, instance_id) + self._cast_compute_message('pause_instance', context, instance_id) def unpause(self, context, instance_id): """Unpause the given instance.""" - self._cast_compute_message("unpause_instance", context, instance_id) + self._cast_compute_message('unpause_instance', context, instance_id) def get_diagnostics(self, context, instance_id): """Retrieve diagnostics for the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance["host"] - return rpc.call(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "get_diagnostics", - "args": {"instance_id": instance["id"]}}) + self._cast_compute_message('get_diagnostics', context, instance_id) def get_actions(self, context, instance_id): """Retrieve actions for the given instance.""" instance = self.db.instance_get_by_internal_id(context, instance_id) - return self.db.instance_get_actions(context, instance["id"]) + return self.db.instance_get_actions(context, instance['id']) def suspend(self, context, instance_id): """suspend the instance with instance_id""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "suspend_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message('suspend_instance', context, instance_id) def resume(self, context, instance_id): """resume the instance with instance_id""" - instance = self.db.instance_get_by_internal_id(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "resume_instance", - "args": {"instance_id": instance['id']}}) + self._cast_compute_message('resume_instance', context, instance_id) def rescue(self, context, instance_id): """Rescue the given instance.""" - self._cast_compute_message("rescue_instance", context, instance_id) + self._cast_compute_message('rescue_instance', context, instance_id) def unrescue(self, context, instance_id): """Unrescue the given instance.""" - self._cast_compute_message("unrescue_instance", context, instance_id) + self._cast_compute_message('unrescue_instance', context, instance_id) - def reset_root_password(self, context, instance_id): - """Reset the root/admin pw for the given instance.""" - self._cast_compute_message("reset_root_password", context, instance_id) + def set_admin_password(self, context, instance_id): + """Set the root/admin password for the given instance.""" + self._cast_compute_message('set_admin_password', context, instance_id) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6f4d14589..b8bf91530 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -53,6 +53,8 @@ flags.DEFINE_string('compute_driver', 'nova.virt.connection.get_connection', 'Driver to use for controlling virtualization') flags.DEFINE_string('stub_network', False, 'Stub network related code') +flags.DEFINE_integer('password_length', 12, + 'Length of generated admin passwords') class ComputeManager(manager.Manager): @@ -248,27 +250,27 @@ class ComputeManager(manager.Manager): self.driver.snapshot(instance_ref, name) @exception.wrap_exception - def reset_root_password(self, context, instance_id, new_pass=None): - """Reset the root/admin password for an instance on this server.""" + def set_admin_password(self, context, instance_id, new_pass=None): + """Set the root/admin password for an instance on this server.""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) self._update_state(context, instance_id) if instance_ref['state'] != power_state.RUNNING: logging.warn('trying to reset the password on a non-running ' - 'instance: %s (state: %s excepted: %s)', - instance_ref['internal_id'], - instance_ref['state'], - power_state.RUNNING) + 'instance: %s (state: %s expected: %s)', + instance_ref['internal_id'], + instance_ref['state'], + power_state.RUNNING) - logging.debug('instance %s: resetting root password', + logging.debug('instance %s: setting admin password', instance_ref['name']) self.db.instance_set_state(context, instance_id, - power_state.NOSTATE, 'resetting_password') + power_state.NOSTATE, 'setting_password') if new_pass is None: - # Generate a random, 12-character password - new_pass = self._generate_password(12) - self.driver.reset_root_password(instance_ref, new_pass) + # Generate a random password + new_pass = self._generate_password(FLAGS.password_length) + self.driver.set_admin_password(instance_ref, new_pass) self._update_state(context, instance_id) def _generate_password(self, length=20): -- cgit From 0209ad587b2d8d35a7abdf60ca9b33391cab4a83 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 6 Jan 2011 07:21:11 -0600 Subject: merged trunk changes --- nova/compute/api.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 476667fa8..19cdf2d0a 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -61,13 +61,11 @@ class API(base.Base): def get_network_topic(self, context, instance_id): try: -<<<<<<< TREE instance = self.get_instance(context, instance_id) + # TODO (dabo) Need to verify whether an internal_id or a db id + # id being passed; use get_instance or get, respectively. + #instance = self.get(context, instance_id) except exception.NotFound, e: -======= - instance = self.get(context, instance_id) - except exception.NotFound as e: ->>>>>>> MERGE-SOURCE logging.warning("Instance %d was not found in get_network_topic", instance_id) raise e -- cgit From 3d70b49a1c17bccfc6163198b2d99efb9a9829a7 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 6 Jan 2011 13:02:32 -0600 Subject: commit before merging trunk --- nova/compute/api.py | 18 ++++-------------- nova/compute/manager.py | 5 +++-- 2 files changed, 7 insertions(+), 16 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 19cdf2d0a..a4345f337 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -62,9 +62,6 @@ class API(base.Base): def get_network_topic(self, context, instance_id): try: instance = self.get_instance(context, instance_id) - # TODO (dabo) Need to verify whether an internal_id or a db id - # id being passed; use get_instance or get, respectively. - #instance = self.get(context, instance_id) except exception.NotFound, e: logging.warning("Instance %d was not found in get_network_topic", instance_id) @@ -223,10 +220,6 @@ class API(base.Base): logging.debug('Going to try and terminate %s' % instance_id) try: instance = self.get_instance(context, instance_id) - #TODO: (dabo) resolve that this is the correct call: - # get_instance vs. get. Depends on whether we get an internal_id - # or an actual db id. - #instance = self.get(context, instance_id) except exception.NotFound, e: logging.warning(_('Instance % was not found during terminate'), instance_id) @@ -252,7 +245,7 @@ class API(base.Base): else: self.db.instance_destroy(context, instance_id) - def get(self, context, instance_id): + def get_instance(self, context, instance_id): """Get a single instance with the given ID.""" return self.db.instance_get_by_id(context, instance_id) @@ -276,9 +269,6 @@ class API(base.Base): project_id) return self.db.instance_get_all(context) - def get_instance(self, context, instance_id): - return self.db.instance_get_by_internal_id(context, instance_id) - def _cast_compute_message(self, method, context, instance_id): """Generic handler for RPC calls to compute.""" instance = self.get_instance(context, instance_id) @@ -309,7 +299,7 @@ class API(base.Base): def get_actions(self, context, instance_id): """Retrieve actions for the given instance.""" - instance = self.db.instance_get_by_internal_id(context, instance_id) + instance = self.db.instance_get_by_id(context, instance_id) return self.db.instance_get_actions(context, instance['id']) def suspend(self, context, instance_id): @@ -337,7 +327,7 @@ class API(base.Base): raise exception.ApiError(_("Invalid device specified: %s. " "Example device: /dev/vdb") % device) self.volume_api.check_attach(context, volume_id) - instance = self.get(context, instance_id) + instance = self.get_instance(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), @@ -360,6 +350,6 @@ class API(base.Base): return instance def associate_floating_ip(self, context, instance_id, address): - instance = self.get(context, instance_id) + instance = self.get_instance(context, instance_id) self.network_api.associate_floating_ip(context, address, instance['fixed_ip']) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 3e6d3eab0..201fffc68 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -83,7 +83,8 @@ class ComputeManager(manager.Manager): # FIXME(ja): include other fields from state? instance_ref = self.db.instance_get(context, instance_id) try: - info = self.driver.get_info(instance_ref['name']) + #info = self.driver.get_info(instance_ref['name']) + info = self.driver.get_info(instance_ref) state = info['state'] except exception.NotFound: state = power_state.NOSTATE @@ -259,7 +260,7 @@ class ComputeManager(manager.Manager): if instance_ref['state'] != power_state.RUNNING: logging.warn('trying to reset the password on a non-running ' 'instance: %s (state: %s expected: %s)', - instance_ref['internal_id'], + instance_ref['id'], instance_ref['state'], power_state.RUNNING) -- cgit From e66f3017373dcf9135c53ae4d510b0b2a5dcecf0 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 6 Jan 2011 15:53:11 -0600 Subject: Got the basic 'set admin password' stuff working --- nova/compute/manager.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 201fffc68..52acfebea 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -83,7 +83,6 @@ class ComputeManager(manager.Manager): # FIXME(ja): include other fields from state? instance_ref = self.db.instance_get(context, instance_id) try: - #info = self.driver.get_info(instance_ref['name']) info = self.driver.get_info(instance_ref) state = info['state'] except exception.NotFound: @@ -266,8 +265,6 @@ class ComputeManager(manager.Manager): logging.debug('instance %s: setting admin password', instance_ref['name']) - self.db.instance_set_state(context, instance_id, - power_state.NOSTATE, 'setting_password') if new_pass is None: # Generate a random password new_pass = self._generate_password(FLAGS.password_length) -- cgit From eaa5b5994891eee0280b750dff221a4b54932eb9 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 7 Jan 2011 10:23:48 -0600 Subject: getting ready to push for merge prop --- nova/compute/api.py | 18 +++++++++--------- nova/compute/manager.py | 3 +-- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 78ffcca7a..106c3f7f0 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -61,7 +61,7 @@ class API(base.Base): def get_network_topic(self, context, instance_id): try: - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) except exception.NotFound, e: logging.warning("Instance %d was not found in get_network_topic", instance_id) @@ -220,7 +220,7 @@ class API(base.Base): def delete(self, context, instance_id): logging.debug('Going to try and terminate %s' % instance_id) try: - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) except exception.NotFound, e: logging.warning(_('Instance % was not found during terminate'), instance_id) @@ -246,7 +246,7 @@ class API(base.Base): else: self.db.instance_destroy(context, instance_id) - def get_instance(self, context, instance_id): + def get(self, context, instance_id): """Get a single instance with the given ID.""" return self.db.instance_get_by_id(context, instance_id) @@ -272,7 +272,7 @@ class API(base.Base): def _cast_compute_message(self, method, context, instance_id): """Generic handler for RPC calls to compute.""" - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), @@ -328,7 +328,7 @@ class API(base.Base): lock the instance with instance_id """ - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), @@ -340,7 +340,7 @@ class API(base.Base): unlock the instance with instance_id """ - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), @@ -352,7 +352,7 @@ class API(base.Base): return the boolean state of (instance with instance_id)'s lock """ - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) return instance['locked'] def attach_volume(self, context, instance_id, volume_id, device): @@ -360,7 +360,7 @@ class API(base.Base): raise exception.ApiError(_("Invalid device specified: %s. " "Example device: /dev/vdb") % device) self.volume_api.check_attach(context, volume_id) - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) host = instance['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), @@ -383,6 +383,6 @@ class API(base.Base): return instance def associate_floating_ip(self, context, instance_id, address): - instance = self.get_instance(context, instance_id) + instance = self.get(context, instance_id) self.network_api.associate_floating_ip(context, address, instance['fixed_ip']) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 10219833b..5d677b023 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -289,8 +289,6 @@ class ComputeManager(manager.Manager): """Set the root/admin password for an instance on this server.""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) - self._update_state(context, instance_id) - if instance_ref['state'] != power_state.RUNNING: logging.warn('trying to reset the password on a non-running ' 'instance: %s (state: %s expected: %s)', @@ -303,6 +301,7 @@ class ComputeManager(manager.Manager): if new_pass is None: # Generate a random password new_pass = self._generate_password(FLAGS.password_length) + self.driver.set_admin_password(instance_ref, new_pass) self._update_state(context, instance_id) -- cgit From 147693e45c7be174c54e39160869ca9a83bb4fff Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 7 Jan 2011 11:04:53 -0600 Subject: Additional cleanup prior to pushing --- nova/compute/api.py | 54 ++++++++++++++++------------------------------------- 1 file changed, 16 insertions(+), 38 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 106c3f7f0..a894a0ce3 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -62,7 +62,7 @@ class API(base.Base): def get_network_topic(self, context, instance_id): try: instance = self.get(context, instance_id) - except exception.NotFound, e: + except exception.NotFound as e: logging.warning("Instance %d was not found in get_network_topic", instance_id) raise e @@ -195,7 +195,7 @@ class API(base.Base): """ try: db.security_group_get_by_name(context, context.project_id, - 'default') + 'default') except exception.NotFound: values = {'name': 'default', 'description': 'default', @@ -218,12 +218,12 @@ class API(base.Base): return self.db.instance_update(context, instance_id, kwargs) def delete(self, context, instance_id): - logging.debug('Going to try and terminate %s' % instance_id) + logging.debug('Going to try to terminate %s' % instance_id) try: instance = self.get(context, instance_id) - except exception.NotFound, e: + except exception.NotFound as e: logging.warning(_('Instance % was not found during terminate'), - instance_id) + instance_id) raise e if (instance['state_description'] == 'terminating'): @@ -239,10 +239,8 @@ class API(base.Base): host = instance['host'] if host: - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "terminate_instance", - "args": {"instance_id": instance_id}}) + self._cast_compute_message('terminate_instance', context, + instance_id) else: self.db.instance_destroy(context, instance_id) @@ -274,9 +272,9 @@ class API(base.Base): """Generic handler for RPC calls to compute.""" instance = self.get(context, instance_id) host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {'method': method, 'args': {'instance_id': instance['id']}}) + queue = self.db.queue_get_for(context, FLAGS.compute_topic, host) + kwargs = {'method': method, 'args': {'instance_id': instance['id']}} + rpc.cast(context, queue, kwargs) def snapshot(self, context, instance_id, name): """Snapshot the given instance.""" @@ -300,8 +298,7 @@ class API(base.Base): def get_actions(self, context, instance_id): """Retrieve actions for the given instance.""" - instance = self.db.instance_get_by_id(context, instance_id) - return self.db.instance_get_actions(context, instance['id']) + return self.db.instance_get_actions(context, instance_id) def suspend(self, context, instance_id): """suspend the instance with instance_id""" @@ -324,34 +321,15 @@ class API(base.Base): self._cast_compute_message('set_admin_password', context, instance_id) def lock(self, context, instance_id): - """ - lock the instance with instance_id - - """ - instance = self.get(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "lock_instance", - "args": {"instance_id": instance['id']}}) + """lock the instance with instance_id""" + self._cast_compute_message('lock_instance', context, instance_id) def unlock(self, context, instance_id): - """ - unlock the instance with instance_id - - """ - instance = self.get(context, instance_id) - host = instance['host'] - rpc.cast(context, - self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "unlock_instance", - "args": {"instance_id": instance['id']}}) + """unlock the instance with instance_id""" + self._cast_compute_message('unlock_instance', context, instance_id) def get_lock(self, context, instance_id): - """ - return the boolean state of (instance with instance_id)'s lock - - """ + """return the boolean state of (instance with instance_id)'s lock""" instance = self.get(context, instance_id) return instance['locked'] -- cgit From 5d9ad54cc38283d0b946779f4235f54370b12489 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 7 Jan 2011 15:50:43 -0500 Subject: incorporated changes suggested by eday --- nova/compute/api.py | 13 +++++++------ nova/compute/manager.py | 1 + 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index a894a0ce3..d90b59de6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -239,8 +239,8 @@ class API(base.Base): host = instance['host'] if host: - self._cast_compute_message('terminate_instance', context, - instance_id) + self._cast_compute_message('snapshot_instance', context, + instance_id, host) else: self.db.instance_destroy(context, instance_id) @@ -268,12 +268,13 @@ class API(base.Base): project_id) return self.db.instance_get_all(context) - def _cast_compute_message(self, method, context, instance_id): + def _cast_compute_message(self, method, context, instance_id, host=None): """Generic handler for RPC calls to compute.""" - instance = self.get(context, instance_id) - host = instance['host'] + if not host: + instance = self.get(context, instance_id) + host = instance['host'] queue = self.db.queue_get_for(context, FLAGS.compute_topic, host) - kwargs = {'method': method, 'args': {'instance_id': instance['id']}} + kwargs = {'method': method, 'args': {'instance_id': instance_id}} rpc.cast(context, queue, kwargs) def snapshot(self, context, instance_id, name): diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 5d677b023..7d4a097bd 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -285,6 +285,7 @@ class ComputeManager(manager.Manager): self.driver.snapshot(instance_ref, name) @exception.wrap_exception + @checks_instance_lock def set_admin_password(self, context, instance_id, new_pass=None): """Set the root/admin password for an instance on this server.""" context = context.elevated() -- cgit From b8fc639af336630c56ce3807639a5e26c0d07982 Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Fri, 7 Jan 2011 13:02:55 -0800 Subject: set the hostname factory in the service init --- nova/compute/api.py | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 821fb06d5..591d99271 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -41,28 +41,18 @@ def id_to_default_hostname(internal_id): return str(internal_id) -def id_to_ec2_hostname(internal_id): - digits = [] - while internal_id != 0: - internal_id, remainder = divmod(internal_id, 36) - digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[remainder]) - return "i-%s" % ''.join(reversed(digits)) - - -HOSTNAME_FORMATTERS = {'default': id_to_default_hostname, - 'ec2': id_to_ec2_hostname} - - class ComputeAPI(base.Base): """API for interacting with the compute manager.""" - def __init__(self, network_manager=None, image_service=None, **kwargs): + def __init__(self, network_manager=None, image_service=None, + hostname_factory=id_to_default_hostname, **kwargs): if not network_manager: network_manager = utils.import_object(FLAGS.network_manager) self.network_manager = network_manager if not image_service: image_service = utils.import_object(FLAGS.image_service) self.image_service = image_service + self.hostname_factory = hostname_factory super(ComputeAPI, self).__init__(**kwargs) def get_network_topic(self, context, instance_id): @@ -88,8 +78,7 @@ class ComputeAPI(base.Base): display_name='', description='', key_name=None, key_data=None, security_group='default', availability_zone=None, - user_data=None, - hostname_format='default'): + user_data=None): """Create the number of instances requested if quote and other arguments check out ok.""" @@ -160,7 +149,6 @@ class ComputeAPI(base.Base): elevated = context.elevated() instances = [] - generate_hostname = HOSTNAME_FORMATTERS[hostname_format] logging.debug(_("Going to run %s instances..."), num_instances) for num in range(num_instances): instance = dict(mac_address=utils.generate_mac(), @@ -179,7 +167,7 @@ class ComputeAPI(base.Base): security_group_id) # Set sane defaults if not specified - updates = dict(hostname=generate_hostname(internal_id)) + updates = dict(hostname=self.hostname_factory(internal_id)) if 'display_name' not in instance: updates['display_name'] = "Server %s" % internal_id -- cgit From 18b8d8307d0fc008f62dd8eeeedb351a954a3471 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 7 Jan 2011 15:51:28 -0600 Subject: removed a merge conflict line I missed before --- nova/compute/manager.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/compute') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 8b5646a54..21b09e443 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -37,7 +37,6 @@ terminating it. import datetime import random import string ->>>>>>> MERGE-SOURCE import functools from nova import exception -- cgit From 8fe01c087943ca9b46d25c84d4408b752461e6bd Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Fri, 7 Jan 2011 16:05:06 -0800 Subject: some small cleanups --- nova/compute/api.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 65e772d81..ffef20cee 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -253,11 +253,7 @@ class API(base.Base): def get(self, context, instance_id): """Get a single instance with the given ID.""" rv = self.db.instance_get_by_id(context, instance_id) - d = dict(rv.iteritems()) - # TODO(termie): this is ugly but required because the db layer returns - # models rather than dicts and the models support - # properties that are 'backreferences' - return d + return dict(rv.iteritems()) def get_all(self, context, project_id=None, reservation_id=None, fixed_ip=None): -- cgit From bae57e82767b4877bae5c2dcb6fe052291d16b32 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Mon, 10 Jan 2011 15:33:10 -0600 Subject: Fixed issues raised by reviews --- nova/compute/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index a20dc59cb..10d7b67cf 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -297,7 +297,7 @@ class API(base.Base): host = instance['host'] if host: - self._cast_compute_message('snapshot_instance', context, + self._cast_compute_message('terminate_instance', context, instance_id, host) else: self.db.instance_destroy(context, instance_id) -- cgit From 3419feff16e1974aa353188eee11609fc786148d Mon Sep 17 00:00:00 2001 From: termie Date: Wed, 12 Jan 2011 19:38:27 -0800 Subject: make sure get_all returns --- nova/compute/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index ffef20cee..630754395 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -261,8 +261,8 @@ class API(base.Base): given parameters. If there is no filter and the context is an admin, it will retreive all instances in the system.""" if reservation_id is not None: - rv = self.db.instance_get_all_by_reservation(context, - reservation_id) + return self.db.instance_get_all_by_reservation(context, + reservation_id) if fixed_ip is not None: return self.db.fixed_ip_get_instance(context, fixed_ip) if project_id or not context.is_admin: -- cgit From 7af8f5ac5fc02abe79dec3cf3651b6f0a9deb78c Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 13 Jan 2011 10:51:31 -0600 Subject: Minor code cleanups --- nova/compute/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/compute') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 267beca45..613ee45f6 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -125,7 +125,7 @@ class ComputeManager(manager.Manager): # FIXME(ja): include other fields from state? instance_ref = self.db.instance_get(context, instance_id) try: - info = self.driver.get_info(instance_ref) + info = self.driver.get_info(instance_ref['name']) state = info['state'] except exception.NotFound: state = power_state.NOSTATE -- cgit From 914b0554a092d2b38f292942dc4d7ddea5d99b9a Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 13 Jan 2011 15:13:31 -0800 Subject: Modified per sorens review. Moved disk.py Removed disk.partition Changed docstrings Use pid to find nbd devices --- nova/compute/disk.py | 247 --------------------------------------------------- 1 file changed, 247 deletions(-) delete mode 100644 nova/compute/disk.py (limited to 'nova/compute') diff --git a/nova/compute/disk.py b/nova/compute/disk.py deleted file mode 100644 index b9c34750d..000000000 --- a/nova/compute/disk.py +++ /dev/null @@ -1,247 +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. -""" -Utility methods to resize, repartition, and modify disk images. - -Includes injection of SSH PGP keys into authorized_keys file. - -""" - -import os -import tempfile -import time - -from nova import exception -from nova import flags -from nova import log as logging -from nova import utils - - -LOG = logging.getLogger('nova.compute.disk') -FLAGS = flags.FLAGS -flags.DEFINE_integer('minimum_root_size', 1024 * 1024 * 1024 * 10, - 'minimum size in bytes of root partition') -flags.DEFINE_integer('block_size', 1024 * 1024 * 256, - 'block_size to use for dd') - - -def partition(infile, outfile, local_bytes=0, resize=True, local_type='ext2'): - """ - Turns a partition (infile) into a bootable drive image (outfile). - - The first 63 sectors (0-62) of the resulting image is a master boot record. - Infile becomes the first primary partition. - If local bytes is specified, a second primary partition is created and - formatted as ext2. - - :: - - In the diagram below, dashes represent drive sectors. - +-----+------. . .-------+------. . .------+ - | 0 a| b c|d e| - +-----+------. . .-------+------. . .------+ - | mbr | primary partiton | local partition | - +-----+------. . .-------+------. . .------+ - - """ - sector_size = 512 - file_size = os.path.getsize(infile) - if resize and file_size < FLAGS.minimum_root_size: - last_sector = FLAGS.minimum_root_size / sector_size - 1 - utils.execute('dd if=/dev/zero of=%s count=1 seek=%d bs=%d' - % (infile, last_sector, sector_size)) - utils.execute('e2fsck -fp %s' % infile, check_exit_code=False) - utils.execute('resize2fs %s' % infile) - file_size = FLAGS.minimum_root_size - elif file_size % sector_size != 0: - LOG.warn(_("Input partition size not evenly divisible by" - " sector size: %d / %d"), file_size, sector_size) - primary_sectors = file_size / sector_size - if local_bytes % sector_size != 0: - LOG.warn(_("Bytes for local storage not evenly divisible" - " by sector size: %d / %d"), local_bytes, sector_size) - local_sectors = local_bytes / sector_size - - mbr_last = 62 # a - primary_first = mbr_last + 1 # b - primary_last = primary_first + primary_sectors - 1 # c - local_first = primary_last + 1 # d - local_last = local_first + local_sectors - 1 # e - last_sector = local_last # e - - # create an empty file - utils.execute('dd if=/dev/zero of=%s count=1 seek=%d bs=%d' - % (outfile, mbr_last, sector_size)) - - # make mbr partition - utils.execute('parted --script %s mklabel msdos' % outfile) - - # append primary file - utils.execute('dd if=%s of=%s bs=%s conv=notrunc,fsync oflag=append' - % (infile, outfile, FLAGS.block_size)) - - # make primary partition - utils.execute('parted --script %s mkpart primary %ds %ds' - % (outfile, primary_first, primary_last)) - - if local_bytes > 0: - # make the file bigger - utils.execute('dd if=/dev/zero of=%s count=1 seek=%d bs=%d' - % (outfile, last_sector, sector_size)) - # make and format local partition - utils.execute('parted --script %s mkpartfs primary %s %ds %ds' - % (outfile, local_type, local_first, local_last)) - - -def extend(image, size): - """Increase image to size""" - file_size = os.path.getsize(image) - if file_size >= size: - return - utils.execute('truncate -s %s %s' % (size, image)) - # NOTE(vish): attempts to resize filesystem - utils.execute('e2fsck -fp %s' % image, check_exit_code=False) - utils.execute('resize2fs %s' % image, check_exit_code=False) - - -def inject_data(image, key=None, net=None, partition=None, nbd=False): - """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 - into the specified partition number. - - If partition is not specified it mounts the image as a single partition. - - """ - device = _link_device(image, nbd) - try: - if not partition is None: - # create partition - out, err = utils.execute('sudo kpartx -a %s' % device) - if err: - raise exception.Error(_('Failed to load partition: %s') % err) - mapped_device = '/dev/mapper/%sp%s' % (device.split('/')[-1], - partition) - else: - mapped_device = device - - # We can only loopback mount raw images. If the device isn't there, - # it's normally because it's a .vmdk or a .vdi etc - if not os.path.exists(mapped_device): - raise exception.Error('Mapped device was not found (we can' - ' only inject raw disk images): %s' % - mapped_device) - - # Configure ext2fs so that it doesn't auto-check every N boots - out, err = utils.execute('sudo tune2fs -c 0 -i 0 %s' % mapped_device) - - tmpdir = tempfile.mkdtemp() - try: - # mount loopback to dir - out, err = utils.execute( - 'sudo mount %s %s' % (mapped_device, tmpdir)) - if err: - raise exception.Error(_('Failed to mount filesystem: %s') - % err) - - try: - if key: - # inject key file - _inject_key_into_fs(key, tmpdir) - if net: - _inject_net_into_fs(net, tmpdir) - finally: - # unmount device - utils.execute('sudo umount %s' % mapped_device) - finally: - # remove temporary directory - utils.execute('rmdir %s' % tmpdir) - if not partition is None: - # remove partitions - utils.execute('sudo kpartx -d %s' % device) - finally: - _unlink_device(device, nbd) - - -def _link_device(image, nbd): - """Link image to device using loopback or nbd""" - if nbd: - device = _allocate_device() - utils.execute('sudo qemu-nbd -c %s %s' % (device, image)) - # NOTE(vish): this forks into another process, so give it a chance - # to set up before continuuing - time.sleep(1) - return device - else: - out, err = utils.execute('sudo losetup --find --show %s' % image) - if err: - raise exception.Error(_('Could not attach image to loopback: %s') - % err) - return out.strip() - - -def _unlink_device(device, nbd): - """Unlink image from device using loopback or nbd""" - if nbd: - utils.execute('sudo qemu-nbd -d %s' % device) - _free_device(device) - else: - utils.execute('sudo losetup --detach %s' % device) - - -_DEVICES = ['/dev/nbd%s' % i for i in xrange(16)] - - -def _allocate_device(): - # NOTE(vish): This assumes no other processes are using nbd devices. - # It will race cause a race condition if multiple - # workers are running on a given machine. - if not _DEVICES: - raise exception.Error(_('No free nbd devices')) - return _DEVICES.pop() - - -def _free_device(device): - _DEVICES.append(device) - - -def _inject_key_into_fs(key, fs): - """Add the given public ssh key to root's authorized_keys. - - key is an ssh key string. - fs is the path to the base of the filesystem into which to inject the key. - """ - sshdir = os.path.join(fs, 'root', '.ssh') - utils.execute('sudo mkdir -p %s' % sshdir) # existing dir doesn't matter - utils.execute('sudo chown root %s' % sshdir) - utils.execute('sudo chmod 700 %s' % sshdir) - keyfile = os.path.join(sshdir, 'authorized_keys') - utils.execute('sudo tee -a %s' % keyfile, '\n' + key.strip() + '\n') - - -def _inject_net_into_fs(net, fs): - """Inject /etc/network/interfaces into the filesystem rooted at fs. - - net is the contents of /etc/network/interfaces. - """ - netdir = os.path.join(os.path.join(fs, 'etc'), 'network') - utils.execute('sudo mkdir -p %s' % netdir) # existing dir doesn't matter - utils.execute('sudo chown root:root %s' % netdir) - utils.execute('sudo chmod 755 %s' % netdir) - netfile = os.path.join(netdir, 'interfaces') - utils.execute('sudo tee %s' % netfile, net) -- cgit From cf0e5bd3eeb6b175b53df6ae0a0ef8957ec7ba13 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 14 Jan 2011 11:24:45 -0600 Subject: Create and use a generic handler for RPC calls --- nova/compute/api.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 90273da36..40b9e33e8 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -331,7 +331,7 @@ class API(base.Base): return self.db.instance_get_all(context) def _cast_compute_message(self, method, context, instance_id, host=None): - """Generic handler for RPC calls to compute.""" + """Generic handler for RPC casts to compute.""" if not host: instance = self.get(context, instance_id) host = instance['host'] @@ -339,6 +339,15 @@ class API(base.Base): kwargs = {'method': method, 'args': {'instance_id': instance_id}} rpc.cast(context, queue, kwargs) + def _call_compute_message(self, method, context, instance_id, host=None): + """Generic handler for RPC calls to compute.""" + if not host: + instance = self.get(context, instance_id) + host = instance["host"] + queue = self.db.queue_get_for(context, FLAGS.compute_topic, host) + kwargs = {"method": method, "args": {"instance_id": instance_id}} + return rpc.call(context, queue, kwargs) + def snapshot(self, context, instance_id, name): """Snapshot the given instance.""" self._cast_compute_message('snapshot_instance', context, instance_id) @@ -357,7 +366,10 @@ class API(base.Base): def get_diagnostics(self, context, instance_id): """Retrieve diagnostics for the given instance.""" - self._cast_compute_message('get_diagnostics', context, instance_id) + return self._call_compute_message( + "get_diagnostics", + context, + instance_id) def get_actions(self, context, instance_id): """Retrieve actions for the given instance.""" -- cgit From 76e875476848ee7f4aa483f65484903115e2bb49 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 14 Jan 2011 10:25:44 -0800 Subject: import re, remove extra call in cloud.py. Move get_console_output to compute_api --- nova/compute/api.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'nova/compute') diff --git a/nova/compute/api.py b/nova/compute/api.py index 90273da36..e6cfa459e 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -21,6 +21,7 @@ Handles all requests relating to instances (guest vms). """ import datetime +import re import time from nova import db @@ -385,23 +386,23 @@ class API(base.Base): def get_ajax_console(self, context, instance_id): """Get a url to an AJAX Console""" - instance = self.get(context, instance_id) - - output = rpc.call(context, - '%s.%s' % (FLAGS.compute_topic, - instance['host']), - {'method': 'get_ajax_console', - 'args': {'instance_id': instance['id']}}) - + output = self._call_compute_message('get_ajax_console', + context, + instance_id) rpc.cast(context, '%s' % FLAGS.ajax_console_proxy_topic, {'method': 'authorize_ajax_console', 'args': {'token': output['token'], 'host': output['host'], 'port': output['port']}}) - return {'url': '%s?token=%s' % (FLAGS.ajax_console_proxy_url, output['token'])} + def get_console_output(self, context, instance_id): + """Get console output for an an instance""" + return self._call_compute_message('get_console_output', + context, + instance_id) + def lock(self, context, instance_id): """lock the instance with instance_id""" self._cast_compute_message('lock_instance', context, instance_id) -- cgit