From 47bc72e5ec27bec349dcfc9468af6325f0a51019 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 12 Jan 2011 12:10:26 -0600 Subject: Start to add rescue/unrescue support --- nova/api/openstack/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index f96e2af91..a9b01548a 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -95,6 +95,8 @@ class APIRouter(wsgi.Router): server_members["actions"] = "GET" server_members['suspend'] = 'POST' server_members['resume'] = 'POST' + server_members['rescue'] = 'POST' + server_members['unrescue'] = 'POST' mapper.resource("server", "servers", controller=servers.Controller(), collection={'detail': 'GET'}, -- cgit From 7f2a4fdf5e43620081e163fc46f2ca4fdefd18f3 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 12 Jan 2011 15:07:51 -0600 Subject: Make rescue/unrescue available to API --- nova/api/openstack/servers.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 29af82533..3a6c61a3a 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -283,6 +283,28 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + def rescue(self, req, id): + """Permit users to rescue the server.""" + context = req.environ["nova.context"] + try: + self.compute_api.rescue(context, id) + except: + readable = traceback.format_exc() + LOG.exception(_("compute.api::rescue %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + + def unrescue(self, req, id): + """Permit users to unrescue the server.""" + context = req.environ["nova.context"] + try: + self.compute_api.unrescue(context, id) + except: + readable = traceback.format_exc() + LOG.exception(_("compute.api::unrescue %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + def get_ajax_console(self, req, id): """ Returns a url to an instance's ajaxterm console. """ try: -- cgit From 752bed3311f09e7a43e642231e1638b4252f74a6 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 13 Jan 2011 10:44:29 -0600 Subject: Stubbed out XenServer rescue/unrescue --- nova/virt/xenapi/vmops.py | 8 ++++++++ nova/virt/xenapi_conn.py | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7e3585991..8681608e1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -264,6 +264,14 @@ class VMOps(object): task = self._session.call_xenapi('Async.VM.resume', vm, False, True) self._wait_with_callback(task, callback) + def rescue(self, instance, callback): + """Rescue the specified instance""" + return True + + def unrescue(self, instance, callback): + """Unrescue the specified instance""" + return True + def get_info(self, instance_id): """Return data about VM instance""" vm = VMHelper.lookup(self._session, instance_id) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 45d0738a5..c24ec972b 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -169,6 +169,14 @@ class XenAPIConnection(object): """resume the specified instance""" self._vmops.resume(instance, callback) + def rescue(self, instance, callback): + """Rescue the specified instance""" + self._vmops.rescue(instance, callback) + + def unrescue(self, instance, callback): + """Unrescue the specified instance""" + self._vmops.unrescue(instance, callback) + def get_info(self, instance_id): """Return data about VM instance""" return self._vmops.get_info(instance_id) -- cgit From 702d1bd5e58c15e5b7f43e9d56bd591d728ecb71 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 13 Jan 2011 10:47:23 -0600 Subject: Make driver calls compatible --- nova/virt/libvirt_conn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c03046703..797ac1b60 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -328,7 +328,7 @@ class LibvirtConnection(object): raise exception.APIError("resume not supported for libvirt") @exception.wrap_exception - def rescue(self, instance): + def rescue(self, instance, callback=None): self.destroy(instance, False) xml = self.to_xml(instance, rescue=True) @@ -358,7 +358,7 @@ class LibvirtConnection(object): return timer.start(interval=0.5, now=True) @exception.wrap_exception - def unrescue(self, instance): + def unrescue(self, instance, callback=None): # NOTE(vish): Because reboot destroys and recreates an instance using # the normal xml file, we can just call reboot here self.reboot(instance) -- cgit From ff6606938749ce5f1a8e430b24d279cde7556c1b Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 13 Jan 2011 11:24:11 -0600 Subject: Make libvirt and XenAPI play nice together --- nova/compute/manager.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6b2fc4adb..f1fdd64e6 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -321,7 +321,11 @@ class ComputeManager(manager.Manager): power_state.NOSTATE, 'rescuing') self.network_manager.setup_compute_network(context, instance_id) - self.driver.rescue(instance_ref) + self.driver.rescue(instance_ref, + lambda result: self._update_state_callback(self, + context, + instance_id, + result)) self._update_state(context, instance_id) @exception.wrap_exception @@ -335,7 +339,11 @@ class ComputeManager(manager.Manager): instance_id, power_state.NOSTATE, 'unrescuing') - self.driver.unrescue(instance_ref) + self.driver.unrescue(instance_ref, + lambda result: self._update_state_callback(self, + context, + instance_id, + result)) self._update_state(context, instance_id) @staticmethod -- cgit From 912e4343cf2622fa42aa4e1c5eac392ce1be96e0 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 14 Jan 2011 11:49:59 -0600 Subject: Create and use a generic handler for RPC calls to compute. --- nova/compute/api.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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 3300e692b61dc53ac8ae3bfdbac5bb1019983feb Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 17 Jan 2011 12:21:08 -0600 Subject: Add Start/Shutdown support to XenAPI --- nova/virt/xenapi/vmops.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 96be17d8c..0e6018973 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -207,6 +207,25 @@ class VMOps(object): logging.debug(_("Finished snapshot and upload for VM %s"), instance) + def start(self, instance): + """Start a VM instance""" + vm = self._get_vm_opaque_ref(instance) + task = self._session.call_xenapi("Async.VM.start", vm, False, False) + self._session.wait_for_task(instance.id, task) + + def shutdown(self, instance, hard=True): + """Shutdown a VM instance""" + vm = self._get_vm_opaque_ref(instance) + if hard: + task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) + else: + # TODO(jk0): clean_shutdown is only supported if XenTools is + # installed and running. What we want to do eventually is try + # this first, and only issue the hard_shutdown if clean_shutdown + # were to fail. + task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) + self._session.wait_for_task(instance.id, task) + def reboot(self, instance): """Reboot VM instance""" vm = self._get_vm_opaque_ref(instance) @@ -320,11 +339,11 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" - return True + self.shutdown(instance) def unrescue(self, instance, callback): """Unrescue the specified instance""" - return True + self.start(instance) def get_info(self, instance): """Return data about VM instance""" -- cgit From 8c79b0c1995bd9d061c1c379c0034f49cbdb8d05 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 17 Jan 2011 13:31:05 -0600 Subject: Better shutdown handling --- nova/virt/xenapi/vmops.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 041e2c5bb..f59c1af44 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -213,18 +213,16 @@ class VMOps(object): task = self._session.call_xenapi("Async.VM.start", vm, False, False) self._session.wait_for_task(instance.id, task) - def shutdown(self, instance, hard=True): + def shutdown(self, instance): """Shutdown a VM instance""" vm = self._get_vm_opaque_ref(instance) - if hard: - task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) - else: - # TODO(jk0): clean_shutdown is only supported if XenTools is - # installed and running. What we want to do eventually is try - # this first, and only issue the hard_shutdown if clean_shutdown - # were to fail. + + try: task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(instance.id, task) + except self.XenAPI.Failure: + task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) + self._session.wait_for_task(instance.id, task) def reboot(self, instance): """Reboot VM instance""" -- cgit From ecc2afda9fed4e9e69edcc470baf254fac448ce7 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 18 Jan 2011 15:49:42 -0600 Subject: Plug VBD to existing instance and minor cleanup --- nova/tests/xenapi/stubs.py | 2 +- nova/virt/xenapi/fake.py | 2 +- nova/virt/xenapi/vm_utils.py | 12 +++++------- nova/virt/xenapi/vmops.py | 45 +++++++++++++++++++++++++++++++++---------- nova/virt/xenapi/volumeops.py | 2 +- nova/virt/xenapi_conn.py | 15 +++++++++------ 6 files changed, 52 insertions(+), 26 deletions(-) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 292bd9ba9..313668826 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -37,7 +37,7 @@ def stubout_instance_snapshot(stubs): return self.rv done = FakeEvent() - self._poll_task(id, task, done) + self._poll_task(task, id, done) rv = done.wait() return rv diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 96d8f5fc8..92ccfd975 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -335,7 +335,7 @@ class SessionBase(object): field in _db_content[cls][ref]): return _db_content[cls][ref][field] - LOG.debuug(_('Raising NotImplemented')) + LOG.debug(_('Raising NotImplemented')) raise NotImplementedError( _('xenapi.fake does not have an implementation for %s or it has ' 'been called with the wrong number of arguments') % name) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index eb0393d2a..d5c6a85b4 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -179,9 +179,7 @@ class VMHelper(HelperBase): """Destroy VBD from host database""" try: task = session.call_xenapi('Async.VBD.destroy', vbd_ref) - #FIXME(armando): find a solution to missing instance_id - #with Josh Kearney - session.wait_for_task(0, task) + session.wait_for_task(task) except cls.XenAPI.Failure, exc: LOG.exception(exc) raise StorageError(_('Unable to destroy VBD %s') % vbd_ref) @@ -222,7 +220,7 @@ class VMHelper(HelperBase): original_parent_uuid = get_vhd_parent_uuid(session, vm_vdi_ref) task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) - template_vm_ref = session.wait_for_task(instance_id, task) + template_vm_ref = session.wait_for_task(task, instance_id) template_vdi_rec = get_vdi_for_vm_safely(session, template_vm_ref)[1] template_vdi_uuid = template_vdi_rec["uuid"] @@ -250,7 +248,7 @@ class VMHelper(HelperBase): kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'put_vdis', kwargs) - session.wait_for_task(instance_id, task) + session.wait_for_task(task, instance_id) @classmethod def fetch_image(cls, session, instance_id, image, user, project, type): @@ -272,7 +270,7 @@ class VMHelper(HelperBase): if type == ImageType.DISK_RAW: args['raw'] = 'true' task = session.async_call_plugin('objectstore', fn, args) - uuid = session.wait_for_task(instance_id, task) + uuid = session.wait_for_task(task, instance_id) return uuid @classmethod @@ -409,7 +407,7 @@ def get_vhd_parent_uuid(session, vdi_ref): def scan_sr(session, instance_id, sr_ref): LOG.debug(_("Re-scanning SR %s"), sr_ref) task = session.call_xenapi('Async.SR.scan', sr_ref) - session.wait_for_task(instance_id, task) + session.wait_for_task(task, instance_id) def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index f59c1af44..0de7c8d42 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -211,24 +211,23 @@ class VMOps(object): """Start a VM instance""" vm = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi("Async.VM.start", vm, False, False) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) def shutdown(self, instance): """Shutdown a VM instance""" vm = self._get_vm_opaque_ref(instance) - try: task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure: task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) def reboot(self, instance): """Reboot VM instance""" vm = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.clean_reboot', vm) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) def set_admin_password(self, instance, new_pass): """Set the root/admin password on the VM instance. This is done via @@ -284,7 +283,7 @@ class VMOps(object): if shutdown: try: task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure, exc: LOG.exception(exc) @@ -293,20 +292,20 @@ class VMOps(object): for vdi in vdis: try: task = self._session.call_xenapi('Async.VDI.destroy', vdi) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure, exc: LOG.exception(exc) # VM Destroy try: task = self._session.call_xenapi('Async.VM.destroy', vm) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure, exc: LOG.exception(exc) def _wait_with_callback(self, instance_id, task, callback): ret = None try: - ret = self._session.wait_for_task(instance_id, task) + ret = self._session.wait_for_task(task, instance_id) except self.XenAPI.Failure, exc: LOG.exception(exc) callback(ret) @@ -337,10 +336,36 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" + vm = self._get_vm_opaque_ref(instance) + target_vm = VMHelper.lookup(self._session, "instance-00000001") + self.shutdown(instance) + vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] + vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] + vbd_ref = VMHelper.create_vbd( + self._session, + target_vm, + vdi_ref, + 1, + False) + + # Plug the VBD into the target instance + self._session.call_xenapi("Async.VBD.plug", vbd_ref) + def unrescue(self, instance, callback): """Unrescue the specified instance""" + vm = self._get_vm_opaque_ref(instance) + target_vm = VMHelper.lookup(self._session, "instance-00000001") + + vbds = self._session.get_xenapi().VM.get_VBDs(target_vm) + + for vbd_ref in vbds: + vbd = self._session.get_xenapi().VBD.get_record(vbd_ref) + if vbd["userdevice"] == str(1): + VMHelper.unplug_vbd(self._session, vbd_ref) + VMHelper.destroy_vbd(self._session, vbd_ref) + self.start(instance) def get_info(self, instance): @@ -425,7 +450,7 @@ class VMOps(object): args.update(addl_args) try: task = self._session.async_call_plugin(plugin, method, args) - ret = self._session.wait_for_task(instance_id, task) + ret = self._session.wait_for_task(task, instance_id) except self.XenAPI.Failure, e: ret = None err_trace = e.details[-1] diff --git a/nova/virt/xenapi/volumeops.py b/nova/virt/xenapi/volumeops.py index 189f968c6..0c5f36f6b 100644 --- a/nova/virt/xenapi/volumeops.py +++ b/nova/virt/xenapi/volumeops.py @@ -85,7 +85,7 @@ class VolumeOps(object): try: task = self._session.call_xenapi('Async.VBD.plug', vbd_ref) - self._session.wait_for_task(vol_rec['deviceNumber'], task) + self._session.wait_for_task(task, vol_rec['deviceNumber']) except self.XenAPI.Failure, exc: LOG.exception(exc) VolumeHelper.destroy_iscsi_storage(self._session, diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index e9793e6a7..07bc0fd84 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -290,7 +290,7 @@ class XenAPISession(object): self._session.xenapi.Async.host.call_plugin, self.get_xenapi_host(), plugin, fn, args) - def wait_for_task(self, id, task): + def wait_for_task(self, task, id=None): """Return the result of the given task. The task is polled until it completes. Not re-entrant.""" done = event.Event() @@ -317,10 +317,11 @@ class XenAPISession(object): try: name = self._session.xenapi.task.get_name_label(task) status = self._session.xenapi.task.get_status(task) - action = dict( - instance_id=int(id), - action=name[0:255], # Ensure action is never > 255 - error=None) + if id: + action = dict( + instance_id=int(id), + action=name[0:255], # Ensure action is never > 255 + error=None) if status == "pending": return elif status == "success": @@ -339,7 +340,9 @@ class XenAPISession(object): status, error_info)) done.send_exception(self.XenAPI.Failure(error_info)) - db.instance_action_create(context.get_admin_context(), action) + + if id: + db.instance_action_create(context.get_admin_context(), action) except self.XenAPI.Failure, exc: LOG.warn(exc) done.send_exception(*sys.exc_info()) -- cgit From 2c0f1d78927c14f1d155e617a066b09a00acb100 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 18 Jan 2011 17:40:36 -0600 Subject: Basic stubbing throughout the stack --- nova/api/openstack/servers.py | 37 ++++++++++++++++++++++++++++++++++++- nova/compute/api.py | 14 ++++++++++++++ nova/compute/manager.py | 13 +++++++++++++ nova/virt/fake.py | 12 ++++++++++++ nova/virt/xenapi/vmops.py | 4 ++++ nova/virt/xenapi_conn.py | 4 ++++ 6 files changed, 83 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 8cbcebed2..06104b37e 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -182,9 +182,44 @@ class Controller(wsgi.Controller): return exc.HTTPNoContent() def action(self, req, id): - """ Multi-purpose method used to reboot, rebuild, and + """ Multi-purpose method used to reboot, rebuild, or resize a server """ + + actions = { + 'reboot':self._action_reboot, + 'resize':self._action_resize, + 'confirmResize':self._action_confirm_resize, + 'revertResize':self._action_revert_resize, + 'rebuild':self._action_rebuild + } + input_dict = self._deserialize(req.body, req) + for key in actions.keys(): + if key in input_dict: + return actions[key](input_dict, req, id) + return faults.Fault(exc.HTTPNotImplemented()) + + def _action_confirm_resize(self, input_dict, req, id): + return fault.Fault(exc.HTTPNotImplemented()) + + def _action_revert_resize(self, input_dict, req, id): + return fault.Fault(exc.HTTPNotImplemented()) + + def _action_rebuild(self, input_dict, req, id): + return fault.Fault(exc.HTTPNotImplemented()) + + def _action_resize(self, input_dict, req, id): + """ Resizes a given instance to the flavor size requested """ + try: + resize_flavor = input_dict['resize']['flavorId'] + self.compute_api.resize(req.environ['nova.context'], id, + flavor_id) + except: + return faults.Fault(exc.HTTPUnprocessableEntity()) + return fault.Fault(exc.HTTPAccepted()) + + + def _action_reboot(self, input_dict, req, id): #TODO(sandy): rebuild/resize not supported. try: reboot_type = input_dict['reboot']['type'] diff --git a/nova/compute/api.py b/nova/compute/api.py index cc85ec691..22d5964c6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -361,6 +361,20 @@ class API(base.Base): """Reboot the given instance.""" self._cast_compute_message('reboot_instance', context, instance_id) + def revert_resize(self, context, instance_id): + """Reverts a resize, deleting the 'new' instance in the process""" + raise NotImplemented() + + def confirm_resize(self, context, instance_id): + """Confirms a migration/resize, deleting the 'old' instance in the + process.""" + raise NotImplemented() + + def resize(self, context, instance_id, flavor_id): + """Resize a running instance.""" + self._cast_compute_message('resize_instance', context, instance_id, + flavor_id) + def pause(self, context, instance_id): """Pause the given instance.""" self._cast_compute_message('pause_instance', context, instance_id) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 613ee45f6..714cec209 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -376,6 +376,19 @@ class ComputeManager(manager.Manager): """Update instance state when async task completes.""" self._update_state(context, instance_id) + @exception.wrap_exception + @checks_instance_lock + def resize_instance(self, context, instance_id, flavor_id): + """Moves a running instance to another host, possibly changing the RAM + and disk size in the process""" + context = context.elevated() + instance_ref = self.db.instance_get(context. instance_id) + LOG.audit(_('instance %s: migrating'), instance_id, context=context) + self.db.instance_set_state(context, instance_id, power_state.RUNNING, + 'migrating') + self.driver.resize(instance_ref, flavor_id) + self._update_state(context, instance_id) + @exception.wrap_exception @checks_instance_lock def pause_instance(self, context, instance_id): diff --git a/nova/virt/fake.py b/nova/virt/fake.py index a57a8f43b..a6fe24c3e 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -138,6 +138,18 @@ class FakeConnection(object): """ pass + 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. diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6e359ef82..50ee24325 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -207,6 +207,10 @@ class VMOps(object): logging.debug(_("Finished snapshot and upload for VM %s"), instance) + def resize(self, instance, flavor): + """Resize a running instance by changing it's RAM and disk size """ + raise NotImplementedError() + def reboot(self, instance): """Reboot VM instance""" vm = self._get_vm_opaque_ref(instance) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 689844f34..c850dbf80 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -145,6 +145,10 @@ class XenAPIConnection(object): """ Create snapshot from a running VM instance """ self._vmops.snapshot(instance, name) + def resize(self, instance, flavor): + """Resize a VM instance""" + raise NotImplementedError() + def reboot(self, instance): """Reboot VM instance""" self._vmops.reboot(instance) -- cgit From 2b2f08dc1dfe1b55433c9122d7d42a480cdb5e67 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 18 Jan 2011 17:57:11 -0600 Subject: Fixed unit tests --- nova/tests/xenapi/stubs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index c1ba25e10..13603717c 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -27,7 +27,7 @@ def stubout_instance_snapshot(stubs): def fake_fetch_image(cls, session, instance_id, image, user, project, type): # Stubout wait_for_task - def fake_wait_for_task(self, id, task): + def fake_wait_for_task(self, task, id=None): class FakeEvent: def send(self, value): @@ -37,7 +37,7 @@ def stubout_instance_snapshot(stubs): return self.rv done = FakeEvent() - self._poll_task(task, id, done) + self._poll_task(id, task, done) rv = done.wait() return rv -- cgit From bb727b7032104d3d3966108d846dd3e5b8a1a37d Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 18 Jan 2011 17:59:26 -0600 Subject: Fix merge conflict --- nova/virt/xenapi/vm_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 9600584b4..5f5ac254d 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -329,7 +329,7 @@ class VMHelper(HelperBase): #let the plugin copy the correct number of bytes args['image-size'] = str(vdi_size) task = session.async_call_plugin('glance', fn, args) - filename = session.wait_for_task(instance_id, task) + filename = session.wait_for_task(task, instance_id) #remove the VDI as it is not needed anymore session.get_xenapi().VDI.destroy(vdi) LOG.debug(_("Kernel/Ramdisk VDI %s destroyed"), vdi) -- cgit From 4b77a532fd641947c9259327cef9104f689f1127 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 18 Jan 2011 18:09:58 -0600 Subject: Fixed unit tests --- nova/tests/xenapi/stubs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 13603717c..66d232a7f 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -27,7 +27,7 @@ def stubout_instance_snapshot(stubs): def fake_fetch_image(cls, session, instance_id, image, user, project, type): # Stubout wait_for_task - def fake_wait_for_task(self, task, id=None): + def fake_wait_for_task(self, task, id): class FakeEvent: def send(self, value): -- cgit From 9bd72f56224a8cc980620b17210d9b9b9ede6166 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 18 Jan 2011 18:33:04 -0800 Subject: various fixes to smoketests, including allowing admin tests to run as a user, better timing, and allowing volume tests to run on non-udev linux --- smoketests/flags.py | 2 +- smoketests/user_smoketests.py | 28 ++++++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/smoketests/flags.py b/smoketests/flags.py index 35f432a77..9dc310692 100644 --- a/smoketests/flags.py +++ b/smoketests/flags.py @@ -35,5 +35,5 @@ DEFINE_bool = DEFINE_bool # http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#39 DEFINE_string('region', 'nova', 'Region to use') -DEFINE_string('test_image', 'ami-tiny', 'Image to use for launch tests') +DEFINE_string('test_image', 'ami-tty', 'Image to use for launch tests') DEFINE_string('use_ipv6', True, 'use the ipv6 or not') diff --git a/smoketests/user_smoketests.py b/smoketests/user_smoketests.py index d5a3a7556..f73ab7e1c 100644 --- a/smoketests/user_smoketests.py +++ b/smoketests/user_smoketests.py @@ -258,10 +258,15 @@ class VolumeTests(UserSmokeTestCase): instance = reservation.instances[0] self.data['instance'] = instance for x in xrange(120): - if self.can_ping(instance.private_dns_name): + time.sleep(1) + instance.update() + #if self.can_ping(instance.private_dns_name): + if instance.state == u'running': break else: self.fail('unable to start instance') + time.sleep(10) + instance.update() def test_001_can_create_volume(self): volume = self.conn.create_volume(1, 'nova') @@ -273,10 +278,11 @@ class VolumeTests(UserSmokeTestCase): def test_002_can_attach_volume(self): volume = self.data['volume'] - for x in xrange(10): - if volume.status == u'available': + for x in xrange(30): + print volume.status + if volume.status.startswith('available'): break - time.sleep(5) + time.sleep(1) volume.update() else: self.fail('cannot attach volume with state %s' % volume.status) @@ -285,12 +291,12 @@ class VolumeTests(UserSmokeTestCase): # Volumes seems to report "available" too soon. for x in xrange(10): - if volume.status == u'in-use': + if volume.status.startswith('in-use'): break time.sleep(5) volume.update() - self.assertEqual(volume.status, u'in-use') + self.assertTrue(volume.status.startswith('in-use')) # Give instance time to recognize volume. time.sleep(5) @@ -298,9 +304,15 @@ class VolumeTests(UserSmokeTestCase): def test_003_can_mount_volume(self): ip = self.data['instance'].private_dns_name conn = self.connect_ssh(ip, TEST_KEY) + # NOTE(vish): this will create an dev for images that don't have + # udev rules + stdin, stdout, stderr = conn.exec_command( + 'grep %s /proc/partitions | ' + '`awk \'{print "mknod /dev/"\\$4" b "\\$1" "\\$2}\'`' + % self.device.rpartition('/')[2]) commands = [] commands.append('mkdir -p /mnt/vol') - commands.append('mkfs.ext2 %s' % self.device) + commands.append('/sbin/mke2fs %s' % self.device) commands.append('mount %s /mnt/vol' % self.device) commands.append('echo success') stdin, stdout, stderr = conn.exec_command(' && '.join(commands)) @@ -327,7 +339,7 @@ class VolumeTests(UserSmokeTestCase): "df -h | grep %s | awk {'print $2'}" % self.device) out = stdout.read() conn.close() - if not out.strip() == '1008M': + if not out.strip() == '1007.9M': self.fail('Volume is not the right size: %s %s' % (out, stderr.read())) -- cgit From dd70c9f3909c800d72d73d507e9da05a6ed932de Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 19 Jan 2011 11:20:16 -0600 Subject: Fixed unit tests --- nova/virt/xenapi/vm_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 5f5ac254d..7484472d7 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -371,7 +371,7 @@ class VMHelper(HelperBase): args = {} args['vdi-ref'] = vdi_ref task = session.async_call_plugin('objectstore', fn, args) - pv_str = session.wait_for_task(instance_id, task) + pv_str = session.wait_for_task(task, instance_id) pv = None if pv_str.lower() == 'true': pv = True -- cgit From 2f4258d99e8d97ec70645cd2df2f4e54dc869e89 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 20 Jan 2011 00:14:42 -0800 Subject: more smoketest fixes --- nova/__init__.py | 2 -- smoketests/base.py | 7 +++++-- smoketests/flags.py | 2 +- smoketests/user_smoketests.py | 26 ++++++++++++++++---------- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/nova/__init__.py b/nova/__init__.py index 8745617bc..256db55a9 100644 --- a/nova/__init__.py +++ b/nova/__init__.py @@ -30,5 +30,3 @@ .. moduleauthor:: Manish Singh .. moduleauthor:: Andy Smith """ - -from exception import * diff --git a/smoketests/base.py b/smoketests/base.py index 610270c5c..89ee92840 100644 --- a/smoketests/base.py +++ b/smoketests/base.py @@ -17,12 +17,10 @@ # under the License. import boto -import boto_v6 import commands import httplib import os import paramiko -import random import sys import unittest from boto.ec2.regioninfo import RegionInfo @@ -30,6 +28,8 @@ from boto.ec2.regioninfo import RegionInfo from smoketests import flags FLAGS = flags.FLAGS +boto_v6 = None + class SmokeTestCase(unittest.TestCase): @@ -146,6 +146,9 @@ class SmokeTestCase(unittest.TestCase): def run_tests(suites): argv = FLAGS(sys.argv) + if FLAGS.use_ipv6: + global boto_v6 + boto_v6 = __import__('boto_v6') if not os.getenv('EC2_ACCESS_KEY'): print >> sys.stderr, 'Missing EC2 environment variables. Please ' \ diff --git a/smoketests/flags.py b/smoketests/flags.py index 9dc310692..dc756347b 100644 --- a/smoketests/flags.py +++ b/smoketests/flags.py @@ -36,4 +36,4 @@ DEFINE_bool = DEFINE_bool DEFINE_string('region', 'nova', 'Region to use') DEFINE_string('test_image', 'ami-tty', 'Image to use for launch tests') -DEFINE_string('use_ipv6', True, 'use the ipv6 or not') +DEFINE_bool('use_ipv6', True, 'use the ipv6 or not') diff --git a/smoketests/user_smoketests.py b/smoketests/user_smoketests.py index f73ab7e1c..e5bc98ede 100644 --- a/smoketests/user_smoketests.py +++ b/smoketests/user_smoketests.py @@ -189,8 +189,8 @@ class InstanceTests(UserSmokeTestCase): try: conn = self.connect_ssh(self.data['private_ip'], TEST_KEY) conn.close() - except Exception: - time.sleep(1) + except Exception, e: + time.sleep(5) else: break else: @@ -224,7 +224,7 @@ class InstanceTests(UserSmokeTestCase): try: conn = self.connect_ssh(self.data['public_ip'], TEST_KEY) conn.close() - except socket.error: + except Exception: time.sleep(1) else: break @@ -256,17 +256,24 @@ class VolumeTests(UserSmokeTestCase): instance_type='m1.tiny', key_name=TEST_KEY) instance = reservation.instances[0] - self.data['instance'] = instance for x in xrange(120): time.sleep(1) instance.update() - #if self.can_ping(instance.private_dns_name): - if instance.state == u'running': + if self.can_ping(instance.private_dns_name): break else: self.fail('unable to start instance') - time.sleep(10) - instance.update() + self.data['instance'] = instance + for x in xrange(30): + try: + conn = self.connect_ssh(instance.private_dns_name, TEST_KEY) + conn.close() + except Exception: + time.sleep(5) + else: + break + else: + self.fail('could not ssh to instance') def test_001_can_create_volume(self): volume = self.conn.create_volume(1, 'nova') @@ -279,7 +286,6 @@ class VolumeTests(UserSmokeTestCase): volume = self.data['volume'] for x in xrange(30): - print volume.status if volume.status.startswith('available'): break time.sleep(1) @@ -438,7 +444,7 @@ class SecurityGroupTests(UserSmokeTestCase): if __name__ == "__main__": suites = {'image': unittest.makeSuite(ImageTests), 'instance': unittest.makeSuite(InstanceTests), - 'security_group': unittest.makeSuite(SecurityGroupTests), + #'security_group': unittest.makeSuite(SecurityGroupTests), 'volume': unittest.makeSuite(VolumeTests) } sys.exit(base.run_tests(suites)) -- cgit From 089bdfa8c2f0f116b55c69bbcde6fca6632cb145 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 20 Jan 2011 15:20:23 -0600 Subject: should be writing some kindof network info to the xenstore now, hopefully --- nova/db/api.py | 5 +++++ nova/db/sqlalchemy/api.py | 6 ++++++ nova/db/sqlalchemy/models.py | 1 + nova/virt/xenapi/vmops.py | 39 +++++++++++++++++++++++++++++++-------- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/nova/db/api.py b/nova/db/api.py index f9d561587..7b37bce2f 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -501,6 +501,11 @@ def network_get(context, network_id): return IMPL.network_get(context, network_id) +def network_get_all(context): + """Get all networks""" + returm IMPL.network_get_all(context) + + # pylint: disable-msg=C0103 def network_get_associated_fixed_ips(context, network_id): """Get all network's ips that have been associated.""" diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index b63b84bed..053780158 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1054,6 +1054,12 @@ def network_get(context, network_id, session=None): return result +@require_context +def network_get_all(context): + session = get_session() + return session.query(models.Network).all() + + # NOTE(vish): pylint complains because of the long method name, but # it fits with the names of the rest of the methods # pylint: disable-msg=C0103 diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index c54ebe3ba..dc476acf4 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -369,6 +369,7 @@ class Network(BASE, NovaBase): "vpn_public_port"), {'mysql_engine': 'InnoDB'}) id = Column(Integer, primary_key=True) + label = Column(String(255)) injected = Column(Boolean, default=False) cidr = Column(String(255), unique=True) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6c2fd6a68..882b9d9d6 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -67,11 +67,6 @@ class VMOps(object): raise exception.Duplicate(_('Attempted to create' ' non-unique name %s') % instance.name) - bridge = db.network_get_by_instance(context.get_admin_context(), - instance['id'])['bridge'] - network_ref = \ - NetworkHelper.find_network_with_bridge(self._session, bridge) - user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) #if kernel is not present we must download a raw disk @@ -99,9 +94,29 @@ class VMOps(object): instance, kernel, ramdisk, pv_kernel) VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True) - if network_ref: - VMHelper.create_vif(self._session, vm_ref, - network_ref, instance.mac_address) + # write network info + network = db.network_get_by_instance(context.get_admin_context(), + instance['id']) + for network in db.network_get_all(): + mapping = {'label': network['label'], + 'gateway': network['gateway'], + 'mac': instance.mac_address, + 'dns': network['dns'], + 'ips': [{'netmask': network['netmask'], + 'enabled': '1', + 'ip': 192.168.3.3}]} # <===== CHANGE!!!! + self.write_network_config_to_xenstore(vm_ref, mapping) + + bridge = network['bridge'] + network_ref = \ + NetworkHelper.find_network_with_bridge(self._session, bridge) + + if network_ref: + VMHelper.create_vif(self._session, vm_ref, + network_ref, instance.mac_address) + + # call reset networking + LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) LOG.info(_('Spawning VM %s created %s.'), instance.name, vm_ref) @@ -341,6 +356,14 @@ class VMOps(object): # TODO: implement this! return 'http://fakeajaxconsole/fake_url' + def reset_networking(self, instance): + vm = self._get_vm_opaque_ref(instance) + self.write_to_xenstore(vm, "resetnetwork", "") + + def write_network_config_to_xenstore(self, instance): + vm = self._get_vm_opaque_ref(instance) + self.write_to_param_xenstore(vm, mapping) + def list_from_xenstore(self, vm, path): """Runs the xenstore-ls command to get a listing of all records from 'path' downward. Returns a dict with the sub-paths as keys, -- cgit From 9b993d50835c79d23dca422335de362ebaf7f4fa Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 20 Jan 2011 15:47:08 -0600 Subject: added plugin call for resetnetworking --- nova/virt/xenapi/vmops.py | 3 ++- plugins/xenserver/xenapi/etc/xapi.d/plugins/agent | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 882b9d9d6..7f9e78df5 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -358,7 +358,8 @@ class VMOps(object): def reset_networking(self, instance): vm = self._get_vm_opaque_ref(instance) - self.write_to_xenstore(vm, "resetnetwork", "") + args = {'id': str(uuid.uuid4())} + resp = self._make_agent_call('resetnetwork', vm, '', args) def write_network_config_to_xenstore(self, instance): vm = self._get_vm_opaque_ref(instance) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent index 12c3a19c8..5c5ec7c45 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent @@ -91,6 +91,18 @@ def password(self, arg_dict): return resp +@jsonify +def resetnetwork(self, arg_dict): + """ + writes a resquest to xenstore that tells the agent to reset the networking + + """ + arg_dict['value'] = json.dumps({'name': 'resetnetwork', 'value': ''}) + request_id = arg_dict['id'] + arg_dict['path'] = "data/host/%s" % request_id + xenstore.write_record(self, arg_dict) + + def _wait_for_agent(self, request_id, arg_dict): """Periodically checks xenstore for a response from the agent. The request is always written to 'data/host/{id}', and -- cgit From 8d1798008fcec536f1117a275b168ca449f1dfbf Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 20 Jan 2011 16:19:07 -0600 Subject: syntax error --- nova/db/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/api.py b/nova/db/api.py index 7b37bce2f..f22cd5615 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -503,7 +503,7 @@ def network_get(context, network_id): def network_get_all(context): """Get all networks""" - returm IMPL.network_get_all(context) + return IMPL.network_get_all(context) # pylint: disable-msg=C0103 -- cgit From f77043d44aa640e1811a3fe236fc8fd5dfecf990 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 20 Jan 2011 16:27:09 -0600 Subject: syntax --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7f9e78df5..1045d5d98 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -104,7 +104,7 @@ class VMOps(object): 'dns': network['dns'], 'ips': [{'netmask': network['netmask'], 'enabled': '1', - 'ip': 192.168.3.3}]} # <===== CHANGE!!!! + 'ip': '192.168.3.3'}]} # <===== CHANGE!!!! self.write_network_config_to_xenstore(vm_ref, mapping) bridge = network['bridge'] -- cgit From f38196b0eb7a11501f9b0ffa9409c05510798761 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 20 Jan 2011 18:08:01 -0600 Subject: added default label to nova-manage and create_networks --- bin/nova-manage | 5 +++-- nova/network/manager.py | 8 +++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index d0901ddfc..38d36ab0f 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -442,7 +442,7 @@ class NetworkCommands(object): def create(self, fixed_range=None, num_networks=None, network_size=None, vlan_start=None, vpn_start=None, - fixed_range_v6=None): + fixed_range_v6=None, label='public'): """Creates fixed ips for host by range arguments: [fixed_range=FLAG], [num_networks=FLAG], [network_size=FLAG], [vlan_start=FLAG], @@ -463,7 +463,8 @@ class NetworkCommands(object): net_manager.create_networks(context.get_admin_context(), fixed_range, int(num_networks), int(network_size), int(vlan_start), - int(vpn_start), fixed_range_v6) + int(vpn_start), fixed_range_v6, + label) class ServiceCommands(object): diff --git a/nova/network/manager.py b/nova/network/manager.py index 61de8055a..a377c40c6 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -325,11 +325,12 @@ class FlatManager(NetworkManager): pass def create_networks(self, context, cidr, num_networks, network_size, - cidr_v6, *args, **kwargs): + cidr_v6, label, *args, **kwargs): """Create networks based on parameters.""" fixed_net = IPy.IP(cidr) fixed_net_v6 = IPy.IP(cidr_v6) significant_bits_v6 = 64 + count = 1 for index in range(num_networks): start = index * network_size significant_bits = 32 - int(math.log(network_size, 2)) @@ -342,6 +343,11 @@ class FlatManager(NetworkManager): net['gateway'] = str(project_net[1]) net['broadcast'] = str(project_net.broadcast()) net['dhcp_start'] = str(project_net[2]) + if num_networks > 1: + net['label'] = "%s_%d" % (label, count) + else: + net['label'] = label + count += 1 if(FLAGS.use_ipv6): cidr_v6 = "%s/%s" % (fixed_net_v6[0], significant_bits_v6) -- cgit From a9f9a0fcb7443b93db3f4de8f68218f20f0cc1a9 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 20 Jan 2011 18:36:18 -0600 Subject: really added migration for networks label --- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py new file mode 100644 index 000000000..13b4766d8 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -0,0 +1,47 @@ +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# +# New Tables +# + + +# +# Tables to alter +# + +networks_label = Column( + 'label', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + networks.create_column(networks_label) -- cgit From d4a643976adbe49ec52db53694481e9ba687cddf Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 20 Jan 2011 18:40:04 -0600 Subject: fixed the migration --- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index 13b4766d8..ddfe114cb 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -25,6 +25,11 @@ from nova import log as logging meta = MetaData() +networks = Table('networks', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + # # New Tables # -- cgit From 7ef1c34c2251eb32ef2effa58ea7ee85f46112f7 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 20 Jan 2011 18:44:00 -0600 Subject: moved argument for label --- bin/nova-manage | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 38d36ab0f..73832b0eb 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -441,8 +441,8 @@ class NetworkCommands(object): """Class for managing networks.""" def create(self, fixed_range=None, num_networks=None, - network_size=None, vlan_start=None, vpn_start=None, - fixed_range_v6=None, label='public'): + network_size=None, label='public', vlan_start=None, + vpn_start=None, fixed_range_v6=None): """Creates fixed ips for host by range arguments: [fixed_range=FLAG], [num_networks=FLAG], [network_size=FLAG], [vlan_start=FLAG], -- cgit From 00b029f60baca843487b3cfd89940ed65e85389a Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 20 Jan 2011 18:51:46 -0600 Subject: undid moving argument --- bin/nova-manage | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 73832b0eb..9603c6a49 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -441,8 +441,8 @@ class NetworkCommands(object): """Class for managing networks.""" def create(self, fixed_range=None, num_networks=None, - network_size=None, label='public', vlan_start=None, - vpn_start=None, fixed_range_v6=None): + network_size=None, vlan_start=None, + vpn_start=None, fixed_range_v6=None, label='public'): """Creates fixed ips for host by range arguments: [fixed_range=FLAG], [num_networks=FLAG], [network_size=FLAG], [vlan_start=FLAG], -- cgit From e6b7fa7ae31e90f2d7322445da3843281fff9a70 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 22 Jan 2011 16:20:36 -0800 Subject: fixes and refactoring of smoketests --- smoketests/base.py | 43 +++++++++++--- smoketests/flags.py | 2 +- smoketests/user_smoketests.py | 133 +++++++++++++----------------------------- 3 files changed, 77 insertions(+), 101 deletions(-) diff --git a/smoketests/base.py b/smoketests/base.py index 89ee92840..afc618074 100644 --- a/smoketests/base.py +++ b/smoketests/base.py @@ -22,6 +22,7 @@ import httplib import os import paramiko import sys +import time import unittest from boto.ec2.regioninfo import RegionInfo @@ -31,7 +32,6 @@ FLAGS = flags.FLAGS boto_v6 = None - class SmokeTestCase(unittest.TestCase): def connect_ssh(self, ip, key_name): # TODO(devcamcar): set a more reasonable connection timeout time @@ -39,12 +39,10 @@ class SmokeTestCase(unittest.TestCase): client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.WarningPolicy()) client.connect(ip, username='root', pkey=key) - stdin, stdout, stderr = client.exec_command('uptime') - print 'uptime: ', stdout.read() return client - def can_ping(self, ip): - """ Attempt to ping the specified IP, and give up after 1 second. """ + def can_ping(self, ip, command="ping"): + """Attempt to ping the specified IP, and give up after 1 second.""" # NOTE(devcamcar): ping timeout flag is different in OSX. if sys.platform == 'darwin': @@ -52,10 +50,41 @@ class SmokeTestCase(unittest.TestCase): else: timeout_flag = 'w' - status, output = commands.getstatusoutput('ping -c1 -%s1 %s' % - (timeout_flag, ip)) + status, output = commands.getstatusoutput('%s -c1 -%s1 %s' % + (command, timeout_flag, ip)) return status == 0 + def wait_for_running(self, instance, tries=60, wait=1): + """Wait for instance to be running""" + for x in xrange(tries): + instance.update() + if instance.state.startswith('running'): + return True + time.sleep(wait) + else: + return False + + def wait_for_ping(self, ip, command="ping", tries=120): + """Wait for ip to be pingable""" + for x in xrange(tries): + if self.can_ping(ip, command): + return True + else: + return False + + def wait_for_ssh(self, ip, key_name, tries=30, wait=5): + """Wait for ip to be sshable""" + for x in xrange(tries): + try: + conn = self.connect_ssh(ip, key_name) + conn.close() + except Exception, e: + time.sleep(wait) + else: + return True + else: + return False + def connection_for_env(self, **kwargs): """ Returns a boto ec2 connection for the current environment. diff --git a/smoketests/flags.py b/smoketests/flags.py index dc756347b..5f3c8505e 100644 --- a/smoketests/flags.py +++ b/smoketests/flags.py @@ -36,4 +36,4 @@ DEFINE_bool = DEFINE_bool DEFINE_string('region', 'nova', 'Region to use') DEFINE_string('test_image', 'ami-tty', 'Image to use for launch tests') -DEFINE_bool('use_ipv6', True, 'use the ipv6 or not') +DEFINE_bool('use_ipv6', False, 'use the ipv6 or not') diff --git a/smoketests/user_smoketests.py b/smoketests/user_smoketests.py index e5bc98ede..26f6344f7 100644 --- a/smoketests/user_smoketests.py +++ b/smoketests/user_smoketests.py @@ -19,7 +19,6 @@ import commands import os import random -import socket import sys import time import unittest @@ -91,7 +90,6 @@ class ImageTests(UserSmokeTestCase): break time.sleep(1) else: - print image.state self.assert_(False) # wasn't available within 10 seconds self.assert_(image.type == 'machine') @@ -143,70 +141,36 @@ class InstanceTests(UserSmokeTestCase): key_name=TEST_KEY, instance_type='m1.tiny') self.assertEqual(len(reservation.instances), 1) - self.data['instance_id'] = reservation.instances[0].id + self.data['instance'] = reservation.instances[0] def test_003_instance_runs_within_60_seconds(self): - reservations = self.conn.get_all_instances([self.data['instance_id']]) - instance = reservations[0].instances[0] + instance = self.data['instance'] # allow 60 seconds to exit pending with IP - for x in xrange(60): - instance.update() - if instance.state == u'running': - break - time.sleep(1) - else: + if not self.wait_for_running(self.data['instance']): self.fail('instance failed to start') - ip = reservations[0].instances[0].private_dns_name + self.data['instance'].update() + ip = self.data['instance'].private_dns_name self.failIf(ip == '0.0.0.0') - self.data['private_ip'] = ip if FLAGS.use_ipv6: - ipv6 = reservations[0].instances[0].dns_name_v6 + ipv6 = self.data['instance'].dns_name_v6 self.failIf(ipv6 is None) - self.data['ip_v6'] = ipv6 def test_004_can_ping_private_ip(self): - for x in xrange(120): - # ping waits for 1 second - status, output = commands.getstatusoutput( - 'ping -c1 %s' % self.data['private_ip']) - if status == 0: - break - else: + if not self.wait_for_ping(self.data['instance'].private_dns_name): self.fail('could not ping instance') if FLAGS.use_ipv6: - for x in xrange(120): - # ping waits for 1 second - status, output = commands.getstatusoutput( - 'ping6 -c1 %s' % self.data['ip_v6']) - if status == 0: - break - else: - self.fail('could not ping instance') + if not self.wait_for_ping(self.data['instance'].ip_v6, "ping6"): + self.fail('could not ping instance v6') def test_005_can_ssh_to_private_ip(self): - for x in xrange(30): - try: - conn = self.connect_ssh(self.data['private_ip'], TEST_KEY) - conn.close() - except Exception, e: - time.sleep(5) - else: - break - else: + if not self.wait_for_ssh(self.data['instance'].private_dns_name, + TEST_KEY): self.fail('could not ssh to instance') if FLAGS.use_ipv6: - for x in xrange(30): - try: - conn = self.connect_ssh( - self.data['ip_v6'], TEST_KEY) - conn.close() - except Exception: - time.sleep(1) - else: - break - else: + if not self.wait_for_ssh(self.data['instance'].ip_v6, + TEST_KEY): self.fail('could not ssh to instance v6') def test_006_can_allocate_elastic_ip(self): @@ -215,21 +179,13 @@ class InstanceTests(UserSmokeTestCase): self.data['public_ip'] = result.public_ip def test_007_can_associate_ip_with_instance(self): - result = self.conn.associate_address(self.data['instance_id'], + result = self.conn.associate_address(self.data['instance'].id, self.data['public_ip']) self.assertTrue(result) def test_008_can_ssh_with_public_ip(self): - for x in xrange(30): - try: - conn = self.connect_ssh(self.data['public_ip'], TEST_KEY) - conn.close() - except Exception: - time.sleep(1) - else: - break - else: - self.fail('could not ssh to instance') + if not self.wait_for_ssh(self.data['public_ip'], TEST_KEY): + self.fail('could not ssh to public ip') def test_009_can_disassociate_ip_from_instance(self): result = self.conn.disassociate_address(self.data['public_ip']) @@ -241,8 +197,7 @@ class InstanceTests(UserSmokeTestCase): def test_999_tearDown(self): self.delete_key_pair(self.conn, TEST_KEY) - if self.data.has_key('instance_id'): - self.conn.terminate_instances([self.data['instance_id']]) + self.conn.terminate_instances([self.data['instance'].id]) class VolumeTests(UserSmokeTestCase): @@ -255,24 +210,14 @@ class VolumeTests(UserSmokeTestCase): reservation = self.conn.run_instances(FLAGS.test_image, instance_type='m1.tiny', key_name=TEST_KEY) - instance = reservation.instances[0] - for x in xrange(120): - time.sleep(1) - instance.update() - if self.can_ping(instance.private_dns_name): - break - else: - self.fail('unable to start instance') - self.data['instance'] = instance - for x in xrange(30): - try: - conn = self.connect_ssh(instance.private_dns_name, TEST_KEY) - conn.close() - except Exception: - time.sleep(5) - else: - break - else: + self.data['instance'] = reservation.instances[0] + if not self.wait_for_running(self.data['instance']): + self.fail('instance failed to start') + self.data['instance'].update() + if not self.wait_for_ping(self.data['instance'].private_dns_name): + self.fail('could not ping instance') + if not self.wait_for_ssh(self.data['instance'].private_dns_name, + TEST_KEY): self.fail('could not ssh to instance') def test_001_can_create_volume(self): @@ -280,32 +225,34 @@ class VolumeTests(UserSmokeTestCase): self.assertEqual(volume.size, 1) self.data['volume'] = volume # Give network time to find volume. - time.sleep(5) + time.sleep(10) def test_002_can_attach_volume(self): volume = self.data['volume'] - for x in xrange(30): + for x in xrange(10): + volume.update() if volume.status.startswith('available'): break time.sleep(1) - volume.update() else: self.fail('cannot attach volume with state %s' % volume.status) volume.attach(self.data['instance'].id, self.device) - # Volumes seems to report "available" too soon. + # wait for x in xrange(10): + volume.update() if volume.status.startswith('in-use'): break - time.sleep(5) - volume.update() + time.sleep(1) + else: + self.fail('volume never got to in use') self.assertTrue(volume.status.startswith('in-use')) # Give instance time to recognize volume. - time.sleep(5) + time.sleep(10) def test_003_can_mount_volume(self): ip = self.data['instance'].private_dns_name @@ -316,12 +263,12 @@ class VolumeTests(UserSmokeTestCase): 'grep %s /proc/partitions | ' '`awk \'{print "mknod /dev/"\\$4" b "\\$1" "\\$2}\'`' % self.device.rpartition('/')[2]) - commands = [] - commands.append('mkdir -p /mnt/vol') - commands.append('/sbin/mke2fs %s' % self.device) - commands.append('mount %s /mnt/vol' % self.device) - commands.append('echo success') - stdin, stdout, stderr = conn.exec_command(' && '.join(commands)) + exec_list = [] + exec_list.append('mkdir -p /mnt/vol') + exec_list.append('/sbin/mke2fs %s' % self.device) + exec_list.append('mount %s /mnt/vol' % self.device) + exec_list.append('echo success') + stdin, stdout, stderr = conn.exec_command(' && '.join(exec_list)) out = stdout.read() conn.close() if not out.strip().endswith('success'): -- cgit From 671f27322156615643ce9194a26bec66819c0c78 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 26 Jan 2011 10:34:57 -0600 Subject: Cleaned up _start() and _shutdown() --- nova/virt/xenapi/vmops.py | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index e348aee95..e4aff2313 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -208,22 +208,6 @@ class VMOps(object): logging.debug(_("Finished snapshot and upload for VM %s"), instance) - def start(self, instance): - """Start a VM instance""" - vm = self._get_vm_opaque_ref(instance) - task = self._session.call_xenapi("Async.VM.start", vm, False, False) - self._session.wait_for_task(task, instance.id) - - def shutdown(self, instance): - """Shutdown a VM instance""" - vm = self._get_vm_opaque_ref(instance) - try: - task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) - self._session.wait_for_task(task, instance.id) - except self.XenAPI.Failure: - task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) - self._session.wait_for_task(task, instance.id) - def reboot(self, instance): """Reboot VM instance""" vm = self._get_vm_opaque_ref(instance) @@ -268,8 +252,13 @@ class VMOps(object): raise RuntimeError(resp_dict['message']) return resp_dict['message'] + def _start(self, instance, vm): + """Start an instance""" + task = self._session.call_xenapi("Async.VM.start", vm, False, False) + self._session.wait_for_task(task, instance.id) + def _shutdown(self, instance, vm): - """Shutdown an instance """ + """Shutdown an instance""" state = self.get_info(instance['name'])['state'] if state == power_state.SHUTDOWN: LOG.warn(_("VM %(vm)s already halted, skipping shutdown...") % @@ -277,8 +266,12 @@ class VMOps(object): return try: - task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) - self._session.wait_for_task(task, instance.id) + try: + task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) + self._session.wait_for_task(task, instance.id) + except self.XenAPI.Failure: + task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure, exc: LOG.exception(exc) @@ -368,9 +361,9 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - target_vm = VMHelper.lookup(self._session, "instance-00000001") + target_vm = VMHelper.lookup(self._session, "instance-00000012") - self.shutdown(instance) + self._shutdown(instance, vm) vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] @@ -387,7 +380,7 @@ class VMOps(object): def unrescue(self, instance, callback): """Unrescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - target_vm = VMHelper.lookup(self._session, "instance-00000001") + target_vm = VMHelper.lookup(self._session, "instance-00000012") vbds = self._session.get_xenapi().VM.get_VBDs(target_vm) @@ -397,7 +390,7 @@ class VMOps(object): VMHelper.unplug_vbd(self._session, vbd_ref) VMHelper.destroy_vbd(self._session, vbd_ref) - self.start(instance) + self._start(instance, vm) def get_info(self, instance): """Return data about VM instance""" -- cgit From 0c7893e4119bcccdfdfdcdef0931fcc8802688e8 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 26 Jan 2011 14:59:17 -0600 Subject: added mapping parameter to write_network_config_to_xenstore --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c7310987b..68fa1ecd6 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -391,7 +391,7 @@ class VMOps(object): args = {'id': str(uuid.uuid4())} resp = self._make_agent_call('resetnetwork', vm, '', args) - def write_network_config_to_xenstore(self, instance): + def write_network_config_to_xenstore(self, instance, mapping): vm = self._get_vm_opaque_ref(instance) self.write_to_param_xenstore(vm, mapping) -- cgit From fe3836c5ce16f7c4921eaee746c108d7ae7b4d1a Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Fri, 28 Jan 2011 05:21:04 +0000 Subject: Launchpad automatic translations update. --- locale/ast.po | 2130 +++++++++++++++++++++++++++++++++++++++++++++++++++++ locale/da.po | 2130 +++++++++++++++++++++++++++++++++++++++++++++++++++++ locale/es.po | 2177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ locale/it.po | 2141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ locale/ja.po | 2143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ locale/pt_BR.po | 2148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ locale/ru.po | 2136 +++++++++++++++++++++++++++++++++++++++++++++++++++++ locale/uk.po | 2130 +++++++++++++++++++++++++++++++++++++++++++++++++++++ locale/zh_CN.po | 2135 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 19270 insertions(+) create mode 100644 locale/ast.po create mode 100644 locale/da.po create mode 100644 locale/es.po create mode 100644 locale/it.po create mode 100644 locale/ja.po create mode 100644 locale/pt_BR.po create mode 100644 locale/ru.po create mode 100644 locale/uk.po create mode 100644 locale/zh_CN.po diff --git a/locale/ast.po b/locale/ast.po new file mode 100644 index 000000000..c887bbc91 --- /dev/null +++ b/locale/ast.po @@ -0,0 +1,2130 @@ +# Asturian translation for nova +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the nova package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: nova\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-10 11:25-0800\n" +"PO-Revision-Date: 2011-01-12 19:50+0000\n" +"Last-Translator: Xuacu Saturio \n" +"Language-Team: Asturian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Generator: Launchpad (build 12177)\n" + +#: nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "Nome del ficheru de l'autoridá de certificáu raíz" + +#: nova/crypto.py:49 +msgid "Filename of private key" +msgstr "Nome del ficheru de clave privada" + +#: nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "Nome del ficheru de llista de refugu de certificáu raíz" + +#: nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "" + +#: nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "" + +#: nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "" + +#: nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "" + +#: nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "" + +#: nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "" + +#: nova/exception.py:36 +#, python-format +msgid "" +"%s\n" +"Command: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" +msgstr "" + +#: nova/exception.py:86 +msgid "Uncaught exception" +msgstr "" + +#: nova/fakerabbit.py:48 +#, python-format +msgid "(%s) publish (key: %s) %s" +msgstr "" + +#: nova/fakerabbit.py:53 +#, python-format +msgid "Publishing to route %s" +msgstr "" + +#: nova/fakerabbit.py:83 +#, python-format +msgid "Declaring queue %s" +msgstr "" + +#: nova/fakerabbit.py:89 +#, python-format +msgid "Declaring exchange %s" +msgstr "" + +#: nova/fakerabbit.py:95 +#, python-format +msgid "Binding %s to %s with key %s" +msgstr "" + +#: nova/fakerabbit.py:120 +#, python-format +msgid "Getting from %s: %s" +msgstr "" + +#: nova/rpc.py:92 +#, python-format +msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." +msgstr "" + +#: nova/rpc.py:99 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "" + +#: nova/rpc.py:118 +msgid "Reconnected to queue" +msgstr "" + +#: nova/rpc.py:125 +msgid "Failed to fetch message from queue" +msgstr "" + +#: nova/rpc.py:155 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "" + +#: nova/rpc.py:170 +#, python-format +msgid "received %s" +msgstr "" + +#: nova/rpc.py:183 +#, python-format +msgid "no method for message: %s" +msgstr "" + +#: nova/rpc.py:184 +#, python-format +msgid "No method for message: %s" +msgstr "" + +#: nova/rpc.py:245 +#, python-format +msgid "Returning exception %s to caller" +msgstr "" + +#: nova/rpc.py:286 +#, python-format +msgid "unpacked context: %s" +msgstr "" + +#: nova/rpc.py:305 +msgid "Making asynchronous call..." +msgstr "" + +#: nova/rpc.py:308 +#, python-format +msgid "MSG_ID is %s" +msgstr "" + +#: nova/rpc.py:356 +#, python-format +msgid "response %s" +msgstr "" + +#: nova/rpc.py:365 +#, python-format +msgid "topic is %s" +msgstr "" + +#: nova/rpc.py:366 +#, python-format +msgid "message %s" +msgstr "" + +#: nova/service.py:157 +#, python-format +msgid "Starting %s node" +msgstr "" + +#: nova/service.py:169 +msgid "Service killed that has no database entry" +msgstr "" + +#: nova/service.py:190 +msgid "The service database object disappeared, Recreating it." +msgstr "" + +#: nova/service.py:202 +msgid "Recovered model server connection!" +msgstr "" + +#: nova/service.py:208 +msgid "model server went away" +msgstr "" + +#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 +#, python-format +msgid "Data store %s is unreachable. Trying again in %d seconds." +msgstr "" + +#: nova/service.py:232 nova/twistd.py:232 +#, python-format +msgid "Serving %s" +msgstr "" + +#: nova/service.py:234 nova/twistd.py:264 +msgid "Full set of FLAGS:" +msgstr "" + +#: nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "" + +#: nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "" + +#: nova/utils.py:53 +#, python-format +msgid "Inner Exception: %s" +msgstr "" + +#: nova/utils.py:54 +#, python-format +msgid "Class %s cannot be found" +msgstr "" + +#: nova/utils.py:113 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: nova/utils.py:125 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "" + +#: nova/utils.py:138 +#, python-format +msgid "Result was %s" +msgstr "" + +#: nova/utils.py:171 +#, python-format +msgid "debug in callback: %s" +msgstr "" + +#: nova/utils.py:176 +#, python-format +msgid "Running %s" +msgstr "" + +#: nova/utils.py:207 +#, python-format +msgid "Couldn't get IP, using 127.0.0.1 %s" +msgstr "" + +#: nova/utils.py:289 +#, python-format +msgid "Invalid backend: %s" +msgstr "" + +#: nova/utils.py:300 +#, python-format +msgid "backend %s" +msgstr "" + +#: nova/api/ec2/__init__.py:133 +msgid "Too many failed authentications." +msgstr "" + +#: nova/api/ec2/__init__.py:142 +#, python-format +msgid "" +"Access key %s has had %d failed authentications and will be locked out for " +"%d minutes." +msgstr "" + +#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:190 +#, python-format +msgid "Authenticated Request For %s:%s)" +msgstr "" + +#: nova/api/ec2/__init__.py:227 +#, python-format +msgid "action: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:229 +#, python-format +msgid "arg: %s\t\tval: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:301 +#, python-format +msgid "Unauthorized request for controller=%s and action=%s" +msgstr "" + +#: nova/api/ec2/__init__.py:339 +#, python-format +msgid "NotFound raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:342 +#, python-format +msgid "ApiError raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:349 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:354 +msgid "An unknown error has occurred. Please try your request again." +msgstr "" + +#: nova/api/ec2/admin.py:84 +#, python-format +msgid "Creating new user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:92 +#, python-format +msgid "Deleting user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:114 +#, python-format +msgid "Adding role %s to user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 +#, python-format +msgid "Adding sitewide role %s to user %s" +msgstr "" + +#: nova/api/ec2/admin.py:122 +#, python-format +msgid "Removing role %s from user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 +#, python-format +msgid "Removing sitewide role %s from user %s" +msgstr "" + +#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 +msgid "operation must be add or remove" +msgstr "" + +#: nova/api/ec2/admin.py:142 +#, python-format +msgid "Getting x509 for user: %s on project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:159 +#, python-format +msgid "Create project %s managed by %s" +msgstr "" + +#: nova/api/ec2/admin.py:170 +#, python-format +msgid "Delete project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 +#, python-format +msgid "Adding user %s to project %s" +msgstr "" + +#: nova/api/ec2/admin.py:188 +#, python-format +msgid "Removing user %s from project %s" +msgstr "" + +#: nova/api/ec2/apirequest.py:95 +#, python-format +msgid "Unsupported API request: controller = %s,action = %s" +msgstr "" + +#: nova/api/ec2/cloud.py:117 +#, python-format +msgid "Generating root CA: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:277 +#, python-format +msgid "Create key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:285 +#, python-format +msgid "Delete key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:357 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "" + +#: nova/api/ec2/cloud.py:361 +msgid "Invalid port range" +msgstr "" + +#: nova/api/ec2/cloud.py:392 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 +msgid "No rule for the specified parameters." +msgstr "" + +#: nova/api/ec2/cloud.py:421 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:432 +#, python-format +msgid "This rule already exists in group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:460 +#, python-format +msgid "Create Security Group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:463 +#, python-format +msgid "group %s already exists" +msgstr "" + +#: nova/api/ec2/cloud.py:475 +#, python-format +msgid "Delete security group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 +#, python-format +msgid "Get console output for instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:543 +#, python-format +msgid "Create volume of %s GB" +msgstr "" + +#: nova/api/ec2/cloud.py:567 +#, python-format +msgid "Attach volume %s to instacne %s at %s" +msgstr "" + +#: nova/api/ec2/cloud.py:579 +#, python-format +msgid "Detach volume %s" +msgstr "" + +#: nova/api/ec2/cloud.py:686 +msgid "Allocate address" +msgstr "" + +#: nova/api/ec2/cloud.py:691 +#, python-format +msgid "Release address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:696 +#, python-format +msgid "Associate address %s to instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:703 +#, python-format +msgid "Disassociate address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:730 +msgid "Going to start terminating instances" +msgstr "" + +#: nova/api/ec2/cloud.py:738 +#, python-format +msgid "Reboot instance %r" +msgstr "" + +#: nova/api/ec2/cloud.py:775 +#, python-format +msgid "De-registering image %s" +msgstr "" + +#: nova/api/ec2/cloud.py:783 +#, python-format +msgid "Registered image %s with id %s" +msgstr "" + +#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 +#, python-format +msgid "attribute not supported: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:794 +#, python-format +msgid "invalid id: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:807 +msgid "user or group not specified" +msgstr "" + +#: nova/api/ec2/cloud.py:809 +msgid "only group \"all\" is supported" +msgstr "" + +#: nova/api/ec2/cloud.py:811 +msgid "operation_type must be add or remove" +msgstr "" + +#: nova/api/ec2/cloud.py:812 +#, python-format +msgid "Updating image %s publicity" +msgstr "" + +#: nova/api/ec2/metadatarequesthandler.py:75 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:70 +#, python-format +msgid "Caught error: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:86 +msgid "Including admin operations in API." +msgstr "" + +#: nova/api/openstack/servers.py:184 +#, python-format +msgid "Compute.api::lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:199 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "" + +#: nova/api/openstack/servers.py:213 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:224 +#, python-format +msgid "Compute.api::pause %s" +msgstr "" + +#: nova/api/openstack/servers.py:235 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "" + +#: nova/api/openstack/servers.py:246 +#, python-format +msgid "compute.api::suspend %s" +msgstr "" + +#: nova/api/openstack/servers.py:257 +#, python-format +msgid "compute.api::resume %s" +msgstr "" + +#: nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "" + +#: nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "" + +#: nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "" + +#: nova/auth/ldapdriver.py:181 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:218 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:478 +#, python-format +msgid "User %s is already a member of the group %s" +msgstr "" + +#: nova/auth/ldapdriver.py:507 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "" + +#: nova/auth/ldapdriver.py:528 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "" + +#: nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "" + +#: nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "" + +#: nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "" + +#: nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "" + +#: nova/auth/manager.py:275 +#, python-format +msgid "failed authorization: no project named %s (user=%s)" +msgstr "" + +#: nova/auth/manager.py:277 +#, python-format +msgid "No project called %s could be found" +msgstr "" + +#: nova/auth/manager.py:281 +#, python-format +msgid "Failed authorization: user %s not admin and not member of project %s" +msgstr "" + +#: nova/auth/manager.py:283 +#, python-format +msgid "User %s is not a member of project %s" +msgstr "" + +#: nova/auth/manager.py:292 nova/auth/manager.py:303 +#, python-format +msgid "Invalid signature for user %s" +msgstr "" + +#: nova/auth/manager.py:293 nova/auth/manager.py:304 +msgid "Signature does not match" +msgstr "" + +#: nova/auth/manager.py:374 +msgid "Must specify project" +msgstr "" + +#: nova/auth/manager.py:408 +#, python-format +msgid "The %s role can not be found" +msgstr "" + +#: nova/auth/manager.py:410 +#, python-format +msgid "The %s role is global only" +msgstr "" + +#: nova/auth/manager.py:412 +#, python-format +msgid "Adding role %s to user %s in project %s" +msgstr "" + +#: nova/auth/manager.py:438 +#, python-format +msgid "Removing role %s from user %s on project %s" +msgstr "" + +#: nova/auth/manager.py:505 +#, python-format +msgid "Created project %s with manager %s" +msgstr "" + +#: nova/auth/manager.py:523 +#, python-format +msgid "modifying project %s" +msgstr "" + +#: nova/auth/manager.py:553 +#, python-format +msgid "Remove user %s from project %s" +msgstr "" + +#: nova/auth/manager.py:581 +#, python-format +msgid "Deleting project %s" +msgstr "" + +#: nova/auth/manager.py:637 +#, python-format +msgid "Created user %s (admin: %r)" +msgstr "" + +#: nova/auth/manager.py:645 +#, python-format +msgid "Deleting user %s" +msgstr "" + +#: nova/auth/manager.py:655 +#, python-format +msgid "Access Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:657 +#, python-format +msgid "Secret Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:659 +#, python-format +msgid "Admin status set to %r for user %s" +msgstr "" + +#: nova/auth/manager.py:708 +#, python-format +msgid "No vpn data for project %s" +msgstr "" + +#: nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "" + +#: nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "" + +#: nova/compute/api.py:67 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "" + +#: nova/compute/api.py:73 +#, python-format +msgid "Instance %d has no host" +msgstr "" + +#: nova/compute/api.py:92 +#, python-format +msgid "Quota exceeeded for %s, tried to run %s instances" +msgstr "" + +#: nova/compute/api.py:94 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "" + +#: nova/compute/api.py:109 +msgid "Creating a raw instance" +msgstr "" + +#: nova/compute/api.py:156 +#, python-format +msgid "Going to run %s instances..." +msgstr "" + +#: nova/compute/api.py:180 +#, python-format +msgid "Casting to scheduler for %s/%s's instance %s" +msgstr "" + +#: nova/compute/api.py:279 +#, python-format +msgid "Going to try and terminate %s" +msgstr "" + +#: nova/compute/api.py:283 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "" + +#: nova/compute/api.py:288 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "" + +#: nova/compute/api.py:450 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "" + +#: nova/compute/api.py:465 +msgid "Volume isn't attached to anything!" +msgstr "" + +#: nova/compute/disk.py:71 +#, python-format +msgid "Input partition size not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:75 +#, python-format +msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "" + +#: nova/compute/disk.py:136 +#, python-format +msgid "Failed to load partition: %s" +msgstr "" + +#: nova/compute/disk.py:158 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "" + +#: nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "" + +#: nova/compute/manager.py:69 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "" + +#: nova/compute/manager.py:71 +#, python-format +msgid "check_instance_lock: arguments: |%s| |%s| |%s|" +msgstr "" + +#: nova/compute/manager.py:75 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "" + +#: nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "" + +#: nova/compute/manager.py:82 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "" + +#: nova/compute/manager.py:86 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "" + +#: nova/compute/manager.py:157 +msgid "Instance has already been created" +msgstr "" + +#: nova/compute/manager.py:158 +#, python-format +msgid "instance %s: starting..." +msgstr "" + +#: nova/compute/manager.py:197 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "" + +#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 +#, python-format +msgid "Terminating instance %s" +msgstr "" + +#: nova/compute/manager.py:217 +#, python-format +msgid "Disassociating address %s" +msgstr "" + +#: nova/compute/manager.py:230 +#, python-format +msgid "Deallocating address %s" +msgstr "" + +#: nova/compute/manager.py:243 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "" + +#: nova/compute/manager.py:257 +#, python-format +msgid "Rebooting instance %s" +msgstr "" + +#: nova/compute/manager.py:260 +#, python-format +msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:286 +#, python-format +msgid "instance %s: snapshotting" +msgstr "" + +#: nova/compute/manager.py:289 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:301 +#, python-format +msgid "instance %s: rescuing" +msgstr "" + +#: nova/compute/manager.py:316 +#, python-format +msgid "instance %s: unrescuing" +msgstr "" + +#: nova/compute/manager.py:335 +#, python-format +msgid "instance %s: pausing" +msgstr "" + +#: nova/compute/manager.py:352 +#, python-format +msgid "instance %s: unpausing" +msgstr "" + +#: nova/compute/manager.py:369 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "" + +#: nova/compute/manager.py:382 +#, python-format +msgid "instance %s: suspending" +msgstr "" + +#: nova/compute/manager.py:401 +#, python-format +msgid "instance %s: resuming" +msgstr "" + +#: nova/compute/manager.py:420 +#, python-format +msgid "instance %s: locking" +msgstr "" + +#: nova/compute/manager.py:432 +#, python-format +msgid "instance %s: unlocking" +msgstr "" + +#: nova/compute/manager.py:442 +#, python-format +msgid "instance %s: getting locked state" +msgstr "" + +#: nova/compute/manager.py:462 +#, python-format +msgid "instance %s: attaching volume %s to %s" +msgstr "" + +#: nova/compute/manager.py:478 +#, python-format +msgid "instance %s: attach failed %s, removing" +msgstr "" + +#: nova/compute/manager.py:493 +#, python-format +msgid "Detach volume %s from mountpoint %s on instance %s" +msgstr "" + +#: nova/compute/manager.py:497 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "" + +#: nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "" + +#: nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "" + +#: nova/compute/monitor.py:355 +#, python-format +msgid "Cannot get blockstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:377 +#, python-format +msgid "Cannot get ifstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:412 +msgid "unexpected exception getting connection" +msgstr "" + +#: nova/compute/monitor.py:427 +#, python-format +msgid "Found instance: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:43 +msgid "Use of empty request context is deprecated" +msgstr "" + +#: nova/db/sqlalchemy/api.py:132 +#, python-format +msgid "No service for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:229 +#, python-format +msgid "No service for %s, %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:574 +#, python-format +msgid "No floating ip for address %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:668 +#, python-format +msgid "No instance for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 +#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 +#, python-format +msgid "Instance %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:891 +#, python-format +msgid "no keypair for user %s, name %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 +#, python-format +msgid "No network for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1036 +#, python-format +msgid "No network for bridge %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1050 +#, python-format +msgid "No network for instance %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1180 +#, python-format +msgid "Token %s does not exist" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1205 +#, python-format +msgid "No quota for project_id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1356 +#, python-format +msgid "No volume for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1401 +#, python-format +msgid "Volume %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1413 +#, python-format +msgid "No export device found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1426 +#, python-format +msgid "No target id found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1471 +#, python-format +msgid "No security group with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1488 +#, python-format +msgid "No security group named %s for project: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1576 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1650 +#, python-format +msgid "No user for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1666 +#, python-format +msgid "No user for access key %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1728 +#, python-format +msgid "No project with id %s" +msgstr "" + +#: nova/image/glance.py:78 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images" +msgstr "" + +#: nova/image/glance.py:97 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images/detail" +msgstr "" + +#: nova/image/s3.py:82 +#, python-format +msgid "Image %s could not be found" +msgstr "" + +#: nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "" + +#: nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "" + +#: nova/network/linux_net.py:176 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "" + +#: nova/network/linux_net.py:186 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "" + +#: nova/network/linux_net.py:254 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "" + +#: nova/network/linux_net.py:256 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "" + +#: nova/network/linux_net.py:334 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "" + +#: nova/network/manager.py:135 +msgid "setting network host" +msgstr "" + +#: nova/network/manager.py:190 +#, python-format +msgid "Leasing IP %s" +msgstr "" + +#: nova/network/manager.py:194 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "" + +#: nova/network/manager.py:197 +#, python-format +msgid "IP %s leased to bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:205 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "" + +#: nova/network/manager.py:214 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "" + +#: nova/network/manager.py:217 +#, python-format +msgid "IP %s released from bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:220 +#, python-format +msgid "IP %s released that was not leased" +msgstr "" + +#: nova/network/manager.py:442 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "" + +#: nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "" + +#: nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "" + +#: nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "" + +#: nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:271 +#, python-format +msgid "Getting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:274 +#, python-format +msgid "Unauthorized attempt to get object %s from bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:292 +#, python-format +msgid "Putting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:295 +#, python-format +msgid "Unauthorized attempt to upload object %s to bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:314 +#, python-format +msgid "Deleting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:393 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "" + +#: nova/objectstore/handler.py:401 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:406 +#, python-format +msgid "Starting image upload: %s" +msgstr "" + +#: nova/objectstore/handler.py:420 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "" + +#: nova/objectstore/handler.py:428 +#, python-format +msgid "Toggling publicity flag of image %s %r" +msgstr "" + +#: nova/objectstore/handler.py:433 +#, python-format +msgid "Updating user fields on image %s" +msgstr "" + +#: nova/objectstore/handler.py:447 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "" + +#: nova/objectstore/handler.py:452 +#, python-format +msgid "Deleted image: %s" +msgstr "" + +#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 +#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 +msgid "No hosts found" +msgstr "" + +#: nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "" + +#: nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %s %s for %s" +msgstr "" + +#: nova/scheduler/simple.py:63 +msgid "All hosts have too many cores" +msgstr "" + +#: nova/scheduler/simple.py:95 +msgid "All hosts have too many gigabytes" +msgstr "" + +#: nova/scheduler/simple.py:115 +msgid "All hosts have too many networks" +msgstr "" + +#: nova/tests/test_cloud.py:198 +msgid "Can't test instances without a real virtual env." +msgstr "" + +#: nova/tests/test_cloud.py:210 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "" + +#: nova/tests/test_compute.py:104 +#, python-format +msgid "Running instances: %s" +msgstr "" + +#: nova/tests/test_compute.py:110 +#, python-format +msgid "After terminating instances: %s" +msgstr "" + +#: nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %s, %s" +msgstr "" + +#: nova/tests/test_rpc.py:94 +#, python-format +msgid "Nested return %s" +msgstr "" + +#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 +#, python-format +msgid "Received %s" +msgstr "" + +#: nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "" + +#: nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "" + +#: nova/virt/fake.py:210 +#, python-format +msgid "Instance %s Not Found" +msgstr "" + +#: nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "" + +#: nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "" + +#: nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "" + +#: nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "" + +#: nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "" + +#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 +#, python-format +msgid "Created VM %s..." +msgstr "" + +#: nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %s by attaching disk file %s" +msgstr "" + +#: nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "" + +#: nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "" + +#: nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "" + +#: nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "" + +#: nova/virt/hyperv.py:275 +#, python-format +msgid "Created switch port %s on switch %s" +msgstr "" + +#: nova/virt/hyperv.py:285 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:287 +#, python-format +msgid "Created nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:320 +#, python-format +msgid "WMI job failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:322 +#, python-format +msgid "WMI job succeeded: %s, Elapsed=%s " +msgstr "" + +#: nova/virt/hyperv.py:358 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:383 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:389 +#, python-format +msgid "Del: disk %s vm %s" +msgstr "" + +#: nova/virt/hyperv.py:405 +#, python-format +msgid "" +"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " +"cpu_time=%s" +msgstr "" + +#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 +#, python-format +msgid "duplicate name found: %s" +msgstr "" + +#: nova/virt/hyperv.py:444 +#, python-format +msgid "Successfully changed vm state of %s to %s" +msgstr "" + +#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 +#, python-format +msgid "Failed to change vm state of %s to %s" +msgstr "" + +#: nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %s -- placed in %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:144 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:157 +msgid "Connection to libvirt broke" +msgstr "" + +#: nova/virt/libvirt_conn.py:229 +#, python-format +msgid "instance %s: deleting instance files %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:271 +#, python-format +msgid "No disk at %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:278 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "" + +#: nova/virt/libvirt_conn.py:294 +#, python-format +msgid "instance %s: rebooted" +msgstr "" + +#: nova/virt/libvirt_conn.py:297 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:340 +#, python-format +msgid "instance %s: rescued" +msgstr "" + +#: nova/virt/libvirt_conn.py:343 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:370 +#, python-format +msgid "instance %s: is running" +msgstr "" + +#: nova/virt/libvirt_conn.py:381 +#, python-format +msgid "instance %s: booted" +msgstr "" + +#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 +#, python-format +msgid "instance %s: failed to boot" +msgstr "" + +#: nova/virt/libvirt_conn.py:395 +#, python-format +msgid "virsh said: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:399 +msgid "cool, it's a device" +msgstr "" + +#: nova/virt/libvirt_conn.py:407 +#, python-format +msgid "data: %r, fpath: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:415 +#, python-format +msgid "Contents of file %s: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:449 +#, python-format +msgid "instance %s: Creating image" +msgstr "" + +#: nova/virt/libvirt_conn.py:505 +#, python-format +msgid "instance %s: injecting key into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:508 +#, python-format +msgid "instance %s: injecting net into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:516 +#, python-format +msgid "instance %s: ignoring error injecting data into image %s (%s)" +msgstr "" + +#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "" + +#: nova/virt/libvirt_conn.py:589 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "" + +#: nova/virt/xenapi_conn.py:113 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" + +#: nova/virt/xenapi_conn.py:263 +#, python-format +msgid "Task [%s] %s status: success %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:271 +#, python-format +msgid "Task [%s] %s status: %s %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 +#, python-format +msgid "Got exception: %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:72 +#, python-format +msgid "%s: _db_content => %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 +#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 +msgid "Raising NotImplemented" +msgstr "" + +#: nova/virt/xenapi/fake.py:249 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:283 +#, python-format +msgid "Calling %s %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:288 +#, python-format +msgid "Calling getter %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:340 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:127 +#, python-format +msgid "Created VM %s as %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:147 +#, python-format +msgid "Creating VBD for VM %s, VDI %s ... " +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:149 +#, python-format +msgid "Created VBD %s for VM %s, VDI %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:165 +#, python-format +msgid "VBD not found in instance %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:175 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:202 +#, python-format +msgid "Creating VIF for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:205 +#, python-format +msgid "Created VIF %s for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:216 +#, python-format +msgid "Snapshotting VM %s with label '%s'..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:229 +#, python-format +msgid "Created snapshot %s from VM %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:243 +#, python-format +msgid "Asking xapi to upload %s as '%s'" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:261 +#, python-format +msgid "Asking xapi to fetch %s as %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:279 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:290 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:318 +#, python-format +msgid "VDI %s is still available" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:331 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:333 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:390 +#, python-format +msgid "VHD %s has parent %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:407 +#, python-format +msgid "Re-scanning SR %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:431 +#, python-format +msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:448 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:452 +#, python-format +msgid "Unexpected number of VDIs (%s) found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:62 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:99 +#, python-format +msgid "Starting VM %s..." +msgstr "" + +#: nova/virt/xenapi/vmops.py:101 +#, python-format +msgid "Spawning VM %s created %s." +msgstr "" + +#: nova/virt/xenapi/vmops.py:112 +#, python-format +msgid "Instance %s: booted" +msgstr "" + +#: nova/virt/xenapi/vmops.py:137 +#, python-format +msgid "Instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:166 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:174 +#, python-format +msgid "Unable to Snapshot %s: %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:184 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:252 +#, python-format +msgid "suspend: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "resume: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:271 +#, python-format +msgid "Instance not found %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %s as %s." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %s when getting PBDs for %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %s when unplugging PBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %s when forgetting SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %s, %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %s, %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:81 +#, python-format +msgid "Unable to use SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:95 +#, python-format +msgid "Mountpoint %s attached to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:106 +#, python-format +msgid "Detach_volume: %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:113 +#, python-format +msgid "Unable to locate volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:121 +#, python-format +msgid "Unable to detach volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:128 +#, python-format +msgid "Mountpoint %s detached from instance %s" +msgstr "" + +#: nova/volume/api.py:44 +#, python-format +msgid "Quota exceeeded for %s, tried to create %sG volume" +msgstr "" + +#: nova/volume/api.py:46 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %s" +msgstr "" + +#: nova/volume/api.py:70 nova/volume/api.py:95 +msgid "Volume status must be available" +msgstr "" + +#: nova/volume/api.py:97 +msgid "Volume is already attached" +msgstr "" + +#: nova/volume/api.py:103 +msgid "Volume is already detached" +msgstr "" + +#: nova/volume/driver.py:76 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "" + +#: nova/volume/driver.py:85 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "" + +#: nova/volume/driver.py:210 +#, python-format +msgid "FAKE AOE: %s" +msgstr "" + +#: nova/volume/driver.py:315 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "" + +#: nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "" + +#: nova/volume/manager.py:93 +#, python-format +msgid "volume %s: creating" +msgstr "" + +#: nova/volume/manager.py:102 +#, python-format +msgid "volume %s: creating lv of size %sG" +msgstr "" + +#: nova/volume/manager.py:106 +#, python-format +msgid "volume %s: creating export" +msgstr "" + +#: nova/volume/manager.py:113 +#, python-format +msgid "volume %s: created successfully" +msgstr "" + +#: nova/volume/manager.py:121 +msgid "Volume is still attached" +msgstr "" + +#: nova/volume/manager.py:123 +msgid "Volume is not local to this node" +msgstr "" + +#: nova/volume/manager.py:124 +#, python-format +msgid "volume %s: removing export" +msgstr "" + +#: nova/volume/manager.py:126 +#, python-format +msgid "volume %s: deleting" +msgstr "" + +#: nova/volume/manager.py:129 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "" diff --git a/locale/da.po b/locale/da.po new file mode 100644 index 000000000..524b27a64 --- /dev/null +++ b/locale/da.po @@ -0,0 +1,2130 @@ +# Danish translation for nova +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the nova package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: nova\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-10 11:25-0800\n" +"PO-Revision-Date: 2011-01-15 21:46+0000\n" +"Last-Translator: Soren Hansen \n" +"Language-Team: Danish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Generator: Launchpad (build 12177)\n" + +#: nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "" + +#: nova/crypto.py:49 +msgid "Filename of private key" +msgstr "Filnavn for privatnøgle" + +#: nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "" + +#: nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "" + +#: nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "" + +#: nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "" + +#: nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "" + +#: nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "" + +#: nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "" + +#: nova/exception.py:36 +#, python-format +msgid "" +"%s\n" +"Command: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" +msgstr "" + +#: nova/exception.py:86 +msgid "Uncaught exception" +msgstr "" + +#: nova/fakerabbit.py:48 +#, python-format +msgid "(%s) publish (key: %s) %s" +msgstr "" + +#: nova/fakerabbit.py:53 +#, python-format +msgid "Publishing to route %s" +msgstr "" + +#: nova/fakerabbit.py:83 +#, python-format +msgid "Declaring queue %s" +msgstr "" + +#: nova/fakerabbit.py:89 +#, python-format +msgid "Declaring exchange %s" +msgstr "" + +#: nova/fakerabbit.py:95 +#, python-format +msgid "Binding %s to %s with key %s" +msgstr "" + +#: nova/fakerabbit.py:120 +#, python-format +msgid "Getting from %s: %s" +msgstr "" + +#: nova/rpc.py:92 +#, python-format +msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." +msgstr "" + +#: nova/rpc.py:99 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "" + +#: nova/rpc.py:118 +msgid "Reconnected to queue" +msgstr "" + +#: nova/rpc.py:125 +msgid "Failed to fetch message from queue" +msgstr "" + +#: nova/rpc.py:155 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "" + +#: nova/rpc.py:170 +#, python-format +msgid "received %s" +msgstr "" + +#: nova/rpc.py:183 +#, python-format +msgid "no method for message: %s" +msgstr "" + +#: nova/rpc.py:184 +#, python-format +msgid "No method for message: %s" +msgstr "" + +#: nova/rpc.py:245 +#, python-format +msgid "Returning exception %s to caller" +msgstr "" + +#: nova/rpc.py:286 +#, python-format +msgid "unpacked context: %s" +msgstr "" + +#: nova/rpc.py:305 +msgid "Making asynchronous call..." +msgstr "" + +#: nova/rpc.py:308 +#, python-format +msgid "MSG_ID is %s" +msgstr "" + +#: nova/rpc.py:356 +#, python-format +msgid "response %s" +msgstr "" + +#: nova/rpc.py:365 +#, python-format +msgid "topic is %s" +msgstr "" + +#: nova/rpc.py:366 +#, python-format +msgid "message %s" +msgstr "" + +#: nova/service.py:157 +#, python-format +msgid "Starting %s node" +msgstr "" + +#: nova/service.py:169 +msgid "Service killed that has no database entry" +msgstr "" + +#: nova/service.py:190 +msgid "The service database object disappeared, Recreating it." +msgstr "" + +#: nova/service.py:202 +msgid "Recovered model server connection!" +msgstr "" + +#: nova/service.py:208 +msgid "model server went away" +msgstr "" + +#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 +#, python-format +msgid "Data store %s is unreachable. Trying again in %d seconds." +msgstr "" + +#: nova/service.py:232 nova/twistd.py:232 +#, python-format +msgid "Serving %s" +msgstr "" + +#: nova/service.py:234 nova/twistd.py:264 +msgid "Full set of FLAGS:" +msgstr "" + +#: nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "" + +#: nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "" + +#: nova/utils.py:53 +#, python-format +msgid "Inner Exception: %s" +msgstr "" + +#: nova/utils.py:54 +#, python-format +msgid "Class %s cannot be found" +msgstr "" + +#: nova/utils.py:113 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: nova/utils.py:125 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "" + +#: nova/utils.py:138 +#, python-format +msgid "Result was %s" +msgstr "" + +#: nova/utils.py:171 +#, python-format +msgid "debug in callback: %s" +msgstr "" + +#: nova/utils.py:176 +#, python-format +msgid "Running %s" +msgstr "" + +#: nova/utils.py:207 +#, python-format +msgid "Couldn't get IP, using 127.0.0.1 %s" +msgstr "" + +#: nova/utils.py:289 +#, python-format +msgid "Invalid backend: %s" +msgstr "" + +#: nova/utils.py:300 +#, python-format +msgid "backend %s" +msgstr "" + +#: nova/api/ec2/__init__.py:133 +msgid "Too many failed authentications." +msgstr "" + +#: nova/api/ec2/__init__.py:142 +#, python-format +msgid "" +"Access key %s has had %d failed authentications and will be locked out for " +"%d minutes." +msgstr "" + +#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:190 +#, python-format +msgid "Authenticated Request For %s:%s)" +msgstr "" + +#: nova/api/ec2/__init__.py:227 +#, python-format +msgid "action: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:229 +#, python-format +msgid "arg: %s\t\tval: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:301 +#, python-format +msgid "Unauthorized request for controller=%s and action=%s" +msgstr "" + +#: nova/api/ec2/__init__.py:339 +#, python-format +msgid "NotFound raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:342 +#, python-format +msgid "ApiError raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:349 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:354 +msgid "An unknown error has occurred. Please try your request again." +msgstr "" + +#: nova/api/ec2/admin.py:84 +#, python-format +msgid "Creating new user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:92 +#, python-format +msgid "Deleting user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:114 +#, python-format +msgid "Adding role %s to user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 +#, python-format +msgid "Adding sitewide role %s to user %s" +msgstr "" + +#: nova/api/ec2/admin.py:122 +#, python-format +msgid "Removing role %s from user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 +#, python-format +msgid "Removing sitewide role %s from user %s" +msgstr "" + +#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 +msgid "operation must be add or remove" +msgstr "" + +#: nova/api/ec2/admin.py:142 +#, python-format +msgid "Getting x509 for user: %s on project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:159 +#, python-format +msgid "Create project %s managed by %s" +msgstr "" + +#: nova/api/ec2/admin.py:170 +#, python-format +msgid "Delete project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 +#, python-format +msgid "Adding user %s to project %s" +msgstr "" + +#: nova/api/ec2/admin.py:188 +#, python-format +msgid "Removing user %s from project %s" +msgstr "" + +#: nova/api/ec2/apirequest.py:95 +#, python-format +msgid "Unsupported API request: controller = %s,action = %s" +msgstr "" + +#: nova/api/ec2/cloud.py:117 +#, python-format +msgid "Generating root CA: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:277 +#, python-format +msgid "Create key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:285 +#, python-format +msgid "Delete key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:357 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "" + +#: nova/api/ec2/cloud.py:361 +msgid "Invalid port range" +msgstr "" + +#: nova/api/ec2/cloud.py:392 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 +msgid "No rule for the specified parameters." +msgstr "" + +#: nova/api/ec2/cloud.py:421 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:432 +#, python-format +msgid "This rule already exists in group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:460 +#, python-format +msgid "Create Security Group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:463 +#, python-format +msgid "group %s already exists" +msgstr "" + +#: nova/api/ec2/cloud.py:475 +#, python-format +msgid "Delete security group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 +#, python-format +msgid "Get console output for instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:543 +#, python-format +msgid "Create volume of %s GB" +msgstr "" + +#: nova/api/ec2/cloud.py:567 +#, python-format +msgid "Attach volume %s to instacne %s at %s" +msgstr "" + +#: nova/api/ec2/cloud.py:579 +#, python-format +msgid "Detach volume %s" +msgstr "" + +#: nova/api/ec2/cloud.py:686 +msgid "Allocate address" +msgstr "" + +#: nova/api/ec2/cloud.py:691 +#, python-format +msgid "Release address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:696 +#, python-format +msgid "Associate address %s to instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:703 +#, python-format +msgid "Disassociate address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:730 +msgid "Going to start terminating instances" +msgstr "" + +#: nova/api/ec2/cloud.py:738 +#, python-format +msgid "Reboot instance %r" +msgstr "" + +#: nova/api/ec2/cloud.py:775 +#, python-format +msgid "De-registering image %s" +msgstr "" + +#: nova/api/ec2/cloud.py:783 +#, python-format +msgid "Registered image %s with id %s" +msgstr "" + +#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 +#, python-format +msgid "attribute not supported: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:794 +#, python-format +msgid "invalid id: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:807 +msgid "user or group not specified" +msgstr "" + +#: nova/api/ec2/cloud.py:809 +msgid "only group \"all\" is supported" +msgstr "" + +#: nova/api/ec2/cloud.py:811 +msgid "operation_type must be add or remove" +msgstr "" + +#: nova/api/ec2/cloud.py:812 +#, python-format +msgid "Updating image %s publicity" +msgstr "" + +#: nova/api/ec2/metadatarequesthandler.py:75 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:70 +#, python-format +msgid "Caught error: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:86 +msgid "Including admin operations in API." +msgstr "" + +#: nova/api/openstack/servers.py:184 +#, python-format +msgid "Compute.api::lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:199 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "" + +#: nova/api/openstack/servers.py:213 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:224 +#, python-format +msgid "Compute.api::pause %s" +msgstr "" + +#: nova/api/openstack/servers.py:235 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "" + +#: nova/api/openstack/servers.py:246 +#, python-format +msgid "compute.api::suspend %s" +msgstr "" + +#: nova/api/openstack/servers.py:257 +#, python-format +msgid "compute.api::resume %s" +msgstr "" + +#: nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "" + +#: nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "" + +#: nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "" + +#: nova/auth/ldapdriver.py:181 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:218 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:478 +#, python-format +msgid "User %s is already a member of the group %s" +msgstr "" + +#: nova/auth/ldapdriver.py:507 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "" + +#: nova/auth/ldapdriver.py:528 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "" + +#: nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "" + +#: nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "" + +#: nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "" + +#: nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "" + +#: nova/auth/manager.py:275 +#, python-format +msgid "failed authorization: no project named %s (user=%s)" +msgstr "" + +#: nova/auth/manager.py:277 +#, python-format +msgid "No project called %s could be found" +msgstr "" + +#: nova/auth/manager.py:281 +#, python-format +msgid "Failed authorization: user %s not admin and not member of project %s" +msgstr "" + +#: nova/auth/manager.py:283 +#, python-format +msgid "User %s is not a member of project %s" +msgstr "" + +#: nova/auth/manager.py:292 nova/auth/manager.py:303 +#, python-format +msgid "Invalid signature for user %s" +msgstr "" + +#: nova/auth/manager.py:293 nova/auth/manager.py:304 +msgid "Signature does not match" +msgstr "" + +#: nova/auth/manager.py:374 +msgid "Must specify project" +msgstr "" + +#: nova/auth/manager.py:408 +#, python-format +msgid "The %s role can not be found" +msgstr "" + +#: nova/auth/manager.py:410 +#, python-format +msgid "The %s role is global only" +msgstr "" + +#: nova/auth/manager.py:412 +#, python-format +msgid "Adding role %s to user %s in project %s" +msgstr "" + +#: nova/auth/manager.py:438 +#, python-format +msgid "Removing role %s from user %s on project %s" +msgstr "" + +#: nova/auth/manager.py:505 +#, python-format +msgid "Created project %s with manager %s" +msgstr "" + +#: nova/auth/manager.py:523 +#, python-format +msgid "modifying project %s" +msgstr "" + +#: nova/auth/manager.py:553 +#, python-format +msgid "Remove user %s from project %s" +msgstr "" + +#: nova/auth/manager.py:581 +#, python-format +msgid "Deleting project %s" +msgstr "" + +#: nova/auth/manager.py:637 +#, python-format +msgid "Created user %s (admin: %r)" +msgstr "" + +#: nova/auth/manager.py:645 +#, python-format +msgid "Deleting user %s" +msgstr "" + +#: nova/auth/manager.py:655 +#, python-format +msgid "Access Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:657 +#, python-format +msgid "Secret Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:659 +#, python-format +msgid "Admin status set to %r for user %s" +msgstr "" + +#: nova/auth/manager.py:708 +#, python-format +msgid "No vpn data for project %s" +msgstr "" + +#: nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "" + +#: nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "" + +#: nova/compute/api.py:67 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "" + +#: nova/compute/api.py:73 +#, python-format +msgid "Instance %d has no host" +msgstr "" + +#: nova/compute/api.py:92 +#, python-format +msgid "Quota exceeeded for %s, tried to run %s instances" +msgstr "" + +#: nova/compute/api.py:94 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "" + +#: nova/compute/api.py:109 +msgid "Creating a raw instance" +msgstr "" + +#: nova/compute/api.py:156 +#, python-format +msgid "Going to run %s instances..." +msgstr "" + +#: nova/compute/api.py:180 +#, python-format +msgid "Casting to scheduler for %s/%s's instance %s" +msgstr "" + +#: nova/compute/api.py:279 +#, python-format +msgid "Going to try and terminate %s" +msgstr "" + +#: nova/compute/api.py:283 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "" + +#: nova/compute/api.py:288 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "" + +#: nova/compute/api.py:450 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "" + +#: nova/compute/api.py:465 +msgid "Volume isn't attached to anything!" +msgstr "" + +#: nova/compute/disk.py:71 +#, python-format +msgid "Input partition size not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:75 +#, python-format +msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "" + +#: nova/compute/disk.py:136 +#, python-format +msgid "Failed to load partition: %s" +msgstr "" + +#: nova/compute/disk.py:158 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "" + +#: nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "" + +#: nova/compute/manager.py:69 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "" + +#: nova/compute/manager.py:71 +#, python-format +msgid "check_instance_lock: arguments: |%s| |%s| |%s|" +msgstr "" + +#: nova/compute/manager.py:75 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "" + +#: nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "" + +#: nova/compute/manager.py:82 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "" + +#: nova/compute/manager.py:86 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "" + +#: nova/compute/manager.py:157 +msgid "Instance has already been created" +msgstr "" + +#: nova/compute/manager.py:158 +#, python-format +msgid "instance %s: starting..." +msgstr "" + +#: nova/compute/manager.py:197 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "" + +#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 +#, python-format +msgid "Terminating instance %s" +msgstr "" + +#: nova/compute/manager.py:217 +#, python-format +msgid "Disassociating address %s" +msgstr "" + +#: nova/compute/manager.py:230 +#, python-format +msgid "Deallocating address %s" +msgstr "" + +#: nova/compute/manager.py:243 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "" + +#: nova/compute/manager.py:257 +#, python-format +msgid "Rebooting instance %s" +msgstr "" + +#: nova/compute/manager.py:260 +#, python-format +msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:286 +#, python-format +msgid "instance %s: snapshotting" +msgstr "" + +#: nova/compute/manager.py:289 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:301 +#, python-format +msgid "instance %s: rescuing" +msgstr "" + +#: nova/compute/manager.py:316 +#, python-format +msgid "instance %s: unrescuing" +msgstr "" + +#: nova/compute/manager.py:335 +#, python-format +msgid "instance %s: pausing" +msgstr "" + +#: nova/compute/manager.py:352 +#, python-format +msgid "instance %s: unpausing" +msgstr "" + +#: nova/compute/manager.py:369 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "" + +#: nova/compute/manager.py:382 +#, python-format +msgid "instance %s: suspending" +msgstr "" + +#: nova/compute/manager.py:401 +#, python-format +msgid "instance %s: resuming" +msgstr "" + +#: nova/compute/manager.py:420 +#, python-format +msgid "instance %s: locking" +msgstr "" + +#: nova/compute/manager.py:432 +#, python-format +msgid "instance %s: unlocking" +msgstr "" + +#: nova/compute/manager.py:442 +#, python-format +msgid "instance %s: getting locked state" +msgstr "" + +#: nova/compute/manager.py:462 +#, python-format +msgid "instance %s: attaching volume %s to %s" +msgstr "" + +#: nova/compute/manager.py:478 +#, python-format +msgid "instance %s: attach failed %s, removing" +msgstr "" + +#: nova/compute/manager.py:493 +#, python-format +msgid "Detach volume %s from mountpoint %s on instance %s" +msgstr "" + +#: nova/compute/manager.py:497 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "" + +#: nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "" + +#: nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "" + +#: nova/compute/monitor.py:355 +#, python-format +msgid "Cannot get blockstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:377 +#, python-format +msgid "Cannot get ifstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:412 +msgid "unexpected exception getting connection" +msgstr "" + +#: nova/compute/monitor.py:427 +#, python-format +msgid "Found instance: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:43 +msgid "Use of empty request context is deprecated" +msgstr "" + +#: nova/db/sqlalchemy/api.py:132 +#, python-format +msgid "No service for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:229 +#, python-format +msgid "No service for %s, %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:574 +#, python-format +msgid "No floating ip for address %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:668 +#, python-format +msgid "No instance for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 +#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 +#, python-format +msgid "Instance %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:891 +#, python-format +msgid "no keypair for user %s, name %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 +#, python-format +msgid "No network for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1036 +#, python-format +msgid "No network for bridge %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1050 +#, python-format +msgid "No network for instance %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1180 +#, python-format +msgid "Token %s does not exist" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1205 +#, python-format +msgid "No quota for project_id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1356 +#, python-format +msgid "No volume for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1401 +#, python-format +msgid "Volume %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1413 +#, python-format +msgid "No export device found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1426 +#, python-format +msgid "No target id found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1471 +#, python-format +msgid "No security group with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1488 +#, python-format +msgid "No security group named %s for project: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1576 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1650 +#, python-format +msgid "No user for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1666 +#, python-format +msgid "No user for access key %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1728 +#, python-format +msgid "No project with id %s" +msgstr "" + +#: nova/image/glance.py:78 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images" +msgstr "" + +#: nova/image/glance.py:97 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images/detail" +msgstr "" + +#: nova/image/s3.py:82 +#, python-format +msgid "Image %s could not be found" +msgstr "" + +#: nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "" + +#: nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "" + +#: nova/network/linux_net.py:176 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "" + +#: nova/network/linux_net.py:186 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "" + +#: nova/network/linux_net.py:254 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "" + +#: nova/network/linux_net.py:256 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "" + +#: nova/network/linux_net.py:334 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "" + +#: nova/network/manager.py:135 +msgid "setting network host" +msgstr "" + +#: nova/network/manager.py:190 +#, python-format +msgid "Leasing IP %s" +msgstr "" + +#: nova/network/manager.py:194 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "" + +#: nova/network/manager.py:197 +#, python-format +msgid "IP %s leased to bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:205 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "" + +#: nova/network/manager.py:214 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "" + +#: nova/network/manager.py:217 +#, python-format +msgid "IP %s released from bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:220 +#, python-format +msgid "IP %s released that was not leased" +msgstr "" + +#: nova/network/manager.py:442 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "" + +#: nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "" + +#: nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "" + +#: nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "" + +#: nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:271 +#, python-format +msgid "Getting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:274 +#, python-format +msgid "Unauthorized attempt to get object %s from bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:292 +#, python-format +msgid "Putting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:295 +#, python-format +msgid "Unauthorized attempt to upload object %s to bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:314 +#, python-format +msgid "Deleting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:393 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "" + +#: nova/objectstore/handler.py:401 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:406 +#, python-format +msgid "Starting image upload: %s" +msgstr "" + +#: nova/objectstore/handler.py:420 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "" + +#: nova/objectstore/handler.py:428 +#, python-format +msgid "Toggling publicity flag of image %s %r" +msgstr "" + +#: nova/objectstore/handler.py:433 +#, python-format +msgid "Updating user fields on image %s" +msgstr "" + +#: nova/objectstore/handler.py:447 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "" + +#: nova/objectstore/handler.py:452 +#, python-format +msgid "Deleted image: %s" +msgstr "" + +#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 +#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 +msgid "No hosts found" +msgstr "" + +#: nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "" + +#: nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %s %s for %s" +msgstr "" + +#: nova/scheduler/simple.py:63 +msgid "All hosts have too many cores" +msgstr "" + +#: nova/scheduler/simple.py:95 +msgid "All hosts have too many gigabytes" +msgstr "" + +#: nova/scheduler/simple.py:115 +msgid "All hosts have too many networks" +msgstr "" + +#: nova/tests/test_cloud.py:198 +msgid "Can't test instances without a real virtual env." +msgstr "" + +#: nova/tests/test_cloud.py:210 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "" + +#: nova/tests/test_compute.py:104 +#, python-format +msgid "Running instances: %s" +msgstr "" + +#: nova/tests/test_compute.py:110 +#, python-format +msgid "After terminating instances: %s" +msgstr "" + +#: nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %s, %s" +msgstr "" + +#: nova/tests/test_rpc.py:94 +#, python-format +msgid "Nested return %s" +msgstr "" + +#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 +#, python-format +msgid "Received %s" +msgstr "" + +#: nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "" + +#: nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "" + +#: nova/virt/fake.py:210 +#, python-format +msgid "Instance %s Not Found" +msgstr "" + +#: nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "" + +#: nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "" + +#: nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "" + +#: nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "" + +#: nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "" + +#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 +#, python-format +msgid "Created VM %s..." +msgstr "" + +#: nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %s by attaching disk file %s" +msgstr "" + +#: nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "" + +#: nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "" + +#: nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "" + +#: nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "" + +#: nova/virt/hyperv.py:275 +#, python-format +msgid "Created switch port %s on switch %s" +msgstr "" + +#: nova/virt/hyperv.py:285 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:287 +#, python-format +msgid "Created nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:320 +#, python-format +msgid "WMI job failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:322 +#, python-format +msgid "WMI job succeeded: %s, Elapsed=%s " +msgstr "" + +#: nova/virt/hyperv.py:358 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:383 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:389 +#, python-format +msgid "Del: disk %s vm %s" +msgstr "" + +#: nova/virt/hyperv.py:405 +#, python-format +msgid "" +"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " +"cpu_time=%s" +msgstr "" + +#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 +#, python-format +msgid "duplicate name found: %s" +msgstr "" + +#: nova/virt/hyperv.py:444 +#, python-format +msgid "Successfully changed vm state of %s to %s" +msgstr "" + +#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 +#, python-format +msgid "Failed to change vm state of %s to %s" +msgstr "" + +#: nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %s -- placed in %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:144 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:157 +msgid "Connection to libvirt broke" +msgstr "" + +#: nova/virt/libvirt_conn.py:229 +#, python-format +msgid "instance %s: deleting instance files %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:271 +#, python-format +msgid "No disk at %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:278 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "" + +#: nova/virt/libvirt_conn.py:294 +#, python-format +msgid "instance %s: rebooted" +msgstr "" + +#: nova/virt/libvirt_conn.py:297 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:340 +#, python-format +msgid "instance %s: rescued" +msgstr "" + +#: nova/virt/libvirt_conn.py:343 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:370 +#, python-format +msgid "instance %s: is running" +msgstr "" + +#: nova/virt/libvirt_conn.py:381 +#, python-format +msgid "instance %s: booted" +msgstr "" + +#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 +#, python-format +msgid "instance %s: failed to boot" +msgstr "" + +#: nova/virt/libvirt_conn.py:395 +#, python-format +msgid "virsh said: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:399 +msgid "cool, it's a device" +msgstr "" + +#: nova/virt/libvirt_conn.py:407 +#, python-format +msgid "data: %r, fpath: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:415 +#, python-format +msgid "Contents of file %s: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:449 +#, python-format +msgid "instance %s: Creating image" +msgstr "" + +#: nova/virt/libvirt_conn.py:505 +#, python-format +msgid "instance %s: injecting key into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:508 +#, python-format +msgid "instance %s: injecting net into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:516 +#, python-format +msgid "instance %s: ignoring error injecting data into image %s (%s)" +msgstr "" + +#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "" + +#: nova/virt/libvirt_conn.py:589 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "" + +#: nova/virt/xenapi_conn.py:113 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" + +#: nova/virt/xenapi_conn.py:263 +#, python-format +msgid "Task [%s] %s status: success %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:271 +#, python-format +msgid "Task [%s] %s status: %s %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 +#, python-format +msgid "Got exception: %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:72 +#, python-format +msgid "%s: _db_content => %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 +#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 +msgid "Raising NotImplemented" +msgstr "" + +#: nova/virt/xenapi/fake.py:249 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:283 +#, python-format +msgid "Calling %s %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:288 +#, python-format +msgid "Calling getter %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:340 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:127 +#, python-format +msgid "Created VM %s as %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:147 +#, python-format +msgid "Creating VBD for VM %s, VDI %s ... " +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:149 +#, python-format +msgid "Created VBD %s for VM %s, VDI %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:165 +#, python-format +msgid "VBD not found in instance %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:175 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:202 +#, python-format +msgid "Creating VIF for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:205 +#, python-format +msgid "Created VIF %s for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:216 +#, python-format +msgid "Snapshotting VM %s with label '%s'..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:229 +#, python-format +msgid "Created snapshot %s from VM %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:243 +#, python-format +msgid "Asking xapi to upload %s as '%s'" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:261 +#, python-format +msgid "Asking xapi to fetch %s as %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:279 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:290 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:318 +#, python-format +msgid "VDI %s is still available" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:331 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:333 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:390 +#, python-format +msgid "VHD %s has parent %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:407 +#, python-format +msgid "Re-scanning SR %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:431 +#, python-format +msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:448 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:452 +#, python-format +msgid "Unexpected number of VDIs (%s) found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:62 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:99 +#, python-format +msgid "Starting VM %s..." +msgstr "" + +#: nova/virt/xenapi/vmops.py:101 +#, python-format +msgid "Spawning VM %s created %s." +msgstr "" + +#: nova/virt/xenapi/vmops.py:112 +#, python-format +msgid "Instance %s: booted" +msgstr "" + +#: nova/virt/xenapi/vmops.py:137 +#, python-format +msgid "Instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:166 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:174 +#, python-format +msgid "Unable to Snapshot %s: %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:184 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:252 +#, python-format +msgid "suspend: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "resume: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:271 +#, python-format +msgid "Instance not found %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %s as %s." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %s when getting PBDs for %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %s when unplugging PBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %s when forgetting SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %s, %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %s, %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:81 +#, python-format +msgid "Unable to use SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:95 +#, python-format +msgid "Mountpoint %s attached to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:106 +#, python-format +msgid "Detach_volume: %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:113 +#, python-format +msgid "Unable to locate volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:121 +#, python-format +msgid "Unable to detach volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:128 +#, python-format +msgid "Mountpoint %s detached from instance %s" +msgstr "" + +#: nova/volume/api.py:44 +#, python-format +msgid "Quota exceeeded for %s, tried to create %sG volume" +msgstr "" + +#: nova/volume/api.py:46 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %s" +msgstr "" + +#: nova/volume/api.py:70 nova/volume/api.py:95 +msgid "Volume status must be available" +msgstr "" + +#: nova/volume/api.py:97 +msgid "Volume is already attached" +msgstr "" + +#: nova/volume/api.py:103 +msgid "Volume is already detached" +msgstr "" + +#: nova/volume/driver.py:76 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "" + +#: nova/volume/driver.py:85 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "" + +#: nova/volume/driver.py:210 +#, python-format +msgid "FAKE AOE: %s" +msgstr "" + +#: nova/volume/driver.py:315 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "" + +#: nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "" + +#: nova/volume/manager.py:93 +#, python-format +msgid "volume %s: creating" +msgstr "" + +#: nova/volume/manager.py:102 +#, python-format +msgid "volume %s: creating lv of size %sG" +msgstr "" + +#: nova/volume/manager.py:106 +#, python-format +msgid "volume %s: creating export" +msgstr "" + +#: nova/volume/manager.py:113 +#, python-format +msgid "volume %s: created successfully" +msgstr "" + +#: nova/volume/manager.py:121 +msgid "Volume is still attached" +msgstr "" + +#: nova/volume/manager.py:123 +msgid "Volume is not local to this node" +msgstr "" + +#: nova/volume/manager.py:124 +#, python-format +msgid "volume %s: removing export" +msgstr "" + +#: nova/volume/manager.py:126 +#, python-format +msgid "volume %s: deleting" +msgstr "" + +#: nova/volume/manager.py:129 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "bind %s: slettet" diff --git a/locale/es.po b/locale/es.po new file mode 100644 index 000000000..a1cf5b7f6 --- /dev/null +++ b/locale/es.po @@ -0,0 +1,2177 @@ +# Spanish translation for nova +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the nova package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: nova\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-10 11:25-0800\n" +"PO-Revision-Date: 2011-01-18 14:56+0000\n" +"Last-Translator: Javier Turégano \n" +"Language-Team: Spanish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Generator: Launchpad (build 12177)\n" + +#: nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "Nombre de fichero de la CA raíz" + +#: nova/crypto.py:49 +msgid "Filename of private key" +msgstr "Nombre de fichero de la clave privada" + +#: nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "Nombre de fichero de la lista de certificados de revocación raíz" + +#: nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "Donde guardamos nuestras claves" + +#: nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "Dónde guardamos nuestra CA raíz" + +#: nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "¿Deberíamos usar una CA para cada proyecto?" + +#: nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "" +"Sujeto (Subject) para el certificado de usuarios, %s para el proyecto, " +"usuario, marca de tiempo" + +#: nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "" +"Sujeto (Subject) para el certificado del proyecto, %s para el proyecto, " +"marca de tiempo" + +#: nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "" +"Sujeto (Subject) para el certificado para vpns, %s para el proyecto, marca " +"de tiempo" + +#: nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "" + +#: nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "Sucedió un error inesperado mientras el comando se ejecutaba." + +#: nova/exception.py:36 +#, python-format +msgid "" +"%s\n" +"Command: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" +msgstr "" +"%s\n" +"Comando: %s\n" +"Código de salida: %s\n" +"Stdout: %s\n" +"Stderr: %r" + +#: nova/exception.py:86 +msgid "Uncaught exception" +msgstr "Excepción no controlada" + +#: nova/fakerabbit.py:48 +#, python-format +msgid "(%s) publish (key: %s) %s" +msgstr "(%s) públicar (clave: %s) %s" + +#: nova/fakerabbit.py:53 +#, python-format +msgid "Publishing to route %s" +msgstr "Publicando la ruta %s" + +#: nova/fakerabbit.py:83 +#, python-format +msgid "Declaring queue %s" +msgstr "Declarando cola %s" + +#: nova/fakerabbit.py:89 +#, python-format +msgid "Declaring exchange %s" +msgstr "Declarando intercambio %s" + +#: nova/fakerabbit.py:95 +#, python-format +msgid "Binding %s to %s with key %s" +msgstr "Asociando %s a %s con clave %s" + +#: nova/fakerabbit.py:120 +#, python-format +msgid "Getting from %s: %s" +msgstr "Obteniendo desde %s: %s" + +#: nova/rpc.py:92 +#, python-format +msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." +msgstr "" +"El servidor AMQP en %s:%d no se puede alcanzar. Se reintentará en %d " +"segundos." + +#: nova/rpc.py:99 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "" +"Imposible conectar al servidor AMQP después de %d intentos. Apagando." + +#: nova/rpc.py:118 +msgid "Reconnected to queue" +msgstr "Reconectado a la cola" + +#: nova/rpc.py:125 +msgid "Failed to fetch message from queue" +msgstr "Fallo al obtener el mensaje de la cola" + +#: nova/rpc.py:155 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "" + +#: nova/rpc.py:170 +#, python-format +msgid "received %s" +msgstr "recibido %s" + +#: nova/rpc.py:183 +#, python-format +msgid "no method for message: %s" +msgstr "no hay método para el mensaje: %s" + +#: nova/rpc.py:184 +#, python-format +msgid "No method for message: %s" +msgstr "No hay método para el mensaje: %s" + +#: nova/rpc.py:245 +#, python-format +msgid "Returning exception %s to caller" +msgstr "" + +#: nova/rpc.py:286 +#, python-format +msgid "unpacked context: %s" +msgstr "contenido desempaquetado: %s" + +#: nova/rpc.py:305 +msgid "Making asynchronous call..." +msgstr "Haciendo una llamada asíncrona..." + +#: nova/rpc.py:308 +#, python-format +msgid "MSG_ID is %s" +msgstr "MSG_ID es %s" + +#: nova/rpc.py:356 +#, python-format +msgid "response %s" +msgstr "respuesta %s" + +#: nova/rpc.py:365 +#, python-format +msgid "topic is %s" +msgstr "" + +#: nova/rpc.py:366 +#, python-format +msgid "message %s" +msgstr "mensaje %s" + +#: nova/service.py:157 +#, python-format +msgid "Starting %s node" +msgstr "Inciando nodo %s" + +#: nova/service.py:169 +msgid "Service killed that has no database entry" +msgstr "Se detuvo un servicio sin entrada en la base de datos" + +#: nova/service.py:190 +msgid "The service database object disappeared, Recreating it." +msgstr "El servicio objeto de base de datos ha desaparecido, recreándolo." + +#: nova/service.py:202 +msgid "Recovered model server connection!" +msgstr "Recuperada la conexión al servidor de modelos." + +#: nova/service.py:208 +msgid "model server went away" +msgstr "el servidor de modelos se ha ido" + +#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 +#, python-format +msgid "Data store %s is unreachable. Trying again in %d seconds." +msgstr "" +"El almacen de datos %s es inalcanzable. Reintentandolo en %d segundos." + +#: nova/service.py:232 nova/twistd.py:232 +#, python-format +msgid "Serving %s" +msgstr "Sirviendo %s" + +#: nova/service.py:234 nova/twistd.py:264 +msgid "Full set of FLAGS:" +msgstr "Conjunto completo de opciones:" + +#: nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "el pidfile %s no existe. ¿No estará el demonio parado?\n" + +#: nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "Comenzando %s" + +#: nova/utils.py:53 +#, python-format +msgid "Inner Exception: %s" +msgstr "Excepción interna: %s" + +#: nova/utils.py:54 +#, python-format +msgid "Class %s cannot be found" +msgstr "La clase %s no ha podido ser encontrada." + +#: nova/utils.py:113 +#, python-format +msgid "Fetching %s" +msgstr "Obteniendo %s" + +#: nova/utils.py:125 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "Ejecutando cmd (subprocesos): %s" + +#: nova/utils.py:138 +#, python-format +msgid "Result was %s" +msgstr "El resultado fue %s" + +#: nova/utils.py:171 +#, python-format +msgid "debug in callback: %s" +msgstr "Depuración de la devolución de llamada: %s" + +#: nova/utils.py:176 +#, python-format +msgid "Running %s" +msgstr "Ejecutando %s" + +#: nova/utils.py:207 +#, python-format +msgid "Couldn't get IP, using 127.0.0.1 %s" +msgstr "No puedo obtener IP, usando 127.0.0.1 %s" + +#: nova/utils.py:289 +#, python-format +msgid "Invalid backend: %s" +msgstr "backend inválido: %s" + +#: nova/utils.py:300 +#, python-format +msgid "backend %s" +msgstr "backend %s" + +#: nova/api/ec2/__init__.py:133 +msgid "Too many failed authentications." +msgstr "Demasiados intentos de autenticacion fallidos." + +#: nova/api/ec2/__init__.py:142 +#, python-format +msgid "" +"Access key %s has had %d failed authentications and will be locked out for " +"%d minutes." +msgstr "" +"La clave de acceso %s ha tenido %d fallos de autenticación y se bloqueará " +"por %d minutos." + +#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "Fallo de autenticación: %s" + +#: nova/api/ec2/__init__.py:190 +#, python-format +msgid "Authenticated Request For %s:%s)" +msgstr "Solicitud de autenticación para %s:%s" + +#: nova/api/ec2/__init__.py:227 +#, python-format +msgid "action: %s" +msgstr "acción: %s" + +#: nova/api/ec2/__init__.py:229 +#, python-format +msgid "arg: %s\t\tval: %s" +msgstr "arg: %s \t \t val: %s" + +#: nova/api/ec2/__init__.py:301 +#, python-format +msgid "Unauthorized request for controller=%s and action=%s" +msgstr "Solicitud no autorizada para controller=%s y action=%s" + +#: nova/api/ec2/__init__.py:339 +#, python-format +msgid "NotFound raised: %s" +msgstr "No encontrado: %s" + +#: nova/api/ec2/__init__.py:342 +#, python-format +msgid "ApiError raised: %s" +msgstr "Sucedió un ApiError: %s" + +#: nova/api/ec2/__init__.py:349 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "Sucedió un error inexperado: %s" + +#: nova/api/ec2/__init__.py:354 +msgid "An unknown error has occurred. Please try your request again." +msgstr "" +"Ha sucedido un error desconocido. Por favor repite el intento de nuevo." + +#: nova/api/ec2/admin.py:84 +#, python-format +msgid "Creating new user: %s" +msgstr "Creando nuevo usuario: %s" + +#: nova/api/ec2/admin.py:92 +#, python-format +msgid "Deleting user: %s" +msgstr "Eliminando usuario: %s" + +#: nova/api/ec2/admin.py:114 +#, python-format +msgid "Adding role %s to user %s for project %s" +msgstr "Añadiendo rol %s al usuario %s para el proyecto %s" + +#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 +#, python-format +msgid "Adding sitewide role %s to user %s" +msgstr "Añadiendo rol global %s al usuario %s" + +#: nova/api/ec2/admin.py:122 +#, python-format +msgid "Removing role %s from user %s for project %s" +msgstr "Eliminando rol %s del usuario %s para el proyecto %s" + +#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 +#, python-format +msgid "Removing sitewide role %s from user %s" +msgstr "Eliminando rol global %s del usuario %s" + +#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 +msgid "operation must be add or remove" +msgstr "la operación debe ser añadir o eliminar" + +#: nova/api/ec2/admin.py:142 +#, python-format +msgid "Getting x509 for user: %s on project: %s" +msgstr "Obteniendo x509 para el usuario: %s en el proyecto %s" + +#: nova/api/ec2/admin.py:159 +#, python-format +msgid "Create project %s managed by %s" +msgstr "Creación del proyecto %s gestionada por %s" + +#: nova/api/ec2/admin.py:170 +#, python-format +msgid "Delete project: %s" +msgstr "Borrar proyecto: %s" + +#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 +#, python-format +msgid "Adding user %s to project %s" +msgstr "Añadiendo usuario %s al proyecto %s" + +#: nova/api/ec2/admin.py:188 +#, python-format +msgid "Removing user %s from project %s" +msgstr "Eliminando usuario %s del proyecto %s" + +#: nova/api/ec2/apirequest.py:95 +#, python-format +msgid "Unsupported API request: controller = %s,action = %s" +msgstr "Solicitud de API no soportada: controller=%s,action=%s" + +#: nova/api/ec2/cloud.py:117 +#, python-format +msgid "Generating root CA: %s" +msgstr "Generando CA raiz: %s" + +#: nova/api/ec2/cloud.py:277 +#, python-format +msgid "Create key pair %s" +msgstr "Creando par de claves %s" + +#: nova/api/ec2/cloud.py:285 +#, python-format +msgid "Delete key pair %s" +msgstr "Borrar para de claves %s" + +#: nova/api/ec2/cloud.py:357 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "%s no es un ipProtocol valido" + +#: nova/api/ec2/cloud.py:361 +msgid "Invalid port range" +msgstr "Rango de puerto inválido" + +#: nova/api/ec2/cloud.py:392 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "Revocar ingreso al grupo de seguridad %s" + +#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 +msgid "No rule for the specified parameters." +msgstr "No hay regla para los parámetros especificados." + +#: nova/api/ec2/cloud.py:421 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "Autorizar ingreso al grupo de seguridad %s" + +#: nova/api/ec2/cloud.py:432 +#, python-format +msgid "This rule already exists in group %s" +msgstr "Esta regla ya existe en el grupo %s" + +#: nova/api/ec2/cloud.py:460 +#, python-format +msgid "Create Security Group %s" +msgstr "Crear Grupo de Seguridad %s" + +#: nova/api/ec2/cloud.py:463 +#, python-format +msgid "group %s already exists" +msgstr "el grupo %s ya existe" + +#: nova/api/ec2/cloud.py:475 +#, python-format +msgid "Delete security group %s" +msgstr "Borrar grupo de seguridad %s" + +#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 +#, python-format +msgid "Get console output for instance %s" +msgstr "Obtener salida de la consola para la instancia %s" + +#: nova/api/ec2/cloud.py:543 +#, python-format +msgid "Create volume of %s GB" +msgstr "Crear volumen de %s GB" + +#: nova/api/ec2/cloud.py:567 +#, python-format +msgid "Attach volume %s to instacne %s at %s" +msgstr "Asociar volumen %s a la instancia %s en %s" + +#: nova/api/ec2/cloud.py:579 +#, python-format +msgid "Detach volume %s" +msgstr "Desasociar volumen %s" + +#: nova/api/ec2/cloud.py:686 +msgid "Allocate address" +msgstr "Asignar dirección" + +#: nova/api/ec2/cloud.py:691 +#, python-format +msgid "Release address %s" +msgstr "Liberar dirección %s" + +#: nova/api/ec2/cloud.py:696 +#, python-format +msgid "Associate address %s to instance %s" +msgstr "Asociar dirección %s a la instancia %s" + +#: nova/api/ec2/cloud.py:703 +#, python-format +msgid "Disassociate address %s" +msgstr "Desasociar dirección %s" + +#: nova/api/ec2/cloud.py:730 +msgid "Going to start terminating instances" +msgstr "Se va a iniciar la finalización de las instancias" + +#: nova/api/ec2/cloud.py:738 +#, python-format +msgid "Reboot instance %r" +msgstr "Reiniciar instancia %r" + +#: nova/api/ec2/cloud.py:775 +#, python-format +msgid "De-registering image %s" +msgstr "Des-registrando la imagen %s" + +#: nova/api/ec2/cloud.py:783 +#, python-format +msgid "Registered image %s with id %s" +msgstr "Registrada imagen %s con id %s" + +#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 +#, python-format +msgid "attribute not supported: %s" +msgstr "atributo no soportado: %s" + +#: nova/api/ec2/cloud.py:794 +#, python-format +msgid "invalid id: %s" +msgstr "id no valido: %s" + +#: nova/api/ec2/cloud.py:807 +msgid "user or group not specified" +msgstr "usuario o grupo no especificado" + +#: nova/api/ec2/cloud.py:809 +msgid "only group \"all\" is supported" +msgstr "sólo el grupo \"all\" está soportado" + +#: nova/api/ec2/cloud.py:811 +msgid "operation_type must be add or remove" +msgstr "operation_type debe ser añadir o eliminar" + +#: nova/api/ec2/cloud.py:812 +#, python-format +msgid "Updating image %s publicity" +msgstr "Actualizando imagen %s públicamente" + +#: nova/api/ec2/metadatarequesthandler.py:75 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "Fallo al generar metadatos para la ip %s" + +#: nova/api/openstack/__init__.py:70 +#, python-format +msgid "Caught error: %s" +msgstr "Capturado error: %s" + +#: nova/api/openstack/__init__.py:86 +msgid "Including admin operations in API." +msgstr "Incluyendo operaciones de administración in API." + +#: nova/api/openstack/servers.py:184 +#, python-format +msgid "Compute.api::lock %s" +msgstr "Compute.api::lock %s" + +#: nova/api/openstack/servers.py:199 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "Compute.api::unlock %s" + +#: nova/api/openstack/servers.py:213 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "Compute.api::get_lock %s" + +#: nova/api/openstack/servers.py:224 +#, python-format +msgid "Compute.api::pause %s" +msgstr "Compute.api::pause %s" + +#: nova/api/openstack/servers.py:235 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "Compute.api::unpause %s" + +#: nova/api/openstack/servers.py:246 +#, python-format +msgid "compute.api::suspend %s" +msgstr "compute.api::suspend %s" + +#: nova/api/openstack/servers.py:257 +#, python-format +msgid "compute.api::resume %s" +msgstr "compute.api::resume %s" + +#: nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "El usuario %s ya existe" + +#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "El proyecto no puede ser creado porque el administrador %s no existe" + +#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "El proyecto no puede ser creado porque el proyecto %s ya existe" + +#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "" +"El proyecto no puede ser modificado porque el administrador %s no existe" + +#: nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "No se ha encontrado el usuario \"%s\"" + +#: nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "No se ha encontrado el proyecto \"%s\"" + +#: nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "Intento de instanciar sigleton" + +#: nova/auth/ldapdriver.py:181 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "El objeto LDAP para %s no existe" + +#: nova/auth/ldapdriver.py:218 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "El proyecto no puede ser creado porque el usuario %s no existe" + +#: nova/auth/ldapdriver.py:478 +#, python-format +msgid "User %s is already a member of the group %s" +msgstr "El usuario %s ya es miembro de el grupo %s" + +#: nova/auth/ldapdriver.py:507 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "" +"Se ha intentado eliminar el último miembro de un grupo. Eliminando el grupo " +"%s en su lugar." + +#: nova/auth/ldapdriver.py:528 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "El grupo con dn %s no existe" + +#: nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "Buscando usuario: %r" + +#: nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "Fallo de autorización para la clave de acceso %s" + +#: nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "No se ha encontrado usuario para la clave de acceso %s" + +#: nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "Utilizando nombre de proyecto = nombre de usuario (%s)" + +#: nova/auth/manager.py:275 +#, python-format +msgid "failed authorization: no project named %s (user=%s)" +msgstr "" +"fallo de autorización: no existe proyecto con el nombre %s (usuario=%s)" + +#: nova/auth/manager.py:277 +#, python-format +msgid "No project called %s could be found" +msgstr "No se ha podido encontrar un proyecto con nombre %s" + +#: nova/auth/manager.py:281 +#, python-format +msgid "Failed authorization: user %s not admin and not member of project %s" +msgstr "" +"Fallo de autorización: el usuario %s no es administrador y no es miembro del " +"proyecto %s" + +#: nova/auth/manager.py:283 +#, python-format +msgid "User %s is not a member of project %s" +msgstr "El usuario %s no es miembro del proyecto %s" + +#: nova/auth/manager.py:292 nova/auth/manager.py:303 +#, python-format +msgid "Invalid signature for user %s" +msgstr "Firma invalida para el usuario %s" + +#: nova/auth/manager.py:293 nova/auth/manager.py:304 +msgid "Signature does not match" +msgstr "Las firmas no concuerdan" + +#: nova/auth/manager.py:374 +msgid "Must specify project" +msgstr "Debes especificar un proyecto" + +#: nova/auth/manager.py:408 +#, python-format +msgid "The %s role can not be found" +msgstr "El rol %s no se ha podido encontrar" + +#: nova/auth/manager.py:410 +#, python-format +msgid "The %s role is global only" +msgstr "El rol %s es únicamente global" + +#: nova/auth/manager.py:412 +#, python-format +msgid "Adding role %s to user %s in project %s" +msgstr "Añadiendo rol %s al usuario %s en el proyecto %s" + +#: nova/auth/manager.py:438 +#, python-format +msgid "Removing role %s from user %s on project %s" +msgstr "Eliminando rol %s al usuario %s en el proyecto %s" + +#: nova/auth/manager.py:505 +#, python-format +msgid "Created project %s with manager %s" +msgstr "Proyecto %s creado con administrador %s" + +#: nova/auth/manager.py:523 +#, python-format +msgid "modifying project %s" +msgstr "modificando proyecto %s" + +#: nova/auth/manager.py:553 +#, python-format +msgid "Remove user %s from project %s" +msgstr "Eliminar usuario %s del proyecto %s" + +#: nova/auth/manager.py:581 +#, python-format +msgid "Deleting project %s" +msgstr "Eliminando proyecto %s" + +#: nova/auth/manager.py:637 +#, python-format +msgid "Created user %s (admin: %r)" +msgstr "Creado usuario %s (administrador: %r)" + +#: nova/auth/manager.py:645 +#, python-format +msgid "Deleting user %s" +msgstr "Eliminando usuario %s" + +#: nova/auth/manager.py:655 +#, python-format +msgid "Access Key change for user %s" +msgstr "Cambio de clave de acceso para el usuario %s" + +#: nova/auth/manager.py:657 +#, python-format +msgid "Secret Key change for user %s" +msgstr "Cambio de clave secreta para el usuario %s" + +#: nova/auth/manager.py:659 +#, python-format +msgid "Admin status set to %r for user %s" +msgstr "El estado del administrador se ha fijado a %r para el usuario %s" + +#: nova/auth/manager.py:708 +#, python-format +msgid "No vpn data for project %s" +msgstr "No hay datos vpn para el proyecto %s" + +#: nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "" + +#: nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "Red a insertar en la configuración de openvpn" + +#: nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "Mascara de red a insertar en la configuración de openvpn" + +#: nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "Lanzando VPN para %s" + +#: nova/compute/api.py:67 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "La instancia %d no se ha encontrado en get_network_topic" + +#: nova/compute/api.py:73 +#, python-format +msgid "Instance %d has no host" +msgstr "La instancia %d no tiene host" + +#: nova/compute/api.py:92 +#, python-format +msgid "Quota exceeeded for %s, tried to run %s instances" +msgstr "Quota superada por %s, intentando lanzar %s instancias" + +#: nova/compute/api.py:94 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "" +"Quota de instancias superada. Sólo puedes ejecutar %s instancias más de este " +"tipo." + +#: nova/compute/api.py:109 +msgid "Creating a raw instance" +msgstr "Creando una instancia raw" + +#: nova/compute/api.py:156 +#, python-format +msgid "Going to run %s instances..." +msgstr "Vamos a ejecutar %s insntacias..." + +#: nova/compute/api.py:180 +#, python-format +msgid "Casting to scheduler for %s/%s's instance %s" +msgstr "Llamando al planificar para %s/%s insntancia %s" + +#: nova/compute/api.py:279 +#, python-format +msgid "Going to try and terminate %s" +msgstr "Se va a probar y terminar %s" + +#: nova/compute/api.py:283 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "La instancia %d no se ha encontrado durante la finalización" + +#: nova/compute/api.py:288 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "La instancia %d ha sido finalizada" + +#: nova/compute/api.py:450 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "" +"El dispositivo especificado no es válido: %s. Ejemplo de dispositivo: " +"/dev/vdb" + +#: nova/compute/api.py:465 +msgid "Volume isn't attached to anything!" +msgstr "¡El volumen no está unido a nada!" + +#: nova/compute/disk.py:71 +#, python-format +msgid "Input partition size not evenly divisible by sector size: %d / %d" +msgstr "" +"El tamaño de la partición de entrada no es divisible de forma uniforme por " +"el tamaño del sector: %d / %d" + +#: nova/compute/disk.py:75 +#, python-format +msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" +msgstr "" +"Los bytes del almacenamiento local no son divisibles de forma uniforme por " +"el tamaño del sector: %d / %d" + +#: nova/compute/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "No se puede unir la imagen con el loopback: %s" + +#: nova/compute/disk.py:136 +#, python-format +msgid "Failed to load partition: %s" +msgstr "Fallo al cargar la partición: %s" + +#: nova/compute/disk.py:158 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "Fallo al montar el sistema de ficheros: %s" + +#: nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "Tipo de instancia desconocido: %s" + +#: nova/compute/manager.py:69 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "check_instance_lock: decorating: |%s|" + +#: nova/compute/manager.py:71 +#, python-format +msgid "check_instance_lock: arguments: |%s| |%s| |%s|" +msgstr "check_instance_lock: arguments: |%s| |%s| |%s|" + +#: nova/compute/manager.py:75 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "check_instance_lock: locked: |%s|" + +#: nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "check_instance_lock: admin: |%s|" + +#: nova/compute/manager.py:82 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "check_instance_lock: ejecutando: |%s|" + +#: nova/compute/manager.py:86 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "check_instance_lock: no ejecutando |%s|" + +#: nova/compute/manager.py:157 +msgid "Instance has already been created" +msgstr "La instancia ha sido creada previamente" + +#: nova/compute/manager.py:158 +#, python-format +msgid "instance %s: starting..." +msgstr "instancia %s: iniciando..." + +#: nova/compute/manager.py:197 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "Instancia %s: no se pudo iniciar" + +#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 +#, python-format +msgid "Terminating instance %s" +msgstr "Finalizando la instancia %s" + +#: nova/compute/manager.py:217 +#, python-format +msgid "Disassociating address %s" +msgstr "Desasociando la dirección %s" + +#: nova/compute/manager.py:230 +#, python-format +msgid "Deallocating address %s" +msgstr "Desasociando la dirección %s" + +#: nova/compute/manager.py:243 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "intentando finalizar una instancia que ya había sido finalizada: %s" + +#: nova/compute/manager.py:257 +#, python-format +msgid "Rebooting instance %s" +msgstr "Reiniciando instancia %s" + +#: nova/compute/manager.py:260 +#, python-format +msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" +"intentando reiniciar una instancia que no está en ejecución: %s (estado: %s " +"esperado: %s)" + +#: nova/compute/manager.py:286 +#, python-format +msgid "instance %s: snapshotting" +msgstr "instancia %s: creando snapshot" + +#: nova/compute/manager.py:289 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" +"intentando crear un snapshot de una instancia que no está en ejecución: %s " +"(estado: %s esperado: %s)" + +#: nova/compute/manager.py:301 +#, python-format +msgid "instance %s: rescuing" +msgstr "instancia %s: rescatando" + +#: nova/compute/manager.py:316 +#, python-format +msgid "instance %s: unrescuing" +msgstr "" + +#: nova/compute/manager.py:335 +#, python-format +msgid "instance %s: pausing" +msgstr "instancia %s: pausando" + +#: nova/compute/manager.py:352 +#, python-format +msgid "instance %s: unpausing" +msgstr "instnacia %s: continuando tras pausa" + +#: nova/compute/manager.py:369 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "instancia %s: obteniendo los diagnosticos" + +#: nova/compute/manager.py:382 +#, python-format +msgid "instance %s: suspending" +msgstr "instancia %s: suspendiendo" + +#: nova/compute/manager.py:401 +#, python-format +msgid "instance %s: resuming" +msgstr "instancia %s: continuando" + +#: nova/compute/manager.py:420 +#, python-format +msgid "instance %s: locking" +msgstr "instancia %s: bloqueando" + +#: nova/compute/manager.py:432 +#, python-format +msgid "instance %s: unlocking" +msgstr "instancia %s: desbloqueando" + +#: nova/compute/manager.py:442 +#, python-format +msgid "instance %s: getting locked state" +msgstr "instancia %s: pasando a estado bloqueado" + +#: nova/compute/manager.py:462 +#, python-format +msgid "instance %s: attaching volume %s to %s" +msgstr "instancia %s: asociando volumen %s a %s" + +#: nova/compute/manager.py:478 +#, python-format +msgid "instance %s: attach failed %s, removing" +msgstr "instalación %s: asociación fallida %s, eliminando" + +#: nova/compute/manager.py:493 +#, python-format +msgid "Detach volume %s from mountpoint %s on instance %s" +msgstr "Desvinculando volumen %s del punto de montaje %s en la instancia %s" + +#: nova/compute/manager.py:497 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "Desvinculando volumen de instancia desconocida %s" + +#: nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "actualizando %s..." + +#: nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "error inesperado durante la actualización" + +#: nova/compute/monitor.py:355 +#, python-format +msgid "Cannot get blockstats for \"%s\" on \"%s\"" +msgstr "No puedo obtener estadísticas del bloque para \"%s\" en \"%s\"" + +#: nova/compute/monitor.py:377 +#, python-format +msgid "Cannot get ifstats for \"%s\" on \"%s\"" +msgstr "No puedo obtener estadísticas de la interfaz para \"%s\" en \"%s\"" + +#: nova/compute/monitor.py:412 +msgid "unexpected exception getting connection" +msgstr "excepción inexperada al obtener la conexión" + +#: nova/compute/monitor.py:427 +#, python-format +msgid "Found instance: %s" +msgstr "Encontrada interfaz: %s" + +#: nova/db/sqlalchemy/api.py:43 +msgid "Use of empty request context is deprecated" +msgstr "El uso de una petición de contexto vacía está en desuso" + +#: nova/db/sqlalchemy/api.py:132 +#, python-format +msgid "No service for id %s" +msgstr "No hay servicio para el id %s" + +#: nova/db/sqlalchemy/api.py:229 +#, python-format +msgid "No service for %s, %s" +msgstr "No hay servicio para %s, %s" + +#: nova/db/sqlalchemy/api.py:574 +#, python-format +msgid "No floating ip for address %s" +msgstr "No hay ip flotante para la dirección %s" + +#: nova/db/sqlalchemy/api.py:668 +#, python-format +msgid "No instance for id %s" +msgstr "No hay instancia con id %s" + +#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 +#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 +#, python-format +msgid "Instance %s not found" +msgstr "La instancia %s no se ha encontrado" + +#: nova/db/sqlalchemy/api.py:891 +#, python-format +msgid "no keypair for user %s, name %s" +msgstr "no hay par de claves para el usuario %s, nombre %s" + +#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 +#, python-format +msgid "No network for id %s" +msgstr "No hay red para el id %s" + +#: nova/db/sqlalchemy/api.py:1036 +#, python-format +msgid "No network for bridge %s" +msgstr "No hay red para el puente %s" + +#: nova/db/sqlalchemy/api.py:1050 +#, python-format +msgid "No network for instance %s" +msgstr "No hay red para la instancia %s" + +#: nova/db/sqlalchemy/api.py:1180 +#, python-format +msgid "Token %s does not exist" +msgstr "El token %s no existe" + +#: nova/db/sqlalchemy/api.py:1205 +#, python-format +msgid "No quota for project_id %s" +msgstr "No hay quota para el project:id %s" + +#: nova/db/sqlalchemy/api.py:1356 +#, python-format +msgid "No volume for id %s" +msgstr "No hay volumen para el id %s" + +#: nova/db/sqlalchemy/api.py:1401 +#, python-format +msgid "Volume %s not found" +msgstr "El volumen %s no se ha encontrado" + +#: nova/db/sqlalchemy/api.py:1413 +#, python-format +msgid "No export device found for volume %s" +msgstr "No se ha encontrado dispositivo exportado para el volumen %s" + +#: nova/db/sqlalchemy/api.py:1426 +#, python-format +msgid "No target id found for volume %s" +msgstr "No se ha encontrado id de destino para el volumen %s" + +#: nova/db/sqlalchemy/api.py:1471 +#, python-format +msgid "No security group with id %s" +msgstr "No hay un grupo de seguridad con el id %s" + +#: nova/db/sqlalchemy/api.py:1488 +#, python-format +msgid "No security group named %s for project: %s" +msgstr "No hay un grupo de seguridad con nombre %s para el proyecto: %s" + +#: nova/db/sqlalchemy/api.py:1576 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "No hay una regla para el grupo de seguridad con el id %s" + +#: nova/db/sqlalchemy/api.py:1650 +#, python-format +msgid "No user for id %s" +msgstr "No hay un usuario con el id %s" + +#: nova/db/sqlalchemy/api.py:1666 +#, python-format +msgid "No user for access key %s" +msgstr "No hay un usuario para la clave de acceso %s" + +#: nova/db/sqlalchemy/api.py:1728 +#, python-format +msgid "No project with id %s" +msgstr "No hay proyecto con id %s" + +#: nova/image/glance.py:78 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images" +msgstr "Parallax ha devuelto un error HTTP %d a la petición para /images" + +#: nova/image/glance.py:97 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images/detail" +msgstr "" +"Parallax ha devuelto un error HTTP %d para la petición para /images/detail" + +#: nova/image/s3.py:82 +#, python-format +msgid "Image %s could not be found" +msgstr "La imagen %s no ha podido ser encontrada" + +#: nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "Quota excedida para %s, intentando asignar direcciones" + +#: nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "" +"La quota de direcciones ha sido excedida. No puedes asignar más direcciones" + +#: nova/network/linux_net.py:176 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "Iniciando interfaz VLAN %s" + +#: nova/network/linux_net.py:186 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "Iniciando interfaz puente para %s" + +#: nova/network/linux_net.py:254 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "Excepción al recargar la configuración de dnsmasq: %s" + +#: nova/network/linux_net.py:256 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "El pid %d está pasado, relanzando dnsmasq" + +#: nova/network/linux_net.py:334 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "Al matar dnsmasq se lanzó %s" + +#: nova/network/manager.py:135 +msgid "setting network host" +msgstr "configurando la red del host" + +#: nova/network/manager.py:190 +#, python-format +msgid "Leasing IP %s" +msgstr "Liberando IP %s" + +#: nova/network/manager.py:194 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "" + +#: nova/network/manager.py:197 +#, python-format +msgid "IP %s leased to bad mac %s vs %s" +msgstr "IP %s asociada a una mac incorrecta %s vs %s" + +#: nova/network/manager.py:205 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "" + +#: nova/network/manager.py:214 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "" + +#: nova/network/manager.py:217 +#, python-format +msgid "IP %s released from bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:220 +#, python-format +msgid "IP %s released that was not leased" +msgstr "" + +#: nova/network/manager.py:442 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "" + +#: nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "Tipo de valor S3 %r desconocido" + +#: nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "Petición autenticada" + +#: nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "Listado de cubos solicitado" + +#: nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "Lista de claves para el cubo %s" + +#: nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "Intento no autorizado para acceder al cubo %s" + +#: nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "Creando el cubo %s" + +#: nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "Eliminando el cubo %s" + +#: nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "Intento no autorizado de eliminar el cubo %s" + +#: nova/objectstore/handler.py:271 +#, python-format +msgid "Getting object: %s / %s" +msgstr "Obteniendo objeto: %s / %s" + +#: nova/objectstore/handler.py:274 +#, python-format +msgid "Unauthorized attempt to get object %s from bucket %s" +msgstr "Intento no autorizado de obtener el objeto %s en el cubo %s" + +#: nova/objectstore/handler.py:292 +#, python-format +msgid "Putting object: %s / %s" +msgstr "Colocando objeto: %s / %s" + +#: nova/objectstore/handler.py:295 +#, python-format +msgid "Unauthorized attempt to upload object %s to bucket %s" +msgstr "Intento no autorizado de subir el objeto %s al cubo %s" + +#: nova/objectstore/handler.py:314 +#, python-format +msgid "Deleting object: %s / %s" +msgstr "Eliminando objeto: %s / %s" + +#: nova/objectstore/handler.py:393 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "No autorizado para subir imagen: directorio incorrecto %s" + +#: nova/objectstore/handler.py:401 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "No autorizado para subir imagen: cubo %s no autorizado" + +#: nova/objectstore/handler.py:406 +#, python-format +msgid "Starting image upload: %s" +msgstr "Comenzando la subida de la imagen: %s" + +#: nova/objectstore/handler.py:420 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "No autorizado para actualizar los atributos de la imagen %s" + +#: nova/objectstore/handler.py:428 +#, python-format +msgid "Toggling publicity flag of image %s %r" +msgstr "Cambiando los atributos de publicidad de la imagen %s %r" + +#: nova/objectstore/handler.py:433 +#, python-format +msgid "Updating user fields on image %s" +msgstr "Actualizando los campos de usuario de la imagen %s" + +#: nova/objectstore/handler.py:447 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "Intento no autorizado de borrar la imagen %s" + +#: nova/objectstore/handler.py:452 +#, python-format +msgid "Deleted image: %s" +msgstr "Eliminada imagen: %s" + +#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 +#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 +msgid "No hosts found" +msgstr "No se han encontrado hosts" + +#: nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "Debe de implementar un horario de reserva" + +#: nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %s %s for %s" +msgstr "" + +#: nova/scheduler/simple.py:63 +msgid "All hosts have too many cores" +msgstr "Todos los hosts tienen demasiados cores" + +#: nova/scheduler/simple.py:95 +msgid "All hosts have too many gigabytes" +msgstr "Todos los hosts tienen demasiados gigabytes" + +#: nova/scheduler/simple.py:115 +msgid "All hosts have too many networks" +msgstr "Todos los hosts tienen demasiadas redes" + +#: nova/tests/test_cloud.py:198 +msgid "Can't test instances without a real virtual env." +msgstr "No puedo probar las imágenes sin un entorno real virtual" + +#: nova/tests/test_cloud.py:210 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "Hay que vigilar la instancia %s hasta que este en ejecución..." + +#: nova/tests/test_compute.py:104 +#, python-format +msgid "Running instances: %s" +msgstr "Ejecutando instancias: %s" + +#: nova/tests/test_compute.py:110 +#, python-format +msgid "After terminating instances: %s" +msgstr "Después de terminar las instancias: %s" + +#: nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %s, %s" +msgstr "" + +#: nova/tests/test_rpc.py:94 +#, python-format +msgid "Nested return %s" +msgstr "" + +#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 +#, python-format +msgid "Received %s" +msgstr "Recibido %s" + +#: nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "Destino %s asignado" + +#: nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "Fallo al abrir conexión con el hypervisor" + +#: nova/virt/fake.py:210 +#, python-format +msgid "Instance %s Not Found" +msgstr "La instancia %s no ha sido encontrada" + +#: nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "En el host inicial" + +#: nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "Intento de crear una vm duplicada %s" + +#: nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "Comenzando VM %s " + +#: nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "VM %s iniciada " + +#: nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "Inicio de vm fallido: %s" + +#: nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "Fallo al crear la VM %s" + +#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 +#, python-format +msgid "Created VM %s..." +msgstr "Creada VM %s..." + +#: nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "Se ha establecido la memoria para vm %s..." + +#: nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "Establecidas vcpus para vm %s..." + +#: nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %s by attaching disk file %s" +msgstr "" +"Creando disco para %s a través de la asignación del fichero de disco %s" + +#: nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "Fallo al añadir unidad de disco a la VM %s" + +#: nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "La nueva ruta para unidad de disco es %s" + +#: nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "Fallo al añadir el fichero vhd a la VM %s" + +#: nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "Discos creados para %s" + +#: nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "Creando nic para %s " + +#: nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "Fallo al crear un puerto en el vswitch externo" + +#: nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "Fallo creando puerto para %s" + +#: nova/virt/hyperv.py:275 +#, python-format +msgid "Created switch port %s on switch %s" +msgstr "Creado puerto %s en el switch %s" + +#: nova/virt/hyperv.py:285 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "Fallo al añadir nic a la VM %s" + +#: nova/virt/hyperv.py:287 +#, python-format +msgid "Created nic for %s " +msgstr "Creando nic para %s " + +#: nova/virt/hyperv.py:320 +#, python-format +msgid "WMI job failed: %s" +msgstr "Trabajo WMI falló: %s" + +#: nova/virt/hyperv.py:322 +#, python-format +msgid "WMI job succeeded: %s, Elapsed=%s " +msgstr "Trabajo WMI ha tenido exito: %s, Transcurrido=%s " + +#: nova/virt/hyperv.py:358 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "Recibida solicitud para destruir vm %s" + +#: nova/virt/hyperv.py:383 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "Fallo al destruir vm %s" + +#: nova/virt/hyperv.py:389 +#, python-format +msgid "Del: disk %s vm %s" +msgstr "Del: disco %s vm %s" + +#: nova/virt/hyperv.py:405 +#, python-format +msgid "" +"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " +"cpu_time=%s" +msgstr "" +"Obtenida información para vm %s: state=%s, mem=%s, num_cpu=%s, cpu_time=%s" + +#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 +#, python-format +msgid "duplicate name found: %s" +msgstr "se ha encontrado un nombre duplicado: %s" + +#: nova/virt/hyperv.py:444 +#, python-format +msgid "Successfully changed vm state of %s to %s" +msgstr "Cambio de estado de la vm con éxito de %s a %s" + +#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 +#, python-format +msgid "Failed to change vm state of %s to %s" +msgstr "Fallo al cambiar el estado de la vm de %s a %s" + +#: nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %s -- placed in %s" +msgstr "Finalizada la obtención de %s -- coloado en %s" + +#: nova/virt/libvirt_conn.py:144 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "Conectando a libvirt: %s" + +#: nova/virt/libvirt_conn.py:157 +msgid "Connection to libvirt broke" +msgstr "Conexión a libvirt rota" + +#: nova/virt/libvirt_conn.py:229 +#, python-format +msgid "instance %s: deleting instance files %s" +msgstr "instancia %s: eliminando los ficheros de la instancia %s" + +#: nova/virt/libvirt_conn.py:271 +#, python-format +msgid "No disk at %s" +msgstr "No hay disco en %s" + +#: nova/virt/libvirt_conn.py:278 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "" +"El snapshotting de instancias no está soportado en libvirt en este momento" + +#: nova/virt/libvirt_conn.py:294 +#, python-format +msgid "instance %s: rebooted" +msgstr "instancia %s: reiniciada" + +#: nova/virt/libvirt_conn.py:297 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "_wait_for_reboot falló: %s" + +#: nova/virt/libvirt_conn.py:340 +#, python-format +msgid "instance %s: rescued" +msgstr "instancia %s: rescatada" + +#: nova/virt/libvirt_conn.py:343 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "_wait_for_rescue falló: %s" + +#: nova/virt/libvirt_conn.py:370 +#, python-format +msgid "instance %s: is running" +msgstr "instancia %s: está ejecutándose" + +#: nova/virt/libvirt_conn.py:381 +#, python-format +msgid "instance %s: booted" +msgstr "instancia %s: arrancada" + +#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 +#, python-format +msgid "instance %s: failed to boot" +msgstr "insntancia %s: falló al arrancar" + +#: nova/virt/libvirt_conn.py:395 +#, python-format +msgid "virsh said: %r" +msgstr "virsh dijo: %r" + +#: nova/virt/libvirt_conn.py:399 +msgid "cool, it's a device" +msgstr "genial, es un dispositivo" + +#: nova/virt/libvirt_conn.py:407 +#, python-format +msgid "data: %r, fpath: %r" +msgstr "datos: %r, fpath: %r" + +#: nova/virt/libvirt_conn.py:415 +#, python-format +msgid "Contents of file %s: %r" +msgstr "Contenidos del fichero %s: %r" + +#: nova/virt/libvirt_conn.py:449 +#, python-format +msgid "instance %s: Creating image" +msgstr "instancia %s: Creando imagen" + +#: nova/virt/libvirt_conn.py:505 +#, python-format +msgid "instance %s: injecting key into image %s" +msgstr "instancia %s: inyectando clave en la imagen %s" + +#: nova/virt/libvirt_conn.py:508 +#, python-format +msgid "instance %s: injecting net into image %s" +msgstr "instancia %s: inyectando red en la imagen %s" + +#: nova/virt/libvirt_conn.py:516 +#, python-format +msgid "instance %s: ignoring error injecting data into image %s (%s)" +msgstr "" +"instancia %s: ignorando el error al inyectar datos en la imagen %s (%s)" + +#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "instancia %s: comenzando método toXML" + +#: nova/virt/libvirt_conn.py:589 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "instancia %s: finalizado método toXML" + +#: nova/virt/xenapi_conn.py:113 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" +"Debes especificar xenapi_connection_url, xenapi_connection_username " +"(opcional), y xenapi_connection_password para usar connection_type=xenapi" + +#: nova/virt/xenapi_conn.py:263 +#, python-format +msgid "Task [%s] %s status: success %s" +msgstr "Tarea [%s] %s estado: éxito %s" + +#: nova/virt/xenapi_conn.py:271 +#, python-format +msgid "Task [%s] %s status: %s %s" +msgstr "Tarea [%s] %s estado: %s %s" + +#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 +#, python-format +msgid "Got exception: %s" +msgstr "Obtenida excepción %s" + +#: nova/virt/xenapi/fake.py:72 +#, python-format +msgid "%s: _db_content => %s" +msgstr "%s: _db_content => %s" + +#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 +#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 +msgid "Raising NotImplemented" +msgstr "Lanzando NotImplemented" + +#: nova/virt/xenapi/fake.py:249 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "xenapi.fake no tiene una implementación para %s" + +#: nova/virt/xenapi/fake.py:283 +#, python-format +msgid "Calling %s %s" +msgstr "Llamando %s %s" + +#: nova/virt/xenapi/fake.py:288 +#, python-format +msgid "Calling getter %s" +msgstr "Llanado al adquiridor %s" + +#: nova/virt/xenapi/fake.py:340 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "" +"xenapi.fake no tiene una implementación para %s o ha sido llamada con un " +"número incorrecto de argumentos" + +#: nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "Encontrada una red no única para el puente %s" + +#: nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "No se ha encontrado red para el puente %s" + +#: nova/virt/xenapi/vm_utils.py:127 +#, python-format +msgid "Created VM %s as %s." +msgstr "Creada VM %s cómo %s" + +#: nova/virt/xenapi/vm_utils.py:147 +#, python-format +msgid "Creating VBD for VM %s, VDI %s ... " +msgstr "Creando VBD para VM %s, VDI %s... " + +#: nova/virt/xenapi/vm_utils.py:149 +#, python-format +msgid "Created VBD %s for VM %s, VDI %s." +msgstr "Creado VBD %s for VM %s, VDI %s." + +#: nova/virt/xenapi/vm_utils.py:165 +#, python-format +msgid "VBD not found in instance %s" +msgstr "VBD no encontrado en la instancia %s" + +#: nova/virt/xenapi/vm_utils.py:175 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "Imposible desconectar VBD %s" + +#: nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "Imposible destruir VBD %s" + +#: nova/virt/xenapi/vm_utils.py:202 +#, python-format +msgid "Creating VIF for VM %s, network %s." +msgstr "Creando VIF para VM %s, red %s." + +#: nova/virt/xenapi/vm_utils.py:205 +#, python-format +msgid "Created VIF %s for VM %s, network %s." +msgstr "Creado VIF %s para VM %s, red %s." + +#: nova/virt/xenapi/vm_utils.py:216 +#, python-format +msgid "Snapshotting VM %s with label '%s'..." +msgstr "Creando snapshot de la VM %s con la etiqueta '%s'..." + +#: nova/virt/xenapi/vm_utils.py:229 +#, python-format +msgid "Created snapshot %s from VM %s." +msgstr "Creando snapshot %s de la VM %s" + +#: nova/virt/xenapi/vm_utils.py:243 +#, python-format +msgid "Asking xapi to upload %s as '%s'" +msgstr "Solicitando a xapi la subida de %s cómo %s'" + +#: nova/virt/xenapi/vm_utils.py:261 +#, python-format +msgid "Asking xapi to fetch %s as %s" +msgstr "Solicitando a xapi obtener %s cómo %s" + +#: nova/virt/xenapi/vm_utils.py:279 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "Buscando vid %s para el kernel PV" + +#: nova/virt/xenapi/vm_utils.py:290 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "PV Kernel en VDI:%d" + +#: nova/virt/xenapi/vm_utils.py:318 +#, python-format +msgid "VDI %s is still available" +msgstr "VDI %s está todavía disponible" + +#: nova/virt/xenapi/vm_utils.py:331 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "(VM_UTILS) xenserver vm state -> |%s|" + +#: nova/virt/xenapi/vm_utils.py:333 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "(VM_UTILS) xenapi power_state -> |%s|" + +#: nova/virt/xenapi/vm_utils.py:390 +#, python-format +msgid "VHD %s has parent %s" +msgstr "VHD %s tiene cómo padre a %s" + +#: nova/virt/xenapi/vm_utils.py:407 +#, python-format +msgid "Re-scanning SR %s" +msgstr "Re-escaneando SR %s" + +#: nova/virt/xenapi/vm_utils.py:431 +#, python-format +msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." +msgstr "" +"El padre %s no concuerda con el padre original %s, esperando la unión..." + +#: nova/virt/xenapi/vm_utils.py:448 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "No se han encontrado VDI's para VM %s" + +#: nova/virt/xenapi/vm_utils.py:452 +#, python-format +msgid "Unexpected number of VDIs (%s) found for VM %s" +msgstr "Número no esperado de VDIs (%s) encontrados para VM %s" + +#: nova/virt/xenapi/vmops.py:62 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "Intentado la creación del nombre no único %s" + +#: nova/virt/xenapi/vmops.py:99 +#, python-format +msgid "Starting VM %s..." +msgstr "Iniciando VM %s..." + +#: nova/virt/xenapi/vmops.py:101 +#, python-format +msgid "Spawning VM %s created %s." +msgstr "Iniciando VM %s creado %s." + +#: nova/virt/xenapi/vmops.py:112 +#, python-format +msgid "Instance %s: booted" +msgstr "Instancia %s: iniciada" + +#: nova/virt/xenapi/vmops.py:137 +#, python-format +msgid "Instance not present %s" +msgstr "Instancia no existente %s" + +#: nova/virt/xenapi/vmops.py:166 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "Comenzando snapshot para la VM %s" + +#: nova/virt/xenapi/vmops.py:174 +#, python-format +msgid "Unable to Snapshot %s: %s" +msgstr "Incapaz de realizar snapshot %s: %s" + +#: nova/virt/xenapi/vmops.py:184 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "Finalizado el snapshot y la subida de la VM %s" + +#: nova/virt/xenapi/vmops.py:252 +#, python-format +msgid "suspend: instance not present %s" +msgstr "suspendido: instancia no encontrada: %s" + +#: nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "resume: instance not present %s" +msgstr "reanudar: instancia no encontrada %s" + +#: nova/virt/xenapi/vmops.py:271 +#, python-format +msgid "Instance not found %s" +msgstr "instancia no encontrada %s" + +#: nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "Introduciendo %s..." + +#: nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %s as %s." +msgstr "Introducido %s cómo %s." + +#: nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "Imposible crear el repositorio de almacenamiento" + +#: nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "Imposible encontrar SR en VBD %s" + +#: nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "Olvidando SR %s... " + +#: nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %s when getting PBDs for %s" +msgstr "Ignorando excepción %s al obtener PBDs de %s" + +#: nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %s when unplugging PBD %s" +msgstr "Ignorando excepción %s al desconectar PBD %s" + +#: nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "Olvidando SR %s completado." + +#: nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %s when forgetting SR %s" +msgstr "Ignorando excepción %s al olvidar SR %s" + +#: nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "Incapaz de insertar VDI en SR %s" + +#: nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "Imposible obtener copia del VDI %s en" + +#: nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "Inposible insertar VDI para SR %s" + +#: nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %s, %s" +msgstr "Imposible obtener información del destino %s, %s" + +#: nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "Punto de montaje no puede ser traducido: %s" + +#: nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %s, %s, %s" +msgstr "Attach_volume: %s, %s, %s" + +#: nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %s for instance %s" +msgstr "Inpoisble crear VDI en SR %s para la instancia %s" + +#: nova/virt/xenapi/volumeops.py:81 +#, python-format +msgid "Unable to use SR %s for instance %s" +msgstr "Imposible utilizar SR %s para la instancia %s" + +#: nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "Imposible adjuntar volumen a la instancia %s" + +#: nova/virt/xenapi/volumeops.py:95 +#, python-format +msgid "Mountpoint %s attached to instance %s" +msgstr "Punto de montaje %s unido a la instancia %s" + +#: nova/virt/xenapi/volumeops.py:106 +#, python-format +msgid "Detach_volume: %s, %s" +msgstr "Detach_volume: %s, %s" + +#: nova/virt/xenapi/volumeops.py:113 +#, python-format +msgid "Unable to locate volume %s" +msgstr "Imposible encontrar volumen %s" + +#: nova/virt/xenapi/volumeops.py:121 +#, python-format +msgid "Unable to detach volume %s" +msgstr "Imposible desasociar volumen %s" + +#: nova/virt/xenapi/volumeops.py:128 +#, python-format +msgid "Mountpoint %s detached from instance %s" +msgstr "Punto d emontaje %s desasociado de la instancia %s" + +#: nova/volume/api.py:44 +#, python-format +msgid "Quota exceeeded for %s, tried to create %sG volume" +msgstr "Quota excedida para %s, intentando crear el volumen %sG" + +#: nova/volume/api.py:46 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %s" +msgstr "Quota de volumen superada. No puedes crear un volumen de tamaño %s" + +#: nova/volume/api.py:70 nova/volume/api.py:95 +msgid "Volume status must be available" +msgstr "El estado del volumen debe estar disponible" + +#: nova/volume/api.py:97 +msgid "Volume is already attached" +msgstr "El volumen ya está asociado previamente" + +#: nova/volume/api.py:103 +msgid "Volume is already detached" +msgstr "El volumen ya ha sido desasociado previamente" + +#: nova/volume/driver.py:76 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "Recuperandose de una ejecución fallida. Intenta el número %s" + +#: nova/volume/driver.py:85 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "el grupo de volumenes %s no existe" + +#: nova/volume/driver.py:210 +#, python-format +msgid "FAKE AOE: %s" +msgstr "Falso AOE: %s" + +#: nova/volume/driver.py:315 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "Falso ISCSI: %s" + +#: nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "Exportando de nuevo los volumenes %s" + +#: nova/volume/manager.py:93 +#, python-format +msgid "volume %s: creating" +msgstr "volumen %s: creando" + +#: nova/volume/manager.py:102 +#, python-format +msgid "volume %s: creating lv of size %sG" +msgstr "volumen %s: creando lv de tamaño %sG" + +#: nova/volume/manager.py:106 +#, python-format +msgid "volume %s: creating export" +msgstr "volumen %s: exportando" + +#: nova/volume/manager.py:113 +#, python-format +msgid "volume %s: created successfully" +msgstr "volumen %s: creado satisfactoriamente" + +#: nova/volume/manager.py:121 +msgid "Volume is still attached" +msgstr "El volumen todavía está asociado" + +#: nova/volume/manager.py:123 +msgid "Volume is not local to this node" +msgstr "Volumen no local a este nodo" + +#: nova/volume/manager.py:124 +#, python-format +msgid "volume %s: removing export" +msgstr "volumen %s: eliminando exportación" + +#: nova/volume/manager.py:126 +#, python-format +msgid "volume %s: deleting" +msgstr "volumen %s: eliminando" + +#: nova/volume/manager.py:129 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "volumen %s: eliminado satisfactoriamente" diff --git a/locale/it.po b/locale/it.po new file mode 100644 index 000000000..f2f6a6b87 --- /dev/null +++ b/locale/it.po @@ -0,0 +1,2141 @@ +# Italian translation for nova +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the nova package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: nova\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-10 11:25-0800\n" +"PO-Revision-Date: 2011-01-14 17:17+0000\n" +"Last-Translator: Armando Migliaccio \n" +"Language-Team: Italian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Generator: Launchpad (build 12177)\n" + +#: nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "Nome del file root CA" + +#: nova/crypto.py:49 +msgid "Filename of private key" +msgstr "Nome del file della chiave privata" + +#: nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "" + +#: nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "Dove si conservano le chiavi" + +#: nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "Dove si conserva root CA" + +#: nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "Si dovrebbe usare un CA per ogni progetto?" + +#: nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "" +"Soggetto per il certificato degli utenti, %s per progetto, utente, orario" + +#: nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "Soggetto per il certificato dei progetti, %s per progetto, orario" + +#: nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "Soggetto per il certificato delle vpn, %s per progetto, orario" + +#: nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "Percorso dei flags: %s" + +#: nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "" +"Si e' verificato un errore inatteso durante l'esecuzione del comando." + +#: nova/exception.py:36 +#, python-format +msgid "" +"%s\n" +"Command: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" +msgstr "" +"%s\n" +"Comando: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" + +#: nova/exception.py:86 +msgid "Uncaught exception" +msgstr "Eccezione non gestita" + +#: nova/fakerabbit.py:48 +#, python-format +msgid "(%s) publish (key: %s) %s" +msgstr "(%s) pubblica (chiave: %s) %s" + +#: nova/fakerabbit.py:53 +#, python-format +msgid "Publishing to route %s" +msgstr "Pubblicando sulla route %s" + +#: nova/fakerabbit.py:83 +#, python-format +msgid "Declaring queue %s" +msgstr "Dichiarando la coda %s" + +#: nova/fakerabbit.py:89 +#, python-format +msgid "Declaring exchange %s" +msgstr "Dichiarando il centralino %s" + +#: nova/fakerabbit.py:95 +#, python-format +msgid "Binding %s to %s with key %s" +msgstr "Collegando %s a %s con la chiave %s" + +#: nova/fakerabbit.py:120 +#, python-format +msgid "Getting from %s: %s" +msgstr "" + +#: nova/rpc.py:92 +#, python-format +msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." +msgstr "" +"Il server AMQP su %s:%d non é raggiungibile. Riprovare in %d secondi." + +#: nova/rpc.py:99 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "" +"Impossibile connettersi al server AMQP dopo %d tentativi. Terminando " +"l'applicazione." + +#: nova/rpc.py:118 +msgid "Reconnected to queue" +msgstr "Riconnesso alla coda" + +#: nova/rpc.py:125 +msgid "Failed to fetch message from queue" +msgstr "Impossibile prelevare il messaggio dalla coda" + +#: nova/rpc.py:155 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "Inizializzando il Consumer Adapter per %s" + +#: nova/rpc.py:170 +#, python-format +msgid "received %s" +msgstr "ricevuto %s" + +#: nova/rpc.py:183 +#, python-format +msgid "no method for message: %s" +msgstr "nessun metodo per il messaggio: %s" + +#: nova/rpc.py:184 +#, python-format +msgid "No method for message: %s" +msgstr "nessun metodo per il messagggio: %s" + +#: nova/rpc.py:245 +#, python-format +msgid "Returning exception %s to caller" +msgstr "Sollevando eccezione %s al chiamante" + +#: nova/rpc.py:286 +#, python-format +msgid "unpacked context: %s" +msgstr "contesto decompresso: %s" + +#: nova/rpc.py:305 +msgid "Making asynchronous call..." +msgstr "Facendo chiamata asincrona..." + +#: nova/rpc.py:308 +#, python-format +msgid "MSG_ID is %s" +msgstr "MSG_ID é %s" + +#: nova/rpc.py:356 +#, python-format +msgid "response %s" +msgstr "risposta %s" + +#: nova/rpc.py:365 +#, python-format +msgid "topic is %s" +msgstr "argomento é %s" + +#: nova/rpc.py:366 +#, python-format +msgid "message %s" +msgstr "messaggio %s" + +#: nova/service.py:157 +#, python-format +msgid "Starting %s node" +msgstr "Avviando il nodo %s" + +#: nova/service.py:169 +msgid "Service killed that has no database entry" +msgstr "Servizio terminato che non ha entry nel database" + +#: nova/service.py:190 +msgid "The service database object disappeared, Recreating it." +msgstr "Il servizio é scomparso dal database, ricreo." + +#: nova/service.py:202 +msgid "Recovered model server connection!" +msgstr "Connessione al model server ripristinata!" + +#: nova/service.py:208 +msgid "model server went away" +msgstr "model server é scomparso" + +#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 +#, python-format +msgid "Data store %s is unreachable. Trying again in %d seconds." +msgstr "Datastore %s é irrangiungibile. Riprovare in %d seconds." + +#: nova/service.py:232 nova/twistd.py:232 +#, python-format +msgid "Serving %s" +msgstr "Servire %s" + +#: nova/service.py:234 nova/twistd.py:264 +msgid "Full set of FLAGS:" +msgstr "Insieme di FLAGS:" + +#: nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "" +"Il pidfile %s non esiste. Assicurarsi che il demone é in esecuzione.\n" + +#: nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "Avvio di %s" + +#: nova/utils.py:53 +#, python-format +msgid "Inner Exception: %s" +msgstr "Eccezione interna: %s" + +#: nova/utils.py:54 +#, python-format +msgid "Class %s cannot be found" +msgstr "Classe %s non può essere trovata" + +#: nova/utils.py:113 +#, python-format +msgid "Fetching %s" +msgstr "Prelievo %s" + +#: nova/utils.py:125 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "Esecuzione del comando (sottoprocesso): %s" + +#: nova/utils.py:138 +#, python-format +msgid "Result was %s" +msgstr "Il risultato é %s" + +#: nova/utils.py:171 +#, python-format +msgid "debug in callback: %s" +msgstr "debug in callback: %s" + +#: nova/utils.py:176 +#, python-format +msgid "Running %s" +msgstr "" + +#: nova/utils.py:207 +#, python-format +msgid "Couldn't get IP, using 127.0.0.1 %s" +msgstr "" + +#: nova/utils.py:289 +#, python-format +msgid "Invalid backend: %s" +msgstr "" + +#: nova/utils.py:300 +#, python-format +msgid "backend %s" +msgstr "" + +#: nova/api/ec2/__init__.py:133 +msgid "Too many failed authentications." +msgstr "" + +#: nova/api/ec2/__init__.py:142 +#, python-format +msgid "" +"Access key %s has had %d failed authentications and will be locked out for " +"%d minutes." +msgstr "" + +#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:190 +#, python-format +msgid "Authenticated Request For %s:%s)" +msgstr "" + +#: nova/api/ec2/__init__.py:227 +#, python-format +msgid "action: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:229 +#, python-format +msgid "arg: %s\t\tval: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:301 +#, python-format +msgid "Unauthorized request for controller=%s and action=%s" +msgstr "" + +#: nova/api/ec2/__init__.py:339 +#, python-format +msgid "NotFound raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:342 +#, python-format +msgid "ApiError raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:349 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:354 +msgid "An unknown error has occurred. Please try your request again." +msgstr "" + +#: nova/api/ec2/admin.py:84 +#, python-format +msgid "Creating new user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:92 +#, python-format +msgid "Deleting user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:114 +#, python-format +msgid "Adding role %s to user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 +#, python-format +msgid "Adding sitewide role %s to user %s" +msgstr "" + +#: nova/api/ec2/admin.py:122 +#, python-format +msgid "Removing role %s from user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 +#, python-format +msgid "Removing sitewide role %s from user %s" +msgstr "" + +#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 +msgid "operation must be add or remove" +msgstr "" + +#: nova/api/ec2/admin.py:142 +#, python-format +msgid "Getting x509 for user: %s on project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:159 +#, python-format +msgid "Create project %s managed by %s" +msgstr "" + +#: nova/api/ec2/admin.py:170 +#, python-format +msgid "Delete project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 +#, python-format +msgid "Adding user %s to project %s" +msgstr "" + +#: nova/api/ec2/admin.py:188 +#, python-format +msgid "Removing user %s from project %s" +msgstr "" + +#: nova/api/ec2/apirequest.py:95 +#, python-format +msgid "Unsupported API request: controller = %s,action = %s" +msgstr "" + +#: nova/api/ec2/cloud.py:117 +#, python-format +msgid "Generating root CA: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:277 +#, python-format +msgid "Create key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:285 +#, python-format +msgid "Delete key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:357 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "" + +#: nova/api/ec2/cloud.py:361 +msgid "Invalid port range" +msgstr "" + +#: nova/api/ec2/cloud.py:392 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 +msgid "No rule for the specified parameters." +msgstr "" + +#: nova/api/ec2/cloud.py:421 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:432 +#, python-format +msgid "This rule already exists in group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:460 +#, python-format +msgid "Create Security Group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:463 +#, python-format +msgid "group %s already exists" +msgstr "" + +#: nova/api/ec2/cloud.py:475 +#, python-format +msgid "Delete security group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 +#, python-format +msgid "Get console output for instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:543 +#, python-format +msgid "Create volume of %s GB" +msgstr "" + +#: nova/api/ec2/cloud.py:567 +#, python-format +msgid "Attach volume %s to instacne %s at %s" +msgstr "" + +#: nova/api/ec2/cloud.py:579 +#, python-format +msgid "Detach volume %s" +msgstr "" + +#: nova/api/ec2/cloud.py:686 +msgid "Allocate address" +msgstr "" + +#: nova/api/ec2/cloud.py:691 +#, python-format +msgid "Release address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:696 +#, python-format +msgid "Associate address %s to instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:703 +#, python-format +msgid "Disassociate address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:730 +msgid "Going to start terminating instances" +msgstr "" + +#: nova/api/ec2/cloud.py:738 +#, python-format +msgid "Reboot instance %r" +msgstr "" + +#: nova/api/ec2/cloud.py:775 +#, python-format +msgid "De-registering image %s" +msgstr "" + +#: nova/api/ec2/cloud.py:783 +#, python-format +msgid "Registered image %s with id %s" +msgstr "" + +#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 +#, python-format +msgid "attribute not supported: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:794 +#, python-format +msgid "invalid id: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:807 +msgid "user or group not specified" +msgstr "" + +#: nova/api/ec2/cloud.py:809 +msgid "only group \"all\" is supported" +msgstr "" + +#: nova/api/ec2/cloud.py:811 +msgid "operation_type must be add or remove" +msgstr "" + +#: nova/api/ec2/cloud.py:812 +#, python-format +msgid "Updating image %s publicity" +msgstr "" + +#: nova/api/ec2/metadatarequesthandler.py:75 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:70 +#, python-format +msgid "Caught error: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:86 +msgid "Including admin operations in API." +msgstr "" + +#: nova/api/openstack/servers.py:184 +#, python-format +msgid "Compute.api::lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:199 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "" + +#: nova/api/openstack/servers.py:213 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:224 +#, python-format +msgid "Compute.api::pause %s" +msgstr "" + +#: nova/api/openstack/servers.py:235 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "" + +#: nova/api/openstack/servers.py:246 +#, python-format +msgid "compute.api::suspend %s" +msgstr "" + +#: nova/api/openstack/servers.py:257 +#, python-format +msgid "compute.api::resume %s" +msgstr "" + +#: nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "" + +#: nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "" + +#: nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "" + +#: nova/auth/ldapdriver.py:181 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:218 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:478 +#, python-format +msgid "User %s is already a member of the group %s" +msgstr "" + +#: nova/auth/ldapdriver.py:507 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "" + +#: nova/auth/ldapdriver.py:528 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "" + +#: nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "" + +#: nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "" + +#: nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "" + +#: nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "" + +#: nova/auth/manager.py:275 +#, python-format +msgid "failed authorization: no project named %s (user=%s)" +msgstr "" + +#: nova/auth/manager.py:277 +#, python-format +msgid "No project called %s could be found" +msgstr "" + +#: nova/auth/manager.py:281 +#, python-format +msgid "Failed authorization: user %s not admin and not member of project %s" +msgstr "" + +#: nova/auth/manager.py:283 +#, python-format +msgid "User %s is not a member of project %s" +msgstr "" + +#: nova/auth/manager.py:292 nova/auth/manager.py:303 +#, python-format +msgid "Invalid signature for user %s" +msgstr "" + +#: nova/auth/manager.py:293 nova/auth/manager.py:304 +msgid "Signature does not match" +msgstr "" + +#: nova/auth/manager.py:374 +msgid "Must specify project" +msgstr "" + +#: nova/auth/manager.py:408 +#, python-format +msgid "The %s role can not be found" +msgstr "" + +#: nova/auth/manager.py:410 +#, python-format +msgid "The %s role is global only" +msgstr "" + +#: nova/auth/manager.py:412 +#, python-format +msgid "Adding role %s to user %s in project %s" +msgstr "" + +#: nova/auth/manager.py:438 +#, python-format +msgid "Removing role %s from user %s on project %s" +msgstr "" + +#: nova/auth/manager.py:505 +#, python-format +msgid "Created project %s with manager %s" +msgstr "" + +#: nova/auth/manager.py:523 +#, python-format +msgid "modifying project %s" +msgstr "" + +#: nova/auth/manager.py:553 +#, python-format +msgid "Remove user %s from project %s" +msgstr "" + +#: nova/auth/manager.py:581 +#, python-format +msgid "Deleting project %s" +msgstr "" + +#: nova/auth/manager.py:637 +#, python-format +msgid "Created user %s (admin: %r)" +msgstr "" + +#: nova/auth/manager.py:645 +#, python-format +msgid "Deleting user %s" +msgstr "" + +#: nova/auth/manager.py:655 +#, python-format +msgid "Access Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:657 +#, python-format +msgid "Secret Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:659 +#, python-format +msgid "Admin status set to %r for user %s" +msgstr "" + +#: nova/auth/manager.py:708 +#, python-format +msgid "No vpn data for project %s" +msgstr "" + +#: nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "" + +#: nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "" + +#: nova/compute/api.py:67 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "" + +#: nova/compute/api.py:73 +#, python-format +msgid "Instance %d has no host" +msgstr "" + +#: nova/compute/api.py:92 +#, python-format +msgid "Quota exceeeded for %s, tried to run %s instances" +msgstr "" + +#: nova/compute/api.py:94 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "" + +#: nova/compute/api.py:109 +msgid "Creating a raw instance" +msgstr "" + +#: nova/compute/api.py:156 +#, python-format +msgid "Going to run %s instances..." +msgstr "" + +#: nova/compute/api.py:180 +#, python-format +msgid "Casting to scheduler for %s/%s's instance %s" +msgstr "" + +#: nova/compute/api.py:279 +#, python-format +msgid "Going to try and terminate %s" +msgstr "" + +#: nova/compute/api.py:283 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "" + +#: nova/compute/api.py:288 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "" + +#: nova/compute/api.py:450 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "" + +#: nova/compute/api.py:465 +msgid "Volume isn't attached to anything!" +msgstr "" + +#: nova/compute/disk.py:71 +#, python-format +msgid "Input partition size not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:75 +#, python-format +msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "" + +#: nova/compute/disk.py:136 +#, python-format +msgid "Failed to load partition: %s" +msgstr "" + +#: nova/compute/disk.py:158 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "" + +#: nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "" + +#: nova/compute/manager.py:69 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "" + +#: nova/compute/manager.py:71 +#, python-format +msgid "check_instance_lock: arguments: |%s| |%s| |%s|" +msgstr "" + +#: nova/compute/manager.py:75 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "" + +#: nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "" + +#: nova/compute/manager.py:82 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "" + +#: nova/compute/manager.py:86 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "" + +#: nova/compute/manager.py:157 +msgid "Instance has already been created" +msgstr "" + +#: nova/compute/manager.py:158 +#, python-format +msgid "instance %s: starting..." +msgstr "" + +#: nova/compute/manager.py:197 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "" + +#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 +#, python-format +msgid "Terminating instance %s" +msgstr "" + +#: nova/compute/manager.py:217 +#, python-format +msgid "Disassociating address %s" +msgstr "" + +#: nova/compute/manager.py:230 +#, python-format +msgid "Deallocating address %s" +msgstr "" + +#: nova/compute/manager.py:243 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "" + +#: nova/compute/manager.py:257 +#, python-format +msgid "Rebooting instance %s" +msgstr "" + +#: nova/compute/manager.py:260 +#, python-format +msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:286 +#, python-format +msgid "instance %s: snapshotting" +msgstr "" + +#: nova/compute/manager.py:289 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:301 +#, python-format +msgid "instance %s: rescuing" +msgstr "" + +#: nova/compute/manager.py:316 +#, python-format +msgid "instance %s: unrescuing" +msgstr "" + +#: nova/compute/manager.py:335 +#, python-format +msgid "instance %s: pausing" +msgstr "" + +#: nova/compute/manager.py:352 +#, python-format +msgid "instance %s: unpausing" +msgstr "" + +#: nova/compute/manager.py:369 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "" + +#: nova/compute/manager.py:382 +#, python-format +msgid "instance %s: suspending" +msgstr "" + +#: nova/compute/manager.py:401 +#, python-format +msgid "instance %s: resuming" +msgstr "" + +#: nova/compute/manager.py:420 +#, python-format +msgid "instance %s: locking" +msgstr "" + +#: nova/compute/manager.py:432 +#, python-format +msgid "instance %s: unlocking" +msgstr "" + +#: nova/compute/manager.py:442 +#, python-format +msgid "instance %s: getting locked state" +msgstr "" + +#: nova/compute/manager.py:462 +#, python-format +msgid "instance %s: attaching volume %s to %s" +msgstr "" + +#: nova/compute/manager.py:478 +#, python-format +msgid "instance %s: attach failed %s, removing" +msgstr "" + +#: nova/compute/manager.py:493 +#, python-format +msgid "Detach volume %s from mountpoint %s on instance %s" +msgstr "" + +#: nova/compute/manager.py:497 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "" + +#: nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "" + +#: nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "" + +#: nova/compute/monitor.py:355 +#, python-format +msgid "Cannot get blockstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:377 +#, python-format +msgid "Cannot get ifstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:412 +msgid "unexpected exception getting connection" +msgstr "" + +#: nova/compute/monitor.py:427 +#, python-format +msgid "Found instance: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:43 +msgid "Use of empty request context is deprecated" +msgstr "" + +#: nova/db/sqlalchemy/api.py:132 +#, python-format +msgid "No service for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:229 +#, python-format +msgid "No service for %s, %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:574 +#, python-format +msgid "No floating ip for address %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:668 +#, python-format +msgid "No instance for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 +#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 +#, python-format +msgid "Instance %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:891 +#, python-format +msgid "no keypair for user %s, name %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 +#, python-format +msgid "No network for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1036 +#, python-format +msgid "No network for bridge %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1050 +#, python-format +msgid "No network for instance %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1180 +#, python-format +msgid "Token %s does not exist" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1205 +#, python-format +msgid "No quota for project_id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1356 +#, python-format +msgid "No volume for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1401 +#, python-format +msgid "Volume %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1413 +#, python-format +msgid "No export device found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1426 +#, python-format +msgid "No target id found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1471 +#, python-format +msgid "No security group with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1488 +#, python-format +msgid "No security group named %s for project: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1576 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1650 +#, python-format +msgid "No user for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1666 +#, python-format +msgid "No user for access key %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1728 +#, python-format +msgid "No project with id %s" +msgstr "" + +#: nova/image/glance.py:78 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images" +msgstr "" + +#: nova/image/glance.py:97 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images/detail" +msgstr "" + +#: nova/image/s3.py:82 +#, python-format +msgid "Image %s could not be found" +msgstr "" + +#: nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "" + +#: nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "" + +#: nova/network/linux_net.py:176 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "" + +#: nova/network/linux_net.py:186 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "" + +#: nova/network/linux_net.py:254 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "" + +#: nova/network/linux_net.py:256 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "" + +#: nova/network/linux_net.py:334 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "" + +#: nova/network/manager.py:135 +msgid "setting network host" +msgstr "" + +#: nova/network/manager.py:190 +#, python-format +msgid "Leasing IP %s" +msgstr "" + +#: nova/network/manager.py:194 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "" + +#: nova/network/manager.py:197 +#, python-format +msgid "IP %s leased to bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:205 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "" + +#: nova/network/manager.py:214 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "" + +#: nova/network/manager.py:217 +#, python-format +msgid "IP %s released from bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:220 +#, python-format +msgid "IP %s released that was not leased" +msgstr "" + +#: nova/network/manager.py:442 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "" + +#: nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "" + +#: nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "" + +#: nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "" + +#: nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:271 +#, python-format +msgid "Getting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:274 +#, python-format +msgid "Unauthorized attempt to get object %s from bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:292 +#, python-format +msgid "Putting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:295 +#, python-format +msgid "Unauthorized attempt to upload object %s to bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:314 +#, python-format +msgid "Deleting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:393 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "" + +#: nova/objectstore/handler.py:401 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:406 +#, python-format +msgid "Starting image upload: %s" +msgstr "" + +#: nova/objectstore/handler.py:420 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "" + +#: nova/objectstore/handler.py:428 +#, python-format +msgid "Toggling publicity flag of image %s %r" +msgstr "" + +#: nova/objectstore/handler.py:433 +#, python-format +msgid "Updating user fields on image %s" +msgstr "" + +#: nova/objectstore/handler.py:447 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "" + +#: nova/objectstore/handler.py:452 +#, python-format +msgid "Deleted image: %s" +msgstr "" + +#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 +#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 +msgid "No hosts found" +msgstr "" + +#: nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "" + +#: nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %s %s for %s" +msgstr "" + +#: nova/scheduler/simple.py:63 +msgid "All hosts have too many cores" +msgstr "" + +#: nova/scheduler/simple.py:95 +msgid "All hosts have too many gigabytes" +msgstr "" + +#: nova/scheduler/simple.py:115 +msgid "All hosts have too many networks" +msgstr "" + +#: nova/tests/test_cloud.py:198 +msgid "Can't test instances without a real virtual env." +msgstr "" + +#: nova/tests/test_cloud.py:210 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "" + +#: nova/tests/test_compute.py:104 +#, python-format +msgid "Running instances: %s" +msgstr "" + +#: nova/tests/test_compute.py:110 +#, python-format +msgid "After terminating instances: %s" +msgstr "" + +#: nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %s, %s" +msgstr "" + +#: nova/tests/test_rpc.py:94 +#, python-format +msgid "Nested return %s" +msgstr "" + +#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 +#, python-format +msgid "Received %s" +msgstr "" + +#: nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "" + +#: nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "" + +#: nova/virt/fake.py:210 +#, python-format +msgid "Instance %s Not Found" +msgstr "" + +#: nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "" + +#: nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "" + +#: nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "" + +#: nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "" + +#: nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "" + +#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 +#, python-format +msgid "Created VM %s..." +msgstr "" + +#: nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %s by attaching disk file %s" +msgstr "" + +#: nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "" + +#: nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "" + +#: nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "" + +#: nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "" + +#: nova/virt/hyperv.py:275 +#, python-format +msgid "Created switch port %s on switch %s" +msgstr "" + +#: nova/virt/hyperv.py:285 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:287 +#, python-format +msgid "Created nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:320 +#, python-format +msgid "WMI job failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:322 +#, python-format +msgid "WMI job succeeded: %s, Elapsed=%s " +msgstr "" + +#: nova/virt/hyperv.py:358 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:383 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:389 +#, python-format +msgid "Del: disk %s vm %s" +msgstr "" + +#: nova/virt/hyperv.py:405 +#, python-format +msgid "" +"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " +"cpu_time=%s" +msgstr "" + +#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 +#, python-format +msgid "duplicate name found: %s" +msgstr "" + +#: nova/virt/hyperv.py:444 +#, python-format +msgid "Successfully changed vm state of %s to %s" +msgstr "" + +#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 +#, python-format +msgid "Failed to change vm state of %s to %s" +msgstr "" + +#: nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %s -- placed in %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:144 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:157 +msgid "Connection to libvirt broke" +msgstr "" + +#: nova/virt/libvirt_conn.py:229 +#, python-format +msgid "instance %s: deleting instance files %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:271 +#, python-format +msgid "No disk at %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:278 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "" + +#: nova/virt/libvirt_conn.py:294 +#, python-format +msgid "instance %s: rebooted" +msgstr "" + +#: nova/virt/libvirt_conn.py:297 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:340 +#, python-format +msgid "instance %s: rescued" +msgstr "" + +#: nova/virt/libvirt_conn.py:343 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:370 +#, python-format +msgid "instance %s: is running" +msgstr "" + +#: nova/virt/libvirt_conn.py:381 +#, python-format +msgid "instance %s: booted" +msgstr "" + +#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 +#, python-format +msgid "instance %s: failed to boot" +msgstr "" + +#: nova/virt/libvirt_conn.py:395 +#, python-format +msgid "virsh said: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:399 +msgid "cool, it's a device" +msgstr "" + +#: nova/virt/libvirt_conn.py:407 +#, python-format +msgid "data: %r, fpath: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:415 +#, python-format +msgid "Contents of file %s: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:449 +#, python-format +msgid "instance %s: Creating image" +msgstr "" + +#: nova/virt/libvirt_conn.py:505 +#, python-format +msgid "instance %s: injecting key into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:508 +#, python-format +msgid "instance %s: injecting net into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:516 +#, python-format +msgid "instance %s: ignoring error injecting data into image %s (%s)" +msgstr "" + +#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "" + +#: nova/virt/libvirt_conn.py:589 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "" + +#: nova/virt/xenapi_conn.py:113 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" + +#: nova/virt/xenapi_conn.py:263 +#, python-format +msgid "Task [%s] %s status: success %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:271 +#, python-format +msgid "Task [%s] %s status: %s %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 +#, python-format +msgid "Got exception: %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:72 +#, python-format +msgid "%s: _db_content => %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 +#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 +msgid "Raising NotImplemented" +msgstr "" + +#: nova/virt/xenapi/fake.py:249 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:283 +#, python-format +msgid "Calling %s %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:288 +#, python-format +msgid "Calling getter %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:340 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:127 +#, python-format +msgid "Created VM %s as %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:147 +#, python-format +msgid "Creating VBD for VM %s, VDI %s ... " +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:149 +#, python-format +msgid "Created VBD %s for VM %s, VDI %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:165 +#, python-format +msgid "VBD not found in instance %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:175 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:202 +#, python-format +msgid "Creating VIF for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:205 +#, python-format +msgid "Created VIF %s for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:216 +#, python-format +msgid "Snapshotting VM %s with label '%s'..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:229 +#, python-format +msgid "Created snapshot %s from VM %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:243 +#, python-format +msgid "Asking xapi to upload %s as '%s'" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:261 +#, python-format +msgid "Asking xapi to fetch %s as %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:279 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:290 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:318 +#, python-format +msgid "VDI %s is still available" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:331 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:333 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:390 +#, python-format +msgid "VHD %s has parent %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:407 +#, python-format +msgid "Re-scanning SR %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:431 +#, python-format +msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:448 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:452 +#, python-format +msgid "Unexpected number of VDIs (%s) found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:62 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:99 +#, python-format +msgid "Starting VM %s..." +msgstr "" + +#: nova/virt/xenapi/vmops.py:101 +#, python-format +msgid "Spawning VM %s created %s." +msgstr "" + +#: nova/virt/xenapi/vmops.py:112 +#, python-format +msgid "Instance %s: booted" +msgstr "" + +#: nova/virt/xenapi/vmops.py:137 +#, python-format +msgid "Instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:166 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:174 +#, python-format +msgid "Unable to Snapshot %s: %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:184 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:252 +#, python-format +msgid "suspend: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "resume: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:271 +#, python-format +msgid "Instance not found %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %s as %s." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %s when getting PBDs for %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %s when unplugging PBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %s when forgetting SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %s, %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %s, %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:81 +#, python-format +msgid "Unable to use SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:95 +#, python-format +msgid "Mountpoint %s attached to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:106 +#, python-format +msgid "Detach_volume: %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:113 +#, python-format +msgid "Unable to locate volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:121 +#, python-format +msgid "Unable to detach volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:128 +#, python-format +msgid "Mountpoint %s detached from instance %s" +msgstr "" + +#: nova/volume/api.py:44 +#, python-format +msgid "Quota exceeeded for %s, tried to create %sG volume" +msgstr "" + +#: nova/volume/api.py:46 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %s" +msgstr "" + +#: nova/volume/api.py:70 nova/volume/api.py:95 +msgid "Volume status must be available" +msgstr "" + +#: nova/volume/api.py:97 +msgid "Volume is already attached" +msgstr "" + +#: nova/volume/api.py:103 +msgid "Volume is already detached" +msgstr "" + +#: nova/volume/driver.py:76 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "" + +#: nova/volume/driver.py:85 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "" + +#: nova/volume/driver.py:210 +#, python-format +msgid "FAKE AOE: %s" +msgstr "" + +#: nova/volume/driver.py:315 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "" + +#: nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "" + +#: nova/volume/manager.py:93 +#, python-format +msgid "volume %s: creating" +msgstr "" + +#: nova/volume/manager.py:102 +#, python-format +msgid "volume %s: creating lv of size %sG" +msgstr "" + +#: nova/volume/manager.py:106 +#, python-format +msgid "volume %s: creating export" +msgstr "" + +#: nova/volume/manager.py:113 +#, python-format +msgid "volume %s: created successfully" +msgstr "" + +#: nova/volume/manager.py:121 +msgid "Volume is still attached" +msgstr "" + +#: nova/volume/manager.py:123 +msgid "Volume is not local to this node" +msgstr "" + +#: nova/volume/manager.py:124 +#, python-format +msgid "volume %s: removing export" +msgstr "" + +#: nova/volume/manager.py:126 +#, python-format +msgid "volume %s: deleting" +msgstr "" + +#: nova/volume/manager.py:129 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "" diff --git a/locale/ja.po b/locale/ja.po new file mode 100644 index 000000000..919625e9a --- /dev/null +++ b/locale/ja.po @@ -0,0 +1,2143 @@ +# Japanese translation for nova +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the nova package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: nova\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-10 11:25-0800\n" +"PO-Revision-Date: 2011-01-14 09:04+0000\n" +"Last-Translator: Koji Iida \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Generator: Launchpad (build 12177)\n" + +#: nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "ルートCAのファイル名" + +#: nova/crypto.py:49 +msgid "Filename of private key" +msgstr "プライベートキーのファイル名" + +#: nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "ルート証明書失効リストのファイル名" + +#: nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "キーを格納するパス" + +#: nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "ルートCAを格納するパス" + +#: nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "プロジェクトごとにCAを使用するか否かのフラグ" + +#: nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "ユーザの証明書のサブジェクト、%s はプロジェクト、ユーザ、タイムスタンプ" + +#: nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "プロジェクトの証明書のサブジェクト、%s はプロジェクト、およびタイムスタンプ" + +#: nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "vpnの証明書のサブジェクト、%sはプロジェクト、およびタイムスタンプ" + +#: nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "Flags のパス: %s" + +#: nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "コマンド実行において予期しないエラーが発生しました。" + +#: nova/exception.py:36 +#, python-format +msgid "" +"%s\n" +"Command: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" +msgstr "" +"%s\n" +"コマンド: %s\n" +"終了コード: %s\n" +"標準出力: %r\n" +"標準エラー出力: %r" + +#: nova/exception.py:86 +msgid "Uncaught exception" +msgstr "キャッチされなかった例外" + +#: nova/fakerabbit.py:48 +#, python-format +msgid "(%s) publish (key: %s) %s" +msgstr "(%s) パブリッシュ (key: %s) %s" + +#: nova/fakerabbit.py:53 +#, python-format +msgid "Publishing to route %s" +msgstr "ルート %s へパブリッシュ" + +#: nova/fakerabbit.py:83 +#, python-format +msgid "Declaring queue %s" +msgstr "queue %s の宣言" + +#: nova/fakerabbit.py:89 +#, python-format +msgid "Declaring exchange %s" +msgstr "exchange %s の宣言" + +#: nova/fakerabbit.py:95 +#, python-format +msgid "Binding %s to %s with key %s" +msgstr "%s を %s にキー %s でバインドします。" + +#: nova/fakerabbit.py:120 +#, python-format +msgid "Getting from %s: %s" +msgstr "%s から %s を取得" + +#: nova/rpc.py:92 +#, python-format +msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." +msgstr "AMQPサーバ %s:%d に接続できません。 %d 秒後に再度試みます。" + +#: nova/rpc.py:99 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "AMQPサーバーに %d 回接続を試みましたが、接続できませんでした。シャットダウンします。" + +#: nova/rpc.py:118 +msgid "Reconnected to queue" +msgstr "キューに再接続しました。" + +#: nova/rpc.py:125 +msgid "Failed to fetch message from queue" +msgstr "キューからメッセージの取得に失敗しました。" + +#: nova/rpc.py:155 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "%sのアダプターコンシューマー(Adapter Consumer)を初期化しています。" + +#: nova/rpc.py:170 +#, python-format +msgid "received %s" +msgstr "受信: %s" + +#: nova/rpc.py:183 +#, python-format +msgid "no method for message: %s" +msgstr "メッセージ %s に対するメソッドが存在しません。" + +#: nova/rpc.py:184 +#, python-format +msgid "No method for message: %s" +msgstr "メッセージ %s に対するメソッドが存在しません。" + +#: nova/rpc.py:245 +#, python-format +msgid "Returning exception %s to caller" +msgstr "呼び出し元に 例外 %s を返却します。" + +#: nova/rpc.py:286 +#, python-format +msgid "unpacked context: %s" +msgstr "context %s をアンパックしました。" + +#: nova/rpc.py:305 +msgid "Making asynchronous call..." +msgstr "非同期呼び出しを実行します…" + +#: nova/rpc.py:308 +#, python-format +msgid "MSG_ID is %s" +msgstr "MSG_IDは %s です。" + +#: nova/rpc.py:356 +#, python-format +msgid "response %s" +msgstr "応答 %s" + +#: nova/rpc.py:365 +#, python-format +msgid "topic is %s" +msgstr "topic は %s です。" + +#: nova/rpc.py:366 +#, python-format +msgid "message %s" +msgstr "メッセージ %s" + +#: nova/service.py:157 +#, python-format +msgid "Starting %s node" +msgstr "ノード %s を開始します。" + +#: nova/service.py:169 +msgid "Service killed that has no database entry" +msgstr "データベースにエントリの存在しないサービスを終了します。" + +#: nova/service.py:190 +msgid "The service database object disappeared, Recreating it." +msgstr "サービスデータベースオブジェクトが消滅しました。再作成します。" + +#: nova/service.py:202 +msgid "Recovered model server connection!" +msgstr "モデルサーバへの接続を復旧しました。" + +#: nova/service.py:208 +msgid "model server went away" +msgstr "モデルサーバが消滅しました。" + +#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 +#, python-format +msgid "Data store %s is unreachable. Trying again in %d seconds." +msgstr "データストア %s に接続できません。 %d 秒後に再接続します。" + +#: nova/service.py:232 nova/twistd.py:232 +#, python-format +msgid "Serving %s" +msgstr "%s サービスの開始" + +#: nova/service.py:234 nova/twistd.py:264 +msgid "Full set of FLAGS:" +msgstr "FLAGSの一覧:" + +#: nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "pidfile %s が存在しません。デーモンは実行中ですか?\n" + +#: nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "%s を開始します。" + +#: nova/utils.py:53 +#, python-format +msgid "Inner Exception: %s" +msgstr "内側で発生した例外: %s" + +#: nova/utils.py:54 +#, python-format +msgid "Class %s cannot be found" +msgstr "クラス %s が見つかりません。" + +#: nova/utils.py:113 +#, python-format +msgid "Fetching %s" +msgstr "ファイルをフェッチ: %s" + +#: nova/utils.py:125 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "コマンド実行(subprocess): %s" + +#: nova/utils.py:138 +#, python-format +msgid "Result was %s" +msgstr "コマンド実行結果: %s" + +#: nova/utils.py:171 +#, python-format +msgid "debug in callback: %s" +msgstr "コールバック中のデバッグ: %s" + +#: nova/utils.py:176 +#, python-format +msgid "Running %s" +msgstr "コマンド実行: %s" + +#: nova/utils.py:207 +#, python-format +msgid "Couldn't get IP, using 127.0.0.1 %s" +msgstr "IPを取得できません。127.0.0.1 を %s として使います。" + +#: nova/utils.py:289 +#, python-format +msgid "Invalid backend: %s" +msgstr "不正なバックエンドです: %s" + +#: nova/utils.py:300 +#, python-format +msgid "backend %s" +msgstr "バックエンドは %s です。" + +#: nova/api/ec2/__init__.py:133 +msgid "Too many failed authentications." +msgstr "認証失敗の回数が多すぎます。" + +#: nova/api/ec2/__init__.py:142 +#, python-format +msgid "" +"Access key %s has had %d failed authentications and will be locked out for " +"%d minutes." +msgstr "アクセスキー %s は %d 回認証に失敗したため、%d 分間ロックされます。" + +#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "%s の認証に失敗しました。" + +#: nova/api/ec2/__init__.py:190 +#, python-format +msgid "Authenticated Request For %s:%s)" +msgstr "リクエストを認証しました: %s:%s" + +#: nova/api/ec2/__init__.py:227 +#, python-format +msgid "action: %s" +msgstr "アクション(action): %s" + +#: nova/api/ec2/__init__.py:229 +#, python-format +msgid "arg: %s\t\tval: %s" +msgstr "引数(arg): %s\t値(val): %s" + +#: nova/api/ec2/__init__.py:301 +#, python-format +msgid "Unauthorized request for controller=%s and action=%s" +msgstr "許可されていないリクエスト: controller=%s, action %sです。" + +#: nova/api/ec2/__init__.py:339 +#, python-format +msgid "NotFound raised: %s" +msgstr "NotFound 発生: %s" + +#: nova/api/ec2/__init__.py:342 +#, python-format +msgid "ApiError raised: %s" +msgstr "APIエラー発生: %s" + +#: nova/api/ec2/__init__.py:349 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "予期しないエラー発生: %s" + +#: nova/api/ec2/__init__.py:354 +msgid "An unknown error has occurred. Please try your request again." +msgstr "未知のエラーが発生しました。再度リクエストを実行してください。" + +#: nova/api/ec2/admin.py:84 +#, python-format +msgid "Creating new user: %s" +msgstr "Creating new user: 新しいユーザ %s を作成します。" + +#: nova/api/ec2/admin.py:92 +#, python-format +msgid "Deleting user: %s" +msgstr "Deleting user: ユーザ %s を削除します。" + +#: nova/api/ec2/admin.py:114 +#, python-format +msgid "Adding role %s to user %s for project %s" +msgstr "Adding role: ロール %s をユーザ %s、プロジェクト %s に追加します。" + +#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 +#, python-format +msgid "Adding sitewide role %s to user %s" +msgstr "Adding sitewide role: サイトワイドのロール %s をユーザ %s に追加します。" + +#: nova/api/ec2/admin.py:122 +#, python-format +msgid "Removing role %s from user %s for project %s" +msgstr "Removing role: ロール %s をユーザ %s プロジェクト %s から削除します。" + +#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 +#, python-format +msgid "Removing sitewide role %s from user %s" +msgstr "Removing sitewide role: サイトワイドのロール %s をユーザ %s から削除します。" + +#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 +msgid "operation must be add or remove" +msgstr "operation は add または remove の何れかである必要があります。" + +#: nova/api/ec2/admin.py:142 +#, python-format +msgid "Getting x509 for user: %s on project: %s" +msgstr "Getting X509: x509の取得: ユーザ %s, プロジェクト %s" + +#: nova/api/ec2/admin.py:159 +#, python-format +msgid "Create project %s managed by %s" +msgstr "Create project: プロジェクト %s (%s により管理される)を作成します。" + +#: nova/api/ec2/admin.py:170 +#, python-format +msgid "Delete project: %s" +msgstr "Delete project: プロジェクト %s を削除しました。" + +#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 +#, python-format +msgid "Adding user %s to project %s" +msgstr "Adding user: ユーザ %s をプロジェクト %s に追加します。" + +#: nova/api/ec2/admin.py:188 +#, python-format +msgid "Removing user %s from project %s" +msgstr "Removing user: ユーザ %s をプロジェクト %s から削除します。" + +#: nova/api/ec2/apirequest.py:95 +#, python-format +msgid "Unsupported API request: controller = %s,action = %s" +msgstr "サポートされていないAPIリクエストです。 controller = %s,action = %s" + +#: nova/api/ec2/cloud.py:117 +#, python-format +msgid "Generating root CA: %s" +msgstr "ルートCA %s を生成しています。" + +#: nova/api/ec2/cloud.py:277 +#, python-format +msgid "Create key pair %s" +msgstr "Create key pair: キーペア %s を作成します。" + +#: nova/api/ec2/cloud.py:285 +#, python-format +msgid "Delete key pair %s" +msgstr "Delete key pair: キーペア %s を削除します。" + +#: nova/api/ec2/cloud.py:357 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "%s は適切なipProtocolではありません。" + +#: nova/api/ec2/cloud.py:361 +msgid "Invalid port range" +msgstr "ポートの範囲が不正です。" + +#: nova/api/ec2/cloud.py:392 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "Revoke security group ingress: セキュリティグループ許可 %s の取消" + +#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 +msgid "No rule for the specified parameters." +msgstr "指定されたパラメータに該当するルールがありません。" + +#: nova/api/ec2/cloud.py:421 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "Authorize security group ingress: セキュリティグループ許可 %s" + +#: nova/api/ec2/cloud.py:432 +#, python-format +msgid "This rule already exists in group %s" +msgstr "指定されたルールは既にグループ %s に存在しています。" + +#: nova/api/ec2/cloud.py:460 +#, python-format +msgid "Create Security Group %s" +msgstr "Create Security Group: セキュリティグループ %s を作成します。" + +#: nova/api/ec2/cloud.py:463 +#, python-format +msgid "group %s already exists" +msgstr "グループ %s は既に存在しています。" + +#: nova/api/ec2/cloud.py:475 +#, python-format +msgid "Delete security group %s" +msgstr "Delete security group: セキュリティグループ %s を削除します。" + +#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 +#, python-format +msgid "Get console output for instance %s" +msgstr "Get console output: インスタンス %s のコンソール出力を取得します。" + +#: nova/api/ec2/cloud.py:543 +#, python-format +msgid "Create volume of %s GB" +msgstr "Create volume: %s GBのボリュームを作成します。" + +#: nova/api/ec2/cloud.py:567 +#, python-format +msgid "Attach volume %s to instacne %s at %s" +msgstr "Attach volume: ボリューム%s をインスタンス %s にデバイス %s でアタッチします。" + +#: nova/api/ec2/cloud.py:579 +#, python-format +msgid "Detach volume %s" +msgstr "Detach volume: ボリューム %s をデタッチします" + +#: nova/api/ec2/cloud.py:686 +msgid "Allocate address" +msgstr "Allocate address: アドレスを割り当てます。" + +#: nova/api/ec2/cloud.py:691 +#, python-format +msgid "Release address %s" +msgstr "Release address: アドレス %s を開放します。" + +#: nova/api/ec2/cloud.py:696 +#, python-format +msgid "Associate address %s to instance %s" +msgstr "Associate address: アドレス %s をインスタンス %s に関連付けます。" + +#: nova/api/ec2/cloud.py:703 +#, python-format +msgid "Disassociate address %s" +msgstr "Disassociate address: アドレス %s の関連付けを解除します。" + +#: nova/api/ec2/cloud.py:730 +msgid "Going to start terminating instances" +msgstr "インスタンス終了処理を開始します。" + +#: nova/api/ec2/cloud.py:738 +#, python-format +msgid "Reboot instance %r" +msgstr "Reboot instance: インスタンス %r を再起動します。" + +#: nova/api/ec2/cloud.py:775 +#, python-format +msgid "De-registering image %s" +msgstr "De-registering image: イメージ %s を登録解除します。" + +#: nova/api/ec2/cloud.py:783 +#, python-format +msgid "Registered image %s with id %s" +msgstr "Registered image: イメージ %s をid %s で登録します。" + +#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 +#, python-format +msgid "attribute not supported: %s" +msgstr "アトリビュート %s はサポートされていません。" + +#: nova/api/ec2/cloud.py:794 +#, python-format +msgid "invalid id: %s" +msgstr "id %s は不正です。" + +#: nova/api/ec2/cloud.py:807 +msgid "user or group not specified" +msgstr "ユーザまたはグループが指定されていません。" + +#: nova/api/ec2/cloud.py:809 +msgid "only group \"all\" is supported" +msgstr "グループ \"all\" のみサポートされています。" + +#: nova/api/ec2/cloud.py:811 +msgid "operation_type must be add or remove" +msgstr "operation_type は add または remove の何れかである必要があります。" + +#: nova/api/ec2/cloud.py:812 +#, python-format +msgid "Updating image %s publicity" +msgstr "イメージ %s の公開設定を更新します。" + +#: nova/api/ec2/metadatarequesthandler.py:75 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "ip %s に対するメタデータの取得に失敗しました。" + +#: nova/api/openstack/__init__.py:70 +#, python-format +msgid "Caught error: %s" +msgstr "エラー %s をキャッチしました。" + +#: nova/api/openstack/__init__.py:86 +msgid "Including admin operations in API." +msgstr "管理用オペレーション(admin operation)をAPIに登録します。" + +#: nova/api/openstack/servers.py:184 +#, python-format +msgid "Compute.api::lock %s" +msgstr "例外: Compute.api::lock %s" + +#: nova/api/openstack/servers.py:199 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "例外: Compute.api::unlock %s" + +#: nova/api/openstack/servers.py:213 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "例外: Compute.api::get_lock %s" + +#: nova/api/openstack/servers.py:224 +#, python-format +msgid "Compute.api::pause %s" +msgstr "例外: Compute.api::pause %s" + +#: nova/api/openstack/servers.py:235 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "例外: Compute.api::unpause %s" + +#: nova/api/openstack/servers.py:246 +#, python-format +msgid "compute.api::suspend %s" +msgstr "例外: compute.api::suspend %s" + +#: nova/api/openstack/servers.py:257 +#, python-format +msgid "compute.api::resume %s" +msgstr "例外: compute.api::resume %s" + +#: nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "ユーザー %s は既に存在しています。" + +#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "マネージャ %s が存在しないためプロジェクトを作成できません。" + +#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "プロジェクト %s が既に存在するためプロジェクトを作成できません。" + +#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "マネージャ %s が存在しないためプロジェクトを更新できません。" + +#: nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "ユーザ \"%s\" が見つかりません。" + +#: nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "プロジェクト \"%s\" が見つかりません。" + +#: nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "シングルトンをインスタンス化しようとしました。" + +#: nova/auth/ldapdriver.py:181 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "LDAPオブジェクト %s が存在しません。" + +#: nova/auth/ldapdriver.py:218 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "ユーザ %s が存在しないためプロジェクトを作成できません。" + +#: nova/auth/ldapdriver.py:478 +#, python-format +msgid "User %s is already a member of the group %s" +msgstr "ユーザ %s は既にグループ %s のメンバーです。" + +#: nova/auth/ldapdriver.py:507 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "グループの最後のメンバーを削除しようとしました。代わりにグループ %s を削除してください。" + +#: nova/auth/ldapdriver.py:528 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "dnが %s のグループは存在しません。" + +#: nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "ユーザ %r を検索します。" + +#: nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "Failed authorization: アクセスキー %s の認証に失敗しました。" + +#: nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "アクセスキー %s に対するユーザが見つかりませんでした。" + +#: nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "ユーザ名 (%s) をプロジェクト名として使用します。" + +#: nova/auth/manager.py:275 +#, python-format +msgid "failed authorization: no project named %s (user=%s)" +msgstr "Failed authorization: 認証に失敗しました。プロジェクト名 %s (ユーザ = %s) は存在しません。" + +#: nova/auth/manager.py:277 +#, python-format +msgid "No project called %s could be found" +msgstr "プロジェクト %s は見つかりませんでした。" + +#: nova/auth/manager.py:281 +#, python-format +msgid "Failed authorization: user %s not admin and not member of project %s" +msgstr "" +"Failed authorization: 認証に失敗しました: ユーザ %s は管理者ではなくかつプロジェクト %s のメンバーではありません。" + +#: nova/auth/manager.py:283 +#, python-format +msgid "User %s is not a member of project %s" +msgstr "ユーザ %s はプロジェクト %s のメンバーではありません。" + +#: nova/auth/manager.py:292 nova/auth/manager.py:303 +#, python-format +msgid "Invalid signature for user %s" +msgstr "Invalid signature: ユーザ %s の署名が不正です。" + +#: nova/auth/manager.py:293 nova/auth/manager.py:304 +msgid "Signature does not match" +msgstr "署名が一致しません。" + +#: nova/auth/manager.py:374 +msgid "Must specify project" +msgstr "プロジェクトを指定してください。" + +#: nova/auth/manager.py:408 +#, python-format +msgid "The %s role can not be found" +msgstr "ロール %s が見つかりません。" + +#: nova/auth/manager.py:410 +#, python-format +msgid "The %s role is global only" +msgstr "ロール %s はグローバルでのみ使用可能です。" + +#: nova/auth/manager.py:412 +#, python-format +msgid "Adding role %s to user %s in project %s" +msgstr "Adding role: ロール %s をユーザ %s (プロジェクト %s の) に追加します。" + +#: nova/auth/manager.py:438 +#, python-format +msgid "Removing role %s from user %s on project %s" +msgstr "Removing role: ロール %s をユーザ %s (プロジェクト %s の)から削除します。" + +#: nova/auth/manager.py:505 +#, python-format +msgid "Created project %s with manager %s" +msgstr "Created project: プロジェクト %s (マネージャ %s)を作成します。" + +#: nova/auth/manager.py:523 +#, python-format +msgid "modifying project %s" +msgstr "modifying project: プロジェクト %s を更新します。" + +#: nova/auth/manager.py:553 +#, python-format +msgid "Remove user %s from project %s" +msgstr "Remove user: ユーザ %s をプロジェクト %s から削除します。" + +#: nova/auth/manager.py:581 +#, python-format +msgid "Deleting project %s" +msgstr "Deleting project: プロジェクト %s を削除します。" + +#: nova/auth/manager.py:637 +#, python-format +msgid "Created user %s (admin: %r)" +msgstr "Created user: ユーザ %s (admin: %r) を作成しました。" + +#: nova/auth/manager.py:645 +#, python-format +msgid "Deleting user %s" +msgstr "Deleting user: ユーザ %s を削除します。" + +#: nova/auth/manager.py:655 +#, python-format +msgid "Access Key change for user %s" +msgstr "Access Key change: ユーザ %s のアクセスキーを更新します。" + +#: nova/auth/manager.py:657 +#, python-format +msgid "Secret Key change for user %s" +msgstr "Secret Key change: ユーザ %s のシークレットキーを更新します。" + +#: nova/auth/manager.py:659 +#, python-format +msgid "Admin status set to %r for user %s" +msgstr "Admin status set: 管理者ステータス %r をユーザ %s に設定します。" + +#: nova/auth/manager.py:708 +#, python-format +msgid "No vpn data for project %s" +msgstr "プロジェクト %s に関するvpnデータがありません。" + +#: nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "cloudpipeインスタンス起動時に実行するスクリプトのテンプレート" + +#: nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "openvpnの設定に入れるネットワークの値" + +#: nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "openvpnの設定に入れるネットマスクの値" + +#: nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "%s 用のVPNを起動します。" + +#: nova/compute/api.py:67 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "get_network_topicにおいてインスタンス %d が見つかりませんでした。" + +#: nova/compute/api.py:73 +#, python-format +msgid "Instance %d has no host" +msgstr "インスタンス %d にホストが登録されていません。" + +#: nova/compute/api.py:92 +#, python-format +msgid "Quota exceeeded for %s, tried to run %s instances" +msgstr "%s のクオータ上限を超えました。%s インスタンスを実行しようとしました。" + +#: nova/compute/api.py:94 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "インスタンスのクオータを超えました。このタイプにおいてはあと %s インスタンスしか実行できません。" + +#: nova/compute/api.py:109 +msgid "Creating a raw instance" +msgstr "raw instanceを生成します。" + +#: nova/compute/api.py:156 +#, python-format +msgid "Going to run %s instances..." +msgstr "%s 個のインスタンスの起動を始めます…" + +#: nova/compute/api.py:180 +#, python-format +msgid "Casting to scheduler for %s/%s's instance %s" +msgstr "スケジューラに対して %s/%s のインスタンス %s を送信します。" + +#: nova/compute/api.py:279 +#, python-format +msgid "Going to try and terminate %s" +msgstr "%s を終了します。" + +#: nova/compute/api.py:283 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "インスタンス %d が終了処理において見つかりませんでした。" + +#: nova/compute/api.py:288 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "インスタンス %d は既に終了済みです。" + +#: nova/compute/api.py:450 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "デバイスの指定 %s が不正です: デバイス指定の例: /dev/vdb" + +#: nova/compute/api.py:465 +msgid "Volume isn't attached to anything!" +msgstr "ボリュームはどこにもアタッチされていません。" + +#: nova/compute/disk.py:71 +#, python-format +msgid "Input partition size not evenly divisible by sector size: %d / %d" +msgstr "インプットパーティションサイズがセクターサイズで割り切れません。 %d / %d" + +#: nova/compute/disk.py:75 +#, python-format +msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" +msgstr "ローカルストレージのバイト数がセクターサイズで割り切れません: %d / %d" + +#: nova/compute/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "イメージをループバック %s にアタッチできません。" + +#: nova/compute/disk.py:136 +#, python-format +msgid "Failed to load partition: %s" +msgstr "パーティション %s のロードに失敗しました。" + +#: nova/compute/disk.py:158 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "ファイルシステム %s のマウントに失敗しました。" + +#: nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "%s は未知のインスタンスタイプです。" + +#: nova/compute/manager.py:69 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "check_instance_lock: decorating: |%s|" + +#: nova/compute/manager.py:71 +#, python-format +msgid "check_instance_lock: arguments: |%s| |%s| |%s|" +msgstr "check_instance_lock: arguments: |%s| |%s| |%s|" + +#: nova/compute/manager.py:75 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "check_instance_lock: locked: |%s|" + +#: nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "check_instance_lock: admin: |%s|" + +#: nova/compute/manager.py:82 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "check_instance_lock: executing: |%s|" + +#: nova/compute/manager.py:86 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "check_instance_lock: not executing |%s|" + +#: nova/compute/manager.py:157 +msgid "Instance has already been created" +msgstr "インスタンスは既に生成されています。" + +#: nova/compute/manager.py:158 +#, python-format +msgid "instance %s: starting..." +msgstr "インスタンス %s を開始します。" + +#: nova/compute/manager.py:197 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "インスタンス %s の起動に失敗しました。" + +#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 +#, python-format +msgid "Terminating instance %s" +msgstr "Terminating instance: インスタンス %s を終了します。" + +#: nova/compute/manager.py:217 +#, python-format +msgid "Disassociating address %s" +msgstr "アドレス %s の関連付けを解除(disassociate)しています。" + +#: nova/compute/manager.py:230 +#, python-format +msgid "Deallocating address %s" +msgstr "アドレス %s の割当を解除(deallocate)します。" + +#: nova/compute/manager.py:243 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "既に消去済みのインスタンス%sを消去しようとしました。" + +#: nova/compute/manager.py:257 +#, python-format +msgid "Rebooting instance %s" +msgstr "Rebooting instance: インスタンス %s を再起動します。" + +#: nova/compute/manager.py:260 +#, python-format +msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" +msgstr "実行していないインスタンスの再起動を試みます。%s (状態: %s 期待する状態: %s)" + +#: nova/compute/manager.py:286 +#, python-format +msgid "instance %s: snapshotting" +msgstr "snapshotting: インスタンス %s のスナップショットを取得します。" + +#: nova/compute/manager.py:289 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %s (state: %s excepted: %s)" +msgstr "実行していないインスタンスのスナップショット取得を試みます。%s (状態: %s 期待する状態: %s)" + +#: nova/compute/manager.py:301 +#, python-format +msgid "instance %s: rescuing" +msgstr "Rescuing: インスタンス %s をレスキューします。" + +#: nova/compute/manager.py:316 +#, python-format +msgid "instance %s: unrescuing" +msgstr "Unrescuing: インスタンス %s をアンレスキューします。" + +#: nova/compute/manager.py:335 +#, python-format +msgid "instance %s: pausing" +msgstr "pausing: インスタンス %s を一時停止します。" + +#: nova/compute/manager.py:352 +#, python-format +msgid "instance %s: unpausing" +msgstr "unpausing: インスタンス %s の一時停止を解除します。" + +#: nova/compute/manager.py:369 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "retrieving diagnostics: インスタンス %s の診断情報を取得します。" + +#: nova/compute/manager.py:382 +#, python-format +msgid "instance %s: suspending" +msgstr "suspending: インスタンス %s をサスペンドします。" + +#: nova/compute/manager.py:401 +#, python-format +msgid "instance %s: resuming" +msgstr "resuming: インスタンス %s をレジュームします。" + +#: nova/compute/manager.py:420 +#, python-format +msgid "instance %s: locking" +msgstr "locking: インスタンス %s をロックします。" + +#: nova/compute/manager.py:432 +#, python-format +msgid "instance %s: unlocking" +msgstr "unlocking: インスタンス %s のロックを解除します。" + +#: nova/compute/manager.py:442 +#, python-format +msgid "instance %s: getting locked state" +msgstr "getting locked state: インスタンス %s のロックを取得しました。" + +#: nova/compute/manager.py:462 +#, python-format +msgid "instance %s: attaching volume %s to %s" +msgstr "attaching volume: インスタンス %s についてボリューム %s を %s にアタッチします。" + +#: nova/compute/manager.py:478 +#, python-format +msgid "instance %s: attach failed %s, removing" +msgstr "インスタンス %s: %sのアタッチに失敗しました。リムーブします。" + +#: nova/compute/manager.py:493 +#, python-format +msgid "Detach volume %s from mountpoint %s on instance %s" +msgstr "Detach volume: ボリューム %s をマウントポイント %s (インスタンス%s)からデタッチします。" + +#: nova/compute/manager.py:497 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "ボリュームを未知のインスタンス %s からデタッチします。" + +#: nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "%s の情報の更新…" + +#: nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "更新の最中に予期しないエラーが発生しました。" + +#: nova/compute/monitor.py:355 +#, python-format +msgid "Cannot get blockstats for \"%s\" on \"%s\"" +msgstr "ブロックデバイス \"%s\" の統計を \"%s\" について取得できません。" + +#: nova/compute/monitor.py:377 +#, python-format +msgid "Cannot get ifstats for \"%s\" on \"%s\"" +msgstr "インタフェース \"%s\" の統計を \"%s\" について取得できません。" + +#: nova/compute/monitor.py:412 +msgid "unexpected exception getting connection" +msgstr "接続に際し予期しないエラーが発生しました。" + +#: nova/compute/monitor.py:427 +#, python-format +msgid "Found instance: %s" +msgstr "インスタンス %s が見つかりました。" + +#: nova/db/sqlalchemy/api.py:43 +msgid "Use of empty request context is deprecated" +msgstr "Request context を空とすることは非推奨です。" + +#: nova/db/sqlalchemy/api.py:132 +#, python-format +msgid "No service for id %s" +msgstr "id %s のserviceが存在しません。" + +#: nova/db/sqlalchemy/api.py:229 +#, python-format +msgid "No service for %s, %s" +msgstr "%s, %s のserviceが存在しません。" + +#: nova/db/sqlalchemy/api.py:574 +#, python-format +msgid "No floating ip for address %s" +msgstr "アドレス %s の floating ip が存在しません。" + +#: nova/db/sqlalchemy/api.py:668 +#, python-format +msgid "No instance for id %s" +msgstr "id %s のinstanceが存在しません。" + +#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 +#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 +#, python-format +msgid "Instance %s not found" +msgstr "インスタンス %s が見つかりません。" + +#: nova/db/sqlalchemy/api.py:891 +#, python-format +msgid "no keypair for user %s, name %s" +msgstr "ユーザ %s, ネーム%s に該当するキーペアが存在しません。" + +#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 +#, python-format +msgid "No network for id %s" +msgstr "id %s に該当するnetwork が存在しません。" + +#: nova/db/sqlalchemy/api.py:1036 +#, python-format +msgid "No network for bridge %s" +msgstr "ブリッジ %s に該当する network が存在しません。" + +#: nova/db/sqlalchemy/api.py:1050 +#, python-format +msgid "No network for instance %s" +msgstr "instance %s に該当する network が存在しません。" + +#: nova/db/sqlalchemy/api.py:1180 +#, python-format +msgid "Token %s does not exist" +msgstr "トークン %s が存在しません。" + +#: nova/db/sqlalchemy/api.py:1205 +#, python-format +msgid "No quota for project_id %s" +msgstr "project_id %s に対するクオータが存在しません。" + +#: nova/db/sqlalchemy/api.py:1356 +#, python-format +msgid "No volume for id %s" +msgstr "id %s に該当するボリュームが存在しません。" + +#: nova/db/sqlalchemy/api.py:1401 +#, python-format +msgid "Volume %s not found" +msgstr "ボリューム %s が見つかりません。" + +#: nova/db/sqlalchemy/api.py:1413 +#, python-format +msgid "No export device found for volume %s" +msgstr "ボリューム %s に関してエクスポートされているデバイスがありません。" + +#: nova/db/sqlalchemy/api.py:1426 +#, python-format +msgid "No target id found for volume %s" +msgstr "ボリューム %s に対する target idが存在しません。" + +#: nova/db/sqlalchemy/api.py:1471 +#, python-format +msgid "No security group with id %s" +msgstr "id %s のセキュリティグループが存在しません。" + +#: nova/db/sqlalchemy/api.py:1488 +#, python-format +msgid "No security group named %s for project: %s" +msgstr "セキュリティグループ名 %s がプロジェクト %s に存在しません。" + +#: nova/db/sqlalchemy/api.py:1576 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "id %s のセキュリティグループルールが存在しません。" + +#: nova/db/sqlalchemy/api.py:1650 +#, python-format +msgid "No user for id %s" +msgstr "id %s のユーザが存在しません。" + +#: nova/db/sqlalchemy/api.py:1666 +#, python-format +msgid "No user for access key %s" +msgstr "アクセスキー %s に該当するユーザが存在しません。" + +#: nova/db/sqlalchemy/api.py:1728 +#, python-format +msgid "No project with id %s" +msgstr "id %s のプロジェクトが存在しません。" + +#: nova/image/glance.py:78 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images" +msgstr "Parallax がHTTPエラー%d を /images に対するリクエストに対して返しました。" + +#: nova/image/glance.py:97 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images/detail" +msgstr "Parallax がHTTPエラー %d を /images/detail に対するリクエストに対して返しました" + +#: nova/image/s3.py:82 +#, python-format +msgid "Image %s could not be found" +msgstr "イメージ %s が見つかりませんでした。" + +#: nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "アドレスを割り当てようとしましたが、%s のクオータを超えました。" + +#: nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "アドレスのクオータを超えました。これ以上アドレスを割り当てることはできません。" + +#: nova/network/linux_net.py:176 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "VLANインタフェース %s を開始します。" + +#: nova/network/linux_net.py:186 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "%s 用のブリッジインタフェースを開始します。" + +#: nova/network/linux_net.py:254 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "dnsmasqに対してhupを送信しましたが %s が発生しました。" + +#: nova/network/linux_net.py:256 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "Pid %d は無効です。dnsmasqを再実行します。" + +#: nova/network/linux_net.py:334 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "dnsmasq をkillしましたが、 %s が発生しました。" + +#: nova/network/manager.py:135 +msgid "setting network host" +msgstr "ネットワークホストの設定をします。" + +#: nova/network/manager.py:190 +#, python-format +msgid "Leasing IP %s" +msgstr "IP %s をリースします。" + +#: nova/network/manager.py:194 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "IP %s がリースされましたが関連付けられていません。" + +#: nova/network/manager.py:197 +#, python-format +msgid "IP %s leased to bad mac %s vs %s" +msgstr "IP %s が期待した mac %s ではなく %s にリースされました。" + +#: nova/network/manager.py:205 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "既に割当解除しているIP %s がリースされました。" + +#: nova/network/manager.py:214 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "割り当てていないIP %s が開放されました。" + +#: nova/network/manager.py:217 +#, python-format +msgid "IP %s released from bad mac %s vs %s" +msgstr "IP %s がmac %s ではない mac %s への割当から開放されました。" + +#: nova/network/manager.py:220 +#, python-format +msgid "IP %s released that was not leased" +msgstr "リースしていないIP %s が開放されました。" + +#: nova/network/manager.py:442 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "無効になった %s 個の fixed ip を割当解除しました。" + +#: nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "未知のS3 value type %r です。" + +#: nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "認証リクエスト" + +#: nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "List of buckets が呼ばれました。" + +#: nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "バケット %s のキーの一覧" + +#: nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "Unauthorized attempt to access bucket: バケット %s に対するアクセスは許可されていません。" + +#: nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "バケットを作成します。 %s" + +#: nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "バケットを削除します。 %s" + +#: nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "Unauthorized attempt to delete bucket: バケット %s に対する削除は許可されていません。" + +#: nova/objectstore/handler.py:271 +#, python-format +msgid "Getting object: %s / %s" +msgstr "オブジェクトの取得: %s / %s" + +#: nova/objectstore/handler.py:274 +#, python-format +msgid "Unauthorized attempt to get object %s from bucket %s" +msgstr "" +"Unauthorized attempt to get object: オブジェクト %s のバケット %s からの取得は許可されていません。" + +#: nova/objectstore/handler.py:292 +#, python-format +msgid "Putting object: %s / %s" +msgstr "オブジェクトの格納:: %s / %s" + +#: nova/objectstore/handler.py:295 +#, python-format +msgid "Unauthorized attempt to upload object %s to bucket %s" +msgstr "" +"Unauthorized attempt to upload: オブジェクト %s のバケット %s へのアップロードは許可されていません。" + +#: nova/objectstore/handler.py:314 +#, python-format +msgid "Deleting object: %s / %s" +msgstr "オブジェクトを削除しています。: %s / %s" + +#: nova/objectstore/handler.py:393 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "" +"Not authorized to upload image: イメージの格納は許可されていません。ディレクトリ %s は正しくありません。" + +#: nova/objectstore/handler.py:401 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "" +"Not authorized to upload image: イメージの格納は許可されていません。バケット %s への格納は許可されていません。" + +#: nova/objectstore/handler.py:406 +#, python-format +msgid "Starting image upload: %s" +msgstr "イメージのアップロードを開始しました。 %s" + +#: nova/objectstore/handler.py:420 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "Not authorized to update attributes: イメージ %s のアトリビュートの更新は許可されていません。" + +#: nova/objectstore/handler.py:428 +#, python-format +msgid "Toggling publicity flag of image %s %r" +msgstr "Toggling publicity flag: イメージ %s の公開フラグを %r に更新します。" + +#: nova/objectstore/handler.py:433 +#, python-format +msgid "Updating user fields on image %s" +msgstr "Updating user fields: イメージ %s のユーザフィールドを更新します。" + +#: nova/objectstore/handler.py:447 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "Unauthorized attempt to delete image: イメージ %s の削除は許可されていません。" + +#: nova/objectstore/handler.py:452 +#, python-format +msgid "Deleted image: %s" +msgstr "イメージ %s を削除しました。" + +#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 +#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 +msgid "No hosts found" +msgstr "適切なホストが見つかりません。" + +#: nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "予備の(fallback)スケジューラを実装する必要があります。" + +#: nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %s %s for %s" +msgstr "メッセージのcast: %s %s for %s" + +#: nova/scheduler/simple.py:63 +msgid "All hosts have too many cores" +msgstr "全てのホストにコア数の空きがありません。" + +#: nova/scheduler/simple.py:95 +msgid "All hosts have too many gigabytes" +msgstr "全てのホストが利用可能な容量(gigabytes)に達しています。" + +#: nova/scheduler/simple.py:115 +msgid "All hosts have too many networks" +msgstr "全てのホストがネットワークの最大数に達しています。" + +#: nova/tests/test_cloud.py:198 +msgid "Can't test instances without a real virtual env." +msgstr "インスタンスのテストには実際の仮想環境が必要です。(fakeでは実行できません。)" + +#: nova/tests/test_cloud.py:210 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "インスタンス %s が実行するまで監視します…" + +#: nova/tests/test_compute.py:104 +#, python-format +msgid "Running instances: %s" +msgstr "インスタンス %s は実行中です。" + +#: nova/tests/test_compute.py:110 +#, python-format +msgid "After terminating instances: %s" +msgstr "インスタンス %s を終了した後です。" + +#: nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %s, %s" +msgstr "ネスとした受信: %s, %s" + +#: nova/tests/test_rpc.py:94 +#, python-format +msgid "Nested return %s" +msgstr "ネストした戻り値: %s" + +#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 +#, python-format +msgid "Received %s" +msgstr "%s を受信。" + +#: nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "ターゲット %s をアロケートしました。" + +#: nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "ハイパーバイザへの接続に失敗しました。" + +#: nova/virt/fake.py:210 +#, python-format +msgid "Instance %s Not Found" +msgstr "インスタンス %s が見つかりません。" + +#: nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "In init host" + +#: nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "VM %s を二重に作成しようとしました。" + +#: nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "VM %s を開始します。 " + +#: nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "VM %s を開始しました。 " + +#: nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "vmの生成(spawn)に失敗しました: %s" + +#: nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "VM %s の作成に失敗しました。" + +#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 +#, python-format +msgid "Created VM %s..." +msgstr "VM %s を作成します。" + +#: nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "vm %s のメモリを設定します。" + +#: nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "vm %s のvcpus を設定します。" + +#: nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %s by attaching disk file %s" +msgstr "%s のディスクをディスクファイル %s をアタッチして作成します。" + +#: nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "VM %s へのディスクドライブの追加に失敗しました。" + +#: nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "新しいドライブパスは %s です。" + +#: nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "vhdファイルの VM %s への追加に失敗しました。" + +#: nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "%s に diskを作成します。" + +#: nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "%s にNICを作成します。 " + +#: nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "外部vswitchへのポート作成に失敗しました。" + +#: nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "ポート %s の作成に失敗しました。" + +#: nova/virt/hyperv.py:275 +#, python-format +msgid "Created switch port %s on switch %s" +msgstr "スイッチポート %s をスイッチ %s に作成しました。" + +#: nova/virt/hyperv.py:285 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "VM %s に対してNICの追加に失敗しました。" + +#: nova/virt/hyperv.py:287 +#, python-format +msgid "Created nic for %s " +msgstr "%s のNICを作成しました。 " + +#: nova/virt/hyperv.py:320 +#, python-format +msgid "WMI job failed: %s" +msgstr "WMIジョブに失敗しました: %s" + +#: nova/virt/hyperv.py:322 +#, python-format +msgid "WMI job succeeded: %s, Elapsed=%s " +msgstr "WMIジョブが成功しました: %s, 経過時間=%s " + +#: nova/virt/hyperv.py:358 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "destroy vm %s リクエストを受信しました。" + +#: nova/virt/hyperv.py:383 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "vm %s の削除に失敗しました。" + +#: nova/virt/hyperv.py:389 +#, python-format +msgid "Del: disk %s vm %s" +msgstr "Del: 削除: disk %s vm %s" + +#: nova/virt/hyperv.py:405 +#, python-format +msgid "" +"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " +"cpu_time=%s" +msgstr "" +"vm %s の情報の取得: state=%s, mem=%s, num_cpu=%s, cpu_time=%s" + +#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 +#, python-format +msgid "duplicate name found: %s" +msgstr "%s は重複しています。" + +#: nova/virt/hyperv.py:444 +#, python-format +msgid "Successfully changed vm state of %s to %s" +msgstr "vmの状態の %s から %s への変更に成功しました。" + +#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 +#, python-format +msgid "Failed to change vm state of %s to %s" +msgstr "VMの状態の %s から %s への変更に失敗しました。" + +#: nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %s -- placed in %s" +msgstr "%s を取得しました。格納先: %s" + +#: nova/virt/libvirt_conn.py:144 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "libvirt %s へ接続します。" + +#: nova/virt/libvirt_conn.py:157 +msgid "Connection to libvirt broke" +msgstr "libvirtへの接続が切れています。" + +#: nova/virt/libvirt_conn.py:229 +#, python-format +msgid "instance %s: deleting instance files %s" +msgstr "インスタンス %s: インスタンスファイル %s を削除しています。" + +#: nova/virt/libvirt_conn.py:271 +#, python-format +msgid "No disk at %s" +msgstr "%s にディスクが存在しません。" + +#: nova/virt/libvirt_conn.py:278 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "インスタンスのスナップショットは現在libvirtに対してはサポートされていません。" + +#: nova/virt/libvirt_conn.py:294 +#, python-format +msgid "instance %s: rebooted" +msgstr "インスタンス%s: 再起動しました。" + +#: nova/virt/libvirt_conn.py:297 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "_wait_for_reboot 失敗: %s" + +#: nova/virt/libvirt_conn.py:340 +#, python-format +msgid "instance %s: rescued" +msgstr "インスタンス %s: rescued" + +#: nova/virt/libvirt_conn.py:343 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "_wait_for_rescue 失敗: %s" + +#: nova/virt/libvirt_conn.py:370 +#, python-format +msgid "instance %s: is running" +msgstr "インスタンス %s を起動中です。" + +#: nova/virt/libvirt_conn.py:381 +#, python-format +msgid "instance %s: booted" +msgstr "インスタンス %s: 起動しました。" + +#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 +#, python-format +msgid "instance %s: failed to boot" +msgstr "インスタンス %s の起動に失敗しました。" + +#: nova/virt/libvirt_conn.py:395 +#, python-format +msgid "virsh said: %r" +msgstr "virsh の出力: %r" + +#: nova/virt/libvirt_conn.py:399 +msgid "cool, it's a device" +msgstr "デバイスです。" + +#: nova/virt/libvirt_conn.py:407 +#, python-format +msgid "data: %r, fpath: %r" +msgstr "データ:%r ファイルパス: %r" + +#: nova/virt/libvirt_conn.py:415 +#, python-format +msgid "Contents of file %s: %r" +msgstr "ファイル %s の中身: %r" + +#: nova/virt/libvirt_conn.py:449 +#, python-format +msgid "instance %s: Creating image" +msgstr "インスタンス %s のイメージを生成します。" + +#: nova/virt/libvirt_conn.py:505 +#, python-format +msgid "instance %s: injecting key into image %s" +msgstr "インスタンス %s にキー %s をインジェクトします。" + +#: nova/virt/libvirt_conn.py:508 +#, python-format +msgid "instance %s: injecting net into image %s" +msgstr "インスタンス %s のネットワーク設定をイメージ %s にインジェクトします。" + +#: nova/virt/libvirt_conn.py:516 +#, python-format +msgid "instance %s: ignoring error injecting data into image %s (%s)" +msgstr "インスタンス %s: データをイメージ %s にインジェクトする際にエラーが発生しました。(%s)" + +#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "インスタンス %s: toXML メソッドを開始。" + +#: nova/virt/libvirt_conn.py:589 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "インスタンス %s: toXML メソッドを完了。" + +#: nova/virt/xenapi_conn.py:113 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" +"connection_type=xenapi を使用するには、以下の指定が必要です: xenapi_connection_url, " +"xenapi_connection_username (オプション), xenapi_connection_password" + +#: nova/virt/xenapi_conn.py:263 +#, python-format +msgid "Task [%s] %s status: success %s" +msgstr "タスク [%s] %s ステータス: success %s" + +#: nova/virt/xenapi_conn.py:271 +#, python-format +msgid "Task [%s] %s status: %s %s" +msgstr "タスク [%s] %s ステータス: %s %s" + +#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 +#, python-format +msgid "Got exception: %s" +msgstr "例外 %s が発生しました。" + +#: nova/virt/xenapi/fake.py:72 +#, python-format +msgid "%s: _db_content => %s" +msgstr "%s: _db_content => %s" + +#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 +#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 +msgid "Raising NotImplemented" +msgstr "NotImplemented 例外を発生させます。" + +#: nova/virt/xenapi/fake.py:249 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "xenapi.fake には %s が実装されていません。" + +#: nova/virt/xenapi/fake.py:283 +#, python-format +msgid "Calling %s %s" +msgstr "呼び出し: %s %s" + +#: nova/virt/xenapi/fake.py:288 +#, python-format +msgid "Calling getter %s" +msgstr "getter %s をコールします。" + +#: nova/virt/xenapi/fake.py:340 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "xenapi.fake に %s に関する実装がないか、引数の数が誤っています。" + +#: nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "ブリッジ %s に対してブリッジが複数存在します。" + +#: nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "ブリッジ %s に対するネットワークが存在しません。" + +#: nova/virt/xenapi/vm_utils.py:127 +#, python-format +msgid "Created VM %s as %s." +msgstr "VM %s を %s として作成しました。" + +#: nova/virt/xenapi/vm_utils.py:147 +#, python-format +msgid "Creating VBD for VM %s, VDI %s ... " +msgstr "VM %s, VDI %s のVBDを作成します… " + +#: nova/virt/xenapi/vm_utils.py:149 +#, python-format +msgid "Created VBD %s for VM %s, VDI %s." +msgstr "VBD %s を VM %s, VDI %s に対して作成しました。" + +#: nova/virt/xenapi/vm_utils.py:165 +#, python-format +msgid "VBD not found in instance %s" +msgstr "インスタンス %s のVBDが見つかりません。" + +#: nova/virt/xenapi/vm_utils.py:175 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "VBD %s の unplug に失敗しました。" + +#: nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "VBD %s の削除に失敗しました。" + +#: nova/virt/xenapi/vm_utils.py:202 +#, python-format +msgid "Creating VIF for VM %s, network %s." +msgstr "VM %s, ネットワーク %s を作成します。" + +#: nova/virt/xenapi/vm_utils.py:205 +#, python-format +msgid "Created VIF %s for VM %s, network %s." +msgstr "VIF %s を VM %s, ネットワーク %s に作成しました。" + +#: nova/virt/xenapi/vm_utils.py:216 +#, python-format +msgid "Snapshotting VM %s with label '%s'..." +msgstr "VM %s のスナップショットをラベル '%s' で作成します。" + +#: nova/virt/xenapi/vm_utils.py:229 +#, python-format +msgid "Created snapshot %s from VM %s." +msgstr "スナップショット %s を VM %s について作成しました。" + +#: nova/virt/xenapi/vm_utils.py:243 +#, python-format +msgid "Asking xapi to upload %s as '%s'" +msgstr "xapiに対して %s を '%s' としてアップロードするように指示します。" + +#: nova/virt/xenapi/vm_utils.py:261 +#, python-format +msgid "Asking xapi to fetch %s as %s" +msgstr "xapi に対して %s を %s として取得するように指示します。" + +#: nova/virt/xenapi/vm_utils.py:279 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "PV kernelのvdi %s を取得します。" + +#: nova/virt/xenapi/vm_utils.py:290 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "VDIのPV Kernel: %d" + +#: nova/virt/xenapi/vm_utils.py:318 +#, python-format +msgid "VDI %s is still available" +msgstr "VDI %s は依然として存在しています。" + +#: nova/virt/xenapi/vm_utils.py:331 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "(VM_UTILS) xenserver の vm state -> |%s|" + +#: nova/virt/xenapi/vm_utils.py:333 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "(VM_UTILS) xenapi の power_state -> |%s|" + +#: nova/virt/xenapi/vm_utils.py:390 +#, python-format +msgid "VHD %s has parent %s" +msgstr "VHD %s のペアレントは %s です。" + +#: nova/virt/xenapi/vm_utils.py:407 +#, python-format +msgid "Re-scanning SR %s" +msgstr "SR %s を再スキャンします。" + +#: nova/virt/xenapi/vm_utils.py:431 +#, python-format +msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." +msgstr "ペアレント %s がオリジナルのペアレント %s と一致しません。合致するのを待ちます…" + +#: nova/virt/xenapi/vm_utils.py:448 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "VM %s にVDIが存在しません。" + +#: nova/virt/xenapi/vm_utils.py:452 +#, python-format +msgid "Unexpected number of VDIs (%s) found for VM %s" +msgstr "予期しない数 (%s) のVDIがVM %s に存在します。" + +#: nova/virt/xenapi/vmops.py:62 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "ユニークではないname %s を作成しようとしました。" + +#: nova/virt/xenapi/vmops.py:99 +#, python-format +msgid "Starting VM %s..." +msgstr "VM %s を開始します…" + +#: nova/virt/xenapi/vmops.py:101 +#, python-format +msgid "Spawning VM %s created %s." +msgstr "VM %s の生成(spawning) により %s を作成しました。" + +#: nova/virt/xenapi/vmops.py:112 +#, python-format +msgid "Instance %s: booted" +msgstr "インスタンス%s: ブートしました。" + +#: nova/virt/xenapi/vmops.py:137 +#, python-format +msgid "Instance not present %s" +msgstr "インスタンス%s が存在しません。" + +#: nova/virt/xenapi/vmops.py:166 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "VM %s に対するスナップショットを開始します。" + +#: nova/virt/xenapi/vmops.py:174 +#, python-format +msgid "Unable to Snapshot %s: %s" +msgstr "%s のスナップショットに失敗しました: %s" + +#: nova/virt/xenapi/vmops.py:184 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "VM %s のスナップショットとアップロードが完了しました。" + +#: nova/virt/xenapi/vmops.py:252 +#, python-format +msgid "suspend: instance not present %s" +msgstr "suspend: インスタンス %s は存在しません。" + +#: nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "resume: instance not present %s" +msgstr "resume: インスタンス %s は存在しません。" + +#: nova/virt/xenapi/vmops.py:271 +#, python-format +msgid "Instance not found %s" +msgstr "インスタンス %s が見つかりません。" + +#: nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "%s を introduce します…" + +#: nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %s as %s." +msgstr "%s を %s として introduce しました。" + +#: nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "Storage Repository を作成できません。" + +#: nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "VBD %s から SRを取得できません。" + +#: nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "SR %s をforgetします。 " + +#: nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %s when getting PBDs for %s" +msgstr "例外 %s が %s のPBDを取得する際に発生しましたが無視します。" + +#: nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %s when unplugging PBD %s" +msgstr "例外 %s が %s のPBDをunplugする際に発生しましたが無視します。" + +#: nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "SR %s のforgetが完了。" + +#: nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %s when forgetting SR %s" +msgstr "例外 %s がSR %s をforgetする際に発生しましたが無視します。" + +#: nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "SR %s のVDIのintroduceができません。" + +#: nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "VDI %s のレコードを取得できません。" + +#: nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "SR %s のVDIをintroduceできません。" + +#: nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %s, %s" +msgstr "ターゲットの情報を取得できません。 %s, %s" + +#: nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "マウントポイントを変換できません。 %s" + +#: nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %s, %s, %s" +msgstr "Attach_volume: ボリュームのアタッチ: %s, %s, %s" + +#: nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %s for instance %s" +msgstr "SR %s にインスタンス %s のVDIを作成できません。" + +#: nova/virt/xenapi/volumeops.py:81 +#, python-format +msgid "Unable to use SR %s for instance %s" +msgstr "SR %s をインスタンス %s に対して利用できません。" + +#: nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "インスタンス %s にボリュームをアタッチできません。" + +#: nova/virt/xenapi/volumeops.py:95 +#, python-format +msgid "Mountpoint %s attached to instance %s" +msgstr "マウントポイント %s をインスタンス %s にアタッチしました。" + +#: nova/virt/xenapi/volumeops.py:106 +#, python-format +msgid "Detach_volume: %s, %s" +msgstr "Detach_volume: ボリュームのデタッチ: %s, %s" + +#: nova/virt/xenapi/volumeops.py:113 +#, python-format +msgid "Unable to locate volume %s" +msgstr "ボリューム %s の存在が確認できません。" + +#: nova/virt/xenapi/volumeops.py:121 +#, python-format +msgid "Unable to detach volume %s" +msgstr "ボリューム %s のデタッチができません。" + +#: nova/virt/xenapi/volumeops.py:128 +#, python-format +msgid "Mountpoint %s detached from instance %s" +msgstr "マウントポイント %s をインスタンス %s からデタッチしました。" + +#: nova/volume/api.py:44 +#, python-format +msgid "Quota exceeeded for %s, tried to create %sG volume" +msgstr "%sのクオータを超えています。サイズ %sG のボリュームの作成を行おうとしました。" + +#: nova/volume/api.py:46 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %s" +msgstr "ボリュームのクオータを超えています。%sの大きさのボリュームは作成できません。" + +#: nova/volume/api.py:70 nova/volume/api.py:95 +msgid "Volume status must be available" +msgstr "ボリュームのステータス(status)が available でなければなりません。" + +#: nova/volume/api.py:97 +msgid "Volume is already attached" +msgstr "ボリュームは既にアタッチされています(attached)。" + +#: nova/volume/api.py:103 +msgid "Volume is already detached" +msgstr "ボリュームは既にデタッチされています(detached)。" + +#: nova/volume/driver.py:76 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "実行失敗からリカバリーします。%s 回目のトライ。" + +#: nova/volume/driver.py:85 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "ボリュームグループ%sが存在しません。" + +#: nova/volume/driver.py:210 +#, python-format +msgid "FAKE AOE: %s" +msgstr "偽のAOE: %s" + +#: nova/volume/driver.py:315 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "偽のISCSI: %s" + +#: nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "%s 個のボリュームを再エクスポートします。" + +#: nova/volume/manager.py:93 +#, python-format +msgid "volume %s: creating" +msgstr "ボリューム%sを作成します。" + +#: nova/volume/manager.py:102 +#, python-format +msgid "volume %s: creating lv of size %sG" +msgstr "ボリューム%sの%sGのlv (論理ボリューム) を作成します。" + +#: nova/volume/manager.py:106 +#, python-format +msgid "volume %s: creating export" +msgstr "ボリューム %s をエクスポートします。" + +#: nova/volume/manager.py:113 +#, python-format +msgid "volume %s: created successfully" +msgstr "ボリューム %s の作成に成功しました。" + +#: nova/volume/manager.py:121 +msgid "Volume is still attached" +msgstr "ボリュームはアタッチされたままです。" + +#: nova/volume/manager.py:123 +msgid "Volume is not local to this node" +msgstr "ボリュームはこのノードのローカルではありません。" + +#: nova/volume/manager.py:124 +#, python-format +msgid "volume %s: removing export" +msgstr "ボリューム %s のエクスポートを解除します。" + +#: nova/volume/manager.py:126 +#, python-format +msgid "volume %s: deleting" +msgstr "ボリューム %s を削除します。" + +#: nova/volume/manager.py:129 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "ボリューム %s の削除に成功しました。" diff --git a/locale/pt_BR.po b/locale/pt_BR.po new file mode 100644 index 000000000..a58ccc182 --- /dev/null +++ b/locale/pt_BR.po @@ -0,0 +1,2148 @@ +# Brazilian Portuguese translation for nova +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the nova package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: nova\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-10 11:25-0800\n" +"PO-Revision-Date: 2011-01-13 18:44+0000\n" +"Last-Translator: Gustavo Morozowski \n" +"Language-Team: Brazilian Portuguese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-01-28 05:21+0000\n" +"X-Generator: Launchpad (build 12177)\n" + +#: nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "Nome do arquivo da CA raiz" + +#: nova/crypto.py:49 +msgid "Filename of private key" +msgstr "Nome do arquivo da chave privada" + +#: nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "Nome de arquivo da Lista de Revogação de Certificados" + +#: nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "Aonde armazenamos nossas chaves" + +#: nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "Aonde mantemos nosso CA raiz" + +#: nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "Devemos usar um CA para cada projeto?" + +#: nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "" +"Sujeito do certificado para usuários, %s para projeto, usuário, timestamp" + +#: nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "Sujeito do certificado para projetos, %s para projeto, timestamp" + +#: nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "Sujeito do certificado para vpns, %s para projeto, timestamp" + +#: nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "" + +#: nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "Erro inesperado ao executar o comando." + +#: nova/exception.py:36 +#, python-format +msgid "" +"%s\n" +"Command: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" +msgstr "" +"%s\n" +"Comando: %s\n" +"Código de retorno: %s\n" +"Stdout: %r\n" +"Stderr: %r" + +#: nova/exception.py:86 +msgid "Uncaught exception" +msgstr "Exceção não capturada" + +#: nova/fakerabbit.py:48 +#, python-format +msgid "(%s) publish (key: %s) %s" +msgstr "(%s) publicar (key: %s) %s" + +#: nova/fakerabbit.py:53 +#, python-format +msgid "Publishing to route %s" +msgstr "Publicando para rota %s" + +#: nova/fakerabbit.py:83 +#, python-format +msgid "Declaring queue %s" +msgstr "Declarando fila %s" + +#: nova/fakerabbit.py:89 +#, python-format +msgid "Declaring exchange %s" +msgstr "" + +#: nova/fakerabbit.py:95 +#, python-format +msgid "Binding %s to %s with key %s" +msgstr "Atribuindo %s para %s com chave %s" + +#: nova/fakerabbit.py:120 +#, python-format +msgid "Getting from %s: %s" +msgstr "Obtendo de %s: %s" + +#: nova/rpc.py:92 +#, python-format +msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." +msgstr "" +"Servidor AMQP em %s:%d inatingível. Tentando novamente em %d segundos." + +#: nova/rpc.py:99 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "" +"Não foi possível conectar ao servidor AMQP após %d tentativas. Desligando." + +#: nova/rpc.py:118 +msgid "Reconnected to queue" +msgstr "Reconectado à fila" + +#: nova/rpc.py:125 +msgid "Failed to fetch message from queue" +msgstr "Falha ao obter mensagem da fila" + +#: nova/rpc.py:155 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "Iniciando o Adaptador Consumidor para %s" + +#: nova/rpc.py:170 +#, python-format +msgid "received %s" +msgstr "recebido %s" + +#: nova/rpc.py:183 +#, python-format +msgid "no method for message: %s" +msgstr "sem método para mensagem: %s" + +#: nova/rpc.py:184 +#, python-format +msgid "No method for message: %s" +msgstr "Sem método para mensagem: %s" + +#: nova/rpc.py:245 +#, python-format +msgid "Returning exception %s to caller" +msgstr "Retornando exceção %s ao método de origem" + +#: nova/rpc.py:286 +#, python-format +msgid "unpacked context: %s" +msgstr "conteúdo descompactado: %s" + +#: nova/rpc.py:305 +msgid "Making asynchronous call..." +msgstr "Fazendo chamada assíncrona..." + +#: nova/rpc.py:308 +#, python-format +msgid "MSG_ID is %s" +msgstr "MSG_ID é %s" + +#: nova/rpc.py:356 +#, python-format +msgid "response %s" +msgstr "resposta %s" + +#: nova/rpc.py:365 +#, python-format +msgid "topic is %s" +msgstr "topico é %s" + +#: nova/rpc.py:366 +#, python-format +msgid "message %s" +msgstr "mensagem %s" + +#: nova/service.py:157 +#, python-format +msgid "Starting %s node" +msgstr "Iniciando nó %s" + +#: nova/service.py:169 +msgid "Service killed that has no database entry" +msgstr "Encerrado serviço que não tem entrada na base de dados" + +#: nova/service.py:190 +msgid "The service database object disappeared, Recreating it." +msgstr "O objeto da base de dados do serviço desapareceu, Recriando." + +#: nova/service.py:202 +msgid "Recovered model server connection!" +msgstr "Recuperada conexão servidor de modelo." + +#: nova/service.py:208 +msgid "model server went away" +msgstr "servidor de modelo perdido" + +#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 +#, python-format +msgid "Data store %s is unreachable. Trying again in %d seconds." +msgstr "" +"Repositório de dados %s não pode ser atingido. Tentando novamente em %d " +"segundos." + +#: nova/service.py:232 nova/twistd.py:232 +#, python-format +msgid "Serving %s" +msgstr "Servindo %s" + +#: nova/service.py:234 nova/twistd.py:264 +msgid "Full set of FLAGS:" +msgstr "Conjunto completo de FLAGS:" + +#: nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "" +"Arquivo de id de processo (pidfile) %s não existe. Daemon não está " +"executando?\n" + +#: nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "Iniciando %s" + +#: nova/utils.py:53 +#, python-format +msgid "Inner Exception: %s" +msgstr "Exceção interna: %s" + +#: nova/utils.py:54 +#, python-format +msgid "Class %s cannot be found" +msgstr "Classe %s não pode ser encontrada" + +#: nova/utils.py:113 +#, python-format +msgid "Fetching %s" +msgstr "Obtendo %s" + +#: nova/utils.py:125 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "Executando comando (subprocesso): %s" + +#: nova/utils.py:138 +#, python-format +msgid "Result was %s" +msgstr "Resultado foi %s" + +#: nova/utils.py:171 +#, python-format +msgid "debug in callback: %s" +msgstr "debug em callback: %s" + +#: nova/utils.py:176 +#, python-format +msgid "Running %s" +msgstr "Executando %s" + +#: nova/utils.py:207 +#, python-format +msgid "Couldn't get IP, using 127.0.0.1 %s" +msgstr "Não foi possível obter IP, usando 127.0.0.1 %s" + +#: nova/utils.py:289 +#, python-format +msgid "Invalid backend: %s" +msgstr "Backend inválido: %s" + +#: nova/utils.py:300 +#, python-format +msgid "backend %s" +msgstr "backend %s" + +#: nova/api/ec2/__init__.py:133 +msgid "Too many failed authentications." +msgstr "Muitas falhas de autenticação." + +#: nova/api/ec2/__init__.py:142 +#, python-format +msgid "" +"Access key %s has had %d failed authentications and will be locked out for " +"%d minutes." +msgstr "" +"Chave de acesso %s tem %d falhas de autenticação e vai ser bloqueada por %d " +"minutos." + +#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "Falha de Autenticação: %s" + +#: nova/api/ec2/__init__.py:190 +#, python-format +msgid "Authenticated Request For %s:%s)" +msgstr "Pedido de Autenticação Para: %s:%s" + +#: nova/api/ec2/__init__.py:227 +#, python-format +msgid "action: %s" +msgstr "ação: %s" + +#: nova/api/ec2/__init__.py:229 +#, python-format +msgid "arg: %s\t\tval: %s" +msgstr "argumento: %s\t\tvalor: %s" + +#: nova/api/ec2/__init__.py:301 +#, python-format +msgid "Unauthorized request for controller=%s and action=%s" +msgstr "Requisição não autorizada para controlador=%s e ação=%s" + +#: nova/api/ec2/__init__.py:339 +#, python-format +msgid "NotFound raised: %s" +msgstr "NotFound lançado: %s" + +#: nova/api/ec2/__init__.py:342 +#, python-format +msgid "ApiError raised: %s" +msgstr "ApiError lançado: %s" + +#: nova/api/ec2/__init__.py:349 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "Erro inexperado lançado: %s" + +#: nova/api/ec2/__init__.py:354 +msgid "An unknown error has occurred. Please try your request again." +msgstr "" +"Ocorreu um erro desconhecido. Por favor tente sua requisição novamente." + +#: nova/api/ec2/admin.py:84 +#, python-format +msgid "Creating new user: %s" +msgstr "Criando novo usuário: %s" + +#: nova/api/ec2/admin.py:92 +#, python-format +msgid "Deleting user: %s" +msgstr "Excluindo usuário: %s" + +#: nova/api/ec2/admin.py:114 +#, python-format +msgid "Adding role %s to user %s for project %s" +msgstr "Adicionando papel %s ao usuário %s para o projeto %s" + +#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 +#, python-format +msgid "Adding sitewide role %s to user %s" +msgstr "Adicionando papel em todo site %s ao usuário %s" + +#: nova/api/ec2/admin.py:122 +#, python-format +msgid "Removing role %s from user %s for project %s" +msgstr "Removendo papel %s do usuário %s para o projeto %s" + +#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 +#, python-format +msgid "Removing sitewide role %s from user %s" +msgstr "Removendo papel %s em todo site do usuário %s" + +#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 +msgid "operation must be add or remove" +msgstr "operações devem ser adicionar e excluir" + +#: nova/api/ec2/admin.py:142 +#, python-format +msgid "Getting x509 for user: %s on project: %s" +msgstr "Obtendo x509 para usuário: %s do projeto: %s" + +#: nova/api/ec2/admin.py:159 +#, python-format +msgid "Create project %s managed by %s" +msgstr "Criar projeto %s gerenciado por %s" + +#: nova/api/ec2/admin.py:170 +#, python-format +msgid "Delete project: %s" +msgstr "Excluir projeto: %s" + +#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 +#, python-format +msgid "Adding user %s to project %s" +msgstr "Adicionando usuário %s ao projeto %s" + +#: nova/api/ec2/admin.py:188 +#, python-format +msgid "Removing user %s from project %s" +msgstr "Excluindo usuário %s do projeto %s" + +#: nova/api/ec2/apirequest.py:95 +#, python-format +msgid "Unsupported API request: controller = %s,action = %s" +msgstr "Requisição de API não suportada: controlador = %s,ação = %s" + +#: nova/api/ec2/cloud.py:117 +#, python-format +msgid "Generating root CA: %s" +msgstr "Gerando CA raiz: %s" + +#: nova/api/ec2/cloud.py:277 +#, python-format +msgid "Create key pair %s" +msgstr "Criar par de chaves %s" + +#: nova/api/ec2/cloud.py:285 +#, python-format +msgid "Delete key pair %s" +msgstr "Remover par de chaves %s" + +#: nova/api/ec2/cloud.py:357 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "%s não é um ipProtocol válido" + +#: nova/api/ec2/cloud.py:361 +msgid "Invalid port range" +msgstr "" + +#: nova/api/ec2/cloud.py:392 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "Revogado entrada do grupo de segurança %s" + +#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 +msgid "No rule for the specified parameters." +msgstr "Não existe regra para os parâmetros especificados" + +#: nova/api/ec2/cloud.py:421 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "Autorizada entrada do grupo de segurança %s" + +#: nova/api/ec2/cloud.py:432 +#, python-format +msgid "This rule already exists in group %s" +msgstr "Esta regra já existe no grupo %s" + +#: nova/api/ec2/cloud.py:460 +#, python-format +msgid "Create Security Group %s" +msgstr "Criar Grupo de Segurança %s" + +#: nova/api/ec2/cloud.py:463 +#, python-format +msgid "group %s already exists" +msgstr "group %s já existe" + +#: nova/api/ec2/cloud.py:475 +#, python-format +msgid "Delete security group %s" +msgstr "Excluir grupo de segurança %s" + +#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 +#, python-format +msgid "Get console output for instance %s" +msgstr "Obter saída do console para instância %s" + +#: nova/api/ec2/cloud.py:543 +#, python-format +msgid "Create volume of %s GB" +msgstr "Criar volume de %s GB" + +#: nova/api/ec2/cloud.py:567 +#, python-format +msgid "Attach volume %s to instacne %s at %s" +msgstr "Anexar volume %s para instância %s em %s" + +#: nova/api/ec2/cloud.py:579 +#, python-format +msgid "Detach volume %s" +msgstr "Desanexar volume %s" + +#: nova/api/ec2/cloud.py:686 +msgid "Allocate address" +msgstr "Alocar endereço" + +#: nova/api/ec2/cloud.py:691 +#, python-format +msgid "Release address %s" +msgstr "Liberar endereço %s" + +#: nova/api/ec2/cloud.py:696 +#, python-format +msgid "Associate address %s to instance %s" +msgstr "Atribuir endereço %s à instância %s" + +#: nova/api/ec2/cloud.py:703 +#, python-format +msgid "Disassociate address %s" +msgstr "Desatribuir endereço %s" + +#: nova/api/ec2/cloud.py:730 +msgid "Going to start terminating instances" +msgstr "Começando a terminar instâncias" + +#: nova/api/ec2/cloud.py:738 +#, python-format +msgid "Reboot instance %r" +msgstr "Reiniciar instância %r" + +#: nova/api/ec2/cloud.py:775 +#, python-format +msgid "De-registering image %s" +msgstr "Removendo o registro da imagem %s" + +#: nova/api/ec2/cloud.py:783 +#, python-format +msgid "Registered image %s with id %s" +msgstr "Registrada imagem %s com id %s" + +#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 +#, python-format +msgid "attribute not supported: %s" +msgstr "atributo não suportado: %s" + +#: nova/api/ec2/cloud.py:794 +#, python-format +msgid "invalid id: %s" +msgstr "id inválido: %s" + +#: nova/api/ec2/cloud.py:807 +msgid "user or group not specified" +msgstr "usuário ou grupo não especificado" + +#: nova/api/ec2/cloud.py:809 +msgid "only group \"all\" is supported" +msgstr "apenas o grupo \"all\" é suportado" + +#: nova/api/ec2/cloud.py:811 +msgid "operation_type must be add or remove" +msgstr "operation_type deve ser add ou remove" + +#: nova/api/ec2/cloud.py:812 +#, python-format +msgid "Updating image %s publicity" +msgstr "Atualizando publicidade da imagem %s" + +#: nova/api/ec2/metadatarequesthandler.py:75 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "Falha ao obter metadados para o ip: %s" + +#: nova/api/openstack/__init__.py:70 +#, python-format +msgid "Caught error: %s" +msgstr "Capturado o erro: %s" + +#: nova/api/openstack/__init__.py:86 +msgid "Including admin operations in API." +msgstr "Incluindo operações administrativas na API." + +#: nova/api/openstack/servers.py:184 +#, python-format +msgid "Compute.api::lock %s" +msgstr "Compute.api::lock %s" + +#: nova/api/openstack/servers.py:199 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "Compute.api::unlock %s" + +#: nova/api/openstack/servers.py:213 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "Compute.api::get_lock %s" + +#: nova/api/openstack/servers.py:224 +#, python-format +msgid "Compute.api::pause %s" +msgstr "Compute.api::pause %s" + +#: nova/api/openstack/servers.py:235 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "Compute.api::unpause %s" + +#: nova/api/openstack/servers.py:246 +#, python-format +msgid "compute.api::suspend %s" +msgstr "compute.api::suspend %s" + +#: nova/api/openstack/servers.py:257 +#, python-format +msgid "compute.api::resume %s" +msgstr "compute.api::resume %s" + +#: nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "Usuário %s já existe" + +#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "Projeto não pode ser criado porque o gerente %s não existe." + +#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "Projeto não pode ser criado porque o projeto %s já existe." + +#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "Projeto não pode ser modificado porque o gerente %s não existe." + +#: nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "Usuário \"%s\" não encontrado" + +#: nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "Projeto \"%s\" não encontrado" + +#: nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "Tentativa de instanciar singleton" + +#: nova/auth/ldapdriver.py:181 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "Objeto LDAP para %s não existe" + +#: nova/auth/ldapdriver.py:218 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "Projeto não pode ser criado porque o usuário %s não existe" + +#: nova/auth/ldapdriver.py:478 +#, python-format +msgid "User %s is already a member of the group %s" +msgstr "Usuário %s já pertence ao grupo %s" + +#: nova/auth/ldapdriver.py:507 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "" +"Tentatica de remover o último membto de um grupo. Ao invés disso excluindo o " +"grupo %s." + +#: nova/auth/ldapdriver.py:528 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "Grupo no dn %s não existe" + +#: nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "Procurando usuário: %r" + +#: nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "Falha de autorização para chave de acesso %s" + +#: nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "Nenhum usuário encontrado para chave de acesso %s" + +#: nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "Usando nome do projeto = nome do usuário (%s)" + +#: nova/auth/manager.py:275 +#, python-format +msgid "failed authorization: no project named %s (user=%s)" +msgstr "falha de autorização: nenhum projeto de nome %s (usuário=%s)" + +#: nova/auth/manager.py:277 +#, python-format +msgid "No project called %s could be found" +msgstr "Nenhum projeto chamado %s pode ser encontrado." + +#: nova/auth/manager.py:281 +#, python-format +msgid "Failed authorization: user %s not admin and not member of project %s" +msgstr "" +"Falha de autorização: usuário %s não é administrador nem membro do projeto %s" + +#: nova/auth/manager.py:283 +#, python-format +msgid "User %s is not a member of project %s" +msgstr "Usuário %s não é membro do projeto %s" + +#: nova/auth/manager.py:292 nova/auth/manager.py:303 +#, python-format +msgid "Invalid signature for user %s" +msgstr "Assinatura inválida para usuário %s" + +#: nova/auth/manager.py:293 nova/auth/manager.py:304 +msgid "Signature does not match" +msgstr "Assinatura não confere" + +#: nova/auth/manager.py:374 +msgid "Must specify project" +msgstr "Deve especificar projeto" + +#: nova/auth/manager.py:408 +#, python-format +msgid "The %s role can not be found" +msgstr "O papel %s não foi encontrado" + +#: nova/auth/manager.py:410 +#, python-format +msgid "The %s role is global only" +msgstr "O papel %s é apenas global" + +#: nova/auth/manager.py:412 +#, python-format +msgid "Adding role %s to user %s in project %s" +msgstr "Adicionando papel %s ao usuário %s no projeto %s" + +#: nova/auth/manager.py:438 +#, python-format +msgid "Removing role %s from user %s on project %s" +msgstr "Removendo papel %s do usuário %s no projeto %s" + +#: nova/auth/manager.py:505 +#, python-format +msgid "Created project %s with manager %s" +msgstr "Criado projeto %s com gerente %s" + +#: nova/auth/manager.py:523 +#, python-format +msgid "modifying project %s" +msgstr "modificando projeto %s" + +#: nova/auth/manager.py:553 +#, python-format +msgid "Remove user %s from project %s" +msgstr "Remover usuário %s do projeto %s" + +#: nova/auth/manager.py:581 +#, python-format +msgid "Deleting project %s" +msgstr "Excluindo projeto %s" + +#: nova/auth/manager.py:637 +#, python-format +msgid "Created user %s (admin: %r)" +msgstr "Criado usuário %s (administrador: %r)" + +#: nova/auth/manager.py:645 +#, python-format +msgid "Deleting user %s" +msgstr "" + +#: nova/auth/manager.py:655 +#, python-format +msgid "Access Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:657 +#, python-format +msgid "Secret Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:659 +#, python-format +msgid "Admin status set to %r for user %s" +msgstr "" + +#: nova/auth/manager.py:708 +#, python-format +msgid "No vpn data for project %s" +msgstr "" + +#: nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "" + +#: nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "" + +#: nova/compute/api.py:67 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "" + +#: nova/compute/api.py:73 +#, python-format +msgid "Instance %d has no host" +msgstr "" + +#: nova/compute/api.py:92 +#, python-format +msgid "Quota exceeeded for %s, tried to run %s instances" +msgstr "" + +#: nova/compute/api.py:94 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "" + +#: nova/compute/api.py:109 +msgid "Creating a raw instance" +msgstr "" + +#: nova/compute/api.py:156 +#, python-format +msgid "Going to run %s instances..." +msgstr "" + +#: nova/compute/api.py:180 +#, python-format +msgid "Casting to scheduler for %s/%s's instance %s" +msgstr "" + +#: nova/compute/api.py:279 +#, python-format +msgid "Going to try and terminate %s" +msgstr "" + +#: nova/compute/api.py:283 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "" + +#: nova/compute/api.py:288 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "" + +#: nova/compute/api.py:450 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "" + +#: nova/compute/api.py:465 +msgid "Volume isn't attached to anything!" +msgstr "" + +#: nova/compute/disk.py:71 +#, python-format +msgid "Input partition size not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:75 +#, python-format +msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "" + +#: nova/compute/disk.py:136 +#, python-format +msgid "Failed to load partition: %s" +msgstr "" + +#: nova/compute/disk.py:158 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "" + +#: nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "" + +#: nova/compute/manager.py:69 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "" + +#: nova/compute/manager.py:71 +#, python-format +msgid "check_instance_lock: arguments: |%s| |%s| |%s|" +msgstr "" + +#: nova/compute/manager.py:75 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "" + +#: nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "" + +#: nova/compute/manager.py:82 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "" + +#: nova/compute/manager.py:86 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "" + +#: nova/compute/manager.py:157 +msgid "Instance has already been created" +msgstr "" + +#: nova/compute/manager.py:158 +#, python-format +msgid "instance %s: starting..." +msgstr "" + +#: nova/compute/manager.py:197 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "" + +#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 +#, python-format +msgid "Terminating instance %s" +msgstr "" + +#: nova/compute/manager.py:217 +#, python-format +msgid "Disassociating address %s" +msgstr "" + +#: nova/compute/manager.py:230 +#, python-format +msgid "Deallocating address %s" +msgstr "" + +#: nova/compute/manager.py:243 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "" + +#: nova/compute/manager.py:257 +#, python-format +msgid "Rebooting instance %s" +msgstr "" + +#: nova/compute/manager.py:260 +#, python-format +msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:286 +#, python-format +msgid "instance %s: snapshotting" +msgstr "" + +#: nova/compute/manager.py:289 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:301 +#, python-format +msgid "instance %s: rescuing" +msgstr "" + +#: nova/compute/manager.py:316 +#, python-format +msgid "instance %s: unrescuing" +msgstr "" + +#: nova/compute/manager.py:335 +#, python-format +msgid "instance %s: pausing" +msgstr "" + +#: nova/compute/manager.py:352 +#, python-format +msgid "instance %s: unpausing" +msgstr "" + +#: nova/compute/manager.py:369 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "" + +#: nova/compute/manager.py:382 +#, python-format +msgid "instance %s: suspending" +msgstr "" + +#: nova/compute/manager.py:401 +#, python-format +msgid "instance %s: resuming" +msgstr "" + +#: nova/compute/manager.py:420 +#, python-format +msgid "instance %s: locking" +msgstr "" + +#: nova/compute/manager.py:432 +#, python-format +msgid "instance %s: unlocking" +msgstr "" + +#: nova/compute/manager.py:442 +#, python-format +msgid "instance %s: getting locked state" +msgstr "" + +#: nova/compute/manager.py:462 +#, python-format +msgid "instance %s: attaching volume %s to %s" +msgstr "" + +#: nova/compute/manager.py:478 +#, python-format +msgid "instance %s: attach failed %s, removing" +msgstr "" + +#: nova/compute/manager.py:493 +#, python-format +msgid "Detach volume %s from mountpoint %s on instance %s" +msgstr "" + +#: nova/compute/manager.py:497 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "" + +#: nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "" + +#: nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "" + +#: nova/compute/monitor.py:355 +#, python-format +msgid "Cannot get blockstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:377 +#, python-format +msgid "Cannot get ifstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:412 +msgid "unexpected exception getting connection" +msgstr "" + +#: nova/compute/monitor.py:427 +#, python-format +msgid "Found instance: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:43 +msgid "Use of empty request context is deprecated" +msgstr "" + +#: nova/db/sqlalchemy/api.py:132 +#, python-format +msgid "No service for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:229 +#, python-format +msgid "No service for %s, %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:574 +#, python-format +msgid "No floating ip for address %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:668 +#, python-format +msgid "No instance for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 +#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 +#, python-format +msgid "Instance %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:891 +#, python-format +msgid "no keypair for user %s, name %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 +#, python-format +msgid "No network for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1036 +#, python-format +msgid "No network for bridge %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1050 +#, python-format +msgid "No network for instance %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1180 +#, python-format +msgid "Token %s does not exist" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1205 +#, python-format +msgid "No quota for project_id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1356 +#, python-format +msgid "No volume for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1401 +#, python-format +msgid "Volume %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1413 +#, python-format +msgid "No export device found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1426 +#, python-format +msgid "No target id found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1471 +#, python-format +msgid "No security group with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1488 +#, python-format +msgid "No security group named %s for project: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1576 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1650 +#, python-format +msgid "No user for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1666 +#, python-format +msgid "No user for access key %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1728 +#, python-format +msgid "No project with id %s" +msgstr "" + +#: nova/image/glance.py:78 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images" +msgstr "" + +#: nova/image/glance.py:97 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images/detail" +msgstr "" + +#: nova/image/s3.py:82 +#, python-format +msgid "Image %s could not be found" +msgstr "" + +#: nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "" + +#: nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "" + +#: nova/network/linux_net.py:176 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "" + +#: nova/network/linux_net.py:186 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "" + +#: nova/network/linux_net.py:254 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "" + +#: nova/network/linux_net.py:256 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "" + +#: nova/network/linux_net.py:334 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "" + +#: nova/network/manager.py:135 +msgid "setting network host" +msgstr "" + +#: nova/network/manager.py:190 +#, python-format +msgid "Leasing IP %s" +msgstr "" + +#: nova/network/manager.py:194 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "" + +#: nova/network/manager.py:197 +#, python-format +msgid "IP %s leased to bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:205 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "" + +#: nova/network/manager.py:214 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "" + +#: nova/network/manager.py:217 +#, python-format +msgid "IP %s released from bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:220 +#, python-format +msgid "IP %s released that was not leased" +msgstr "" + +#: nova/network/manager.py:442 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "" + +#: nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "" + +#: nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "" + +#: nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "" + +#: nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:271 +#, python-format +msgid "Getting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:274 +#, python-format +msgid "Unauthorized attempt to get object %s from bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:292 +#, python-format +msgid "Putting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:295 +#, python-format +msgid "Unauthorized attempt to upload object %s to bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:314 +#, python-format +msgid "Deleting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:393 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "" + +#: nova/objectstore/handler.py:401 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:406 +#, python-format +msgid "Starting image upload: %s" +msgstr "" + +#: nova/objectstore/handler.py:420 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "" + +#: nova/objectstore/handler.py:428 +#, python-format +msgid "Toggling publicity flag of image %s %r" +msgstr "" + +#: nova/objectstore/handler.py:433 +#, python-format +msgid "Updating user fields on image %s" +msgstr "" + +#: nova/objectstore/handler.py:447 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "" + +#: nova/objectstore/handler.py:452 +#, python-format +msgid "Deleted image: %s" +msgstr "" + +#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 +#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 +msgid "No hosts found" +msgstr "" + +#: nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "" + +#: nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %s %s for %s" +msgstr "" + +#: nova/scheduler/simple.py:63 +msgid "All hosts have too many cores" +msgstr "" + +#: nova/scheduler/simple.py:95 +msgid "All hosts have too many gigabytes" +msgstr "" + +#: nova/scheduler/simple.py:115 +msgid "All hosts have too many networks" +msgstr "" + +#: nova/tests/test_cloud.py:198 +msgid "Can't test instances without a real virtual env." +msgstr "" + +#: nova/tests/test_cloud.py:210 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "" + +#: nova/tests/test_compute.py:104 +#, python-format +msgid "Running instances: %s" +msgstr "" + +#: nova/tests/test_compute.py:110 +#, python-format +msgid "After terminating instances: %s" +msgstr "" + +#: nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %s, %s" +msgstr "" + +#: nova/tests/test_rpc.py:94 +#, python-format +msgid "Nested return %s" +msgstr "" + +#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 +#, python-format +msgid "Received %s" +msgstr "" + +#: nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "" + +#: nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "" + +#: nova/virt/fake.py:210 +#, python-format +msgid "Instance %s Not Found" +msgstr "" + +#: nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "" + +#: nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "" + +#: nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "" + +#: nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "" + +#: nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "" + +#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 +#, python-format +msgid "Created VM %s..." +msgstr "" + +#: nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %s by attaching disk file %s" +msgstr "" + +#: nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "" + +#: nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "" + +#: nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "" + +#: nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "" + +#: nova/virt/hyperv.py:275 +#, python-format +msgid "Created switch port %s on switch %s" +msgstr "" + +#: nova/virt/hyperv.py:285 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:287 +#, python-format +msgid "Created nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:320 +#, python-format +msgid "WMI job failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:322 +#, python-format +msgid "WMI job succeeded: %s, Elapsed=%s " +msgstr "" + +#: nova/virt/hyperv.py:358 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:383 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:389 +#, python-format +msgid "Del: disk %s vm %s" +msgstr "" + +#: nova/virt/hyperv.py:405 +#, python-format +msgid "" +"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " +"cpu_time=%s" +msgstr "" + +#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 +#, python-format +msgid "duplicate name found: %s" +msgstr "" + +#: nova/virt/hyperv.py:444 +#, python-format +msgid "Successfully changed vm state of %s to %s" +msgstr "" + +#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 +#, python-format +msgid "Failed to change vm state of %s to %s" +msgstr "" + +#: nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %s -- placed in %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:144 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:157 +msgid "Connection to libvirt broke" +msgstr "" + +#: nova/virt/libvirt_conn.py:229 +#, python-format +msgid "instance %s: deleting instance files %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:271 +#, python-format +msgid "No disk at %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:278 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "" + +#: nova/virt/libvirt_conn.py:294 +#, python-format +msgid "instance %s: rebooted" +msgstr "" + +#: nova/virt/libvirt_conn.py:297 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:340 +#, python-format +msgid "instance %s: rescued" +msgstr "" + +#: nova/virt/libvirt_conn.py:343 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:370 +#, python-format +msgid "instance %s: is running" +msgstr "" + +#: nova/virt/libvirt_conn.py:381 +#, python-format +msgid "instance %s: booted" +msgstr "" + +#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 +#, python-format +msgid "instance %s: failed to boot" +msgstr "" + +#: nova/virt/libvirt_conn.py:395 +#, python-format +msgid "virsh said: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:399 +msgid "cool, it's a device" +msgstr "" + +#: nova/virt/libvirt_conn.py:407 +#, python-format +msgid "data: %r, fpath: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:415 +#, python-format +msgid "Contents of file %s: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:449 +#, python-format +msgid "instance %s: Creating image" +msgstr "" + +#: nova/virt/libvirt_conn.py:505 +#, python-format +msgid "instance %s: injecting key into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:508 +#, python-format +msgid "instance %s: injecting net into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:516 +#, python-format +msgid "instance %s: ignoring error injecting data into image %s (%s)" +msgstr "" + +#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "" + +#: nova/virt/libvirt_conn.py:589 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "" + +#: nova/virt/xenapi_conn.py:113 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" + +#: nova/virt/xenapi_conn.py:263 +#, python-format +msgid "Task [%s] %s status: success %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:271 +#, python-format +msgid "Task [%s] %s status: %s %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 +#, python-format +msgid "Got exception: %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:72 +#, python-format +msgid "%s: _db_content => %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 +#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 +msgid "Raising NotImplemented" +msgstr "" + +#: nova/virt/xenapi/fake.py:249 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:283 +#, python-format +msgid "Calling %s %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:288 +#, python-format +msgid "Calling getter %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:340 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:127 +#, python-format +msgid "Created VM %s as %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:147 +#, python-format +msgid "Creating VBD for VM %s, VDI %s ... " +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:149 +#, python-format +msgid "Created VBD %s for VM %s, VDI %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:165 +#, python-format +msgid "VBD not found in instance %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:175 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:202 +#, python-format +msgid "Creating VIF for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:205 +#, python-format +msgid "Created VIF %s for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:216 +#, python-format +msgid "Snapshotting VM %s with label '%s'..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:229 +#, python-format +msgid "Created snapshot %s from VM %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:243 +#, python-format +msgid "Asking xapi to upload %s as '%s'" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:261 +#, python-format +msgid "Asking xapi to fetch %s as %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:279 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:290 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:318 +#, python-format +msgid "VDI %s is still available" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:331 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:333 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:390 +#, python-format +msgid "VHD %s has parent %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:407 +#, python-format +msgid "Re-scanning SR %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:431 +#, python-format +msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:448 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:452 +#, python-format +msgid "Unexpected number of VDIs (%s) found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:62 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:99 +#, python-format +msgid "Starting VM %s..." +msgstr "" + +#: nova/virt/xenapi/vmops.py:101 +#, python-format +msgid "Spawning VM %s created %s." +msgstr "" + +#: nova/virt/xenapi/vmops.py:112 +#, python-format +msgid "Instance %s: booted" +msgstr "" + +#: nova/virt/xenapi/vmops.py:137 +#, python-format +msgid "Instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:166 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:174 +#, python-format +msgid "Unable to Snapshot %s: %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:184 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:252 +#, python-format +msgid "suspend: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "resume: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:271 +#, python-format +msgid "Instance not found %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %s as %s." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %s when getting PBDs for %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %s when unplugging PBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %s when forgetting SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %s, %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %s, %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:81 +#, python-format +msgid "Unable to use SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:95 +#, python-format +msgid "Mountpoint %s attached to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:106 +#, python-format +msgid "Detach_volume: %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:113 +#, python-format +msgid "Unable to locate volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:121 +#, python-format +msgid "Unable to detach volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:128 +#, python-format +msgid "Mountpoint %s detached from instance %s" +msgstr "" + +#: nova/volume/api.py:44 +#, python-format +msgid "Quota exceeeded for %s, tried to create %sG volume" +msgstr "" + +#: nova/volume/api.py:46 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %s" +msgstr "" + +#: nova/volume/api.py:70 nova/volume/api.py:95 +msgid "Volume status must be available" +msgstr "" + +#: nova/volume/api.py:97 +msgid "Volume is already attached" +msgstr "" + +#: nova/volume/api.py:103 +msgid "Volume is already detached" +msgstr "" + +#: nova/volume/driver.py:76 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "" + +#: nova/volume/driver.py:85 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "" + +#: nova/volume/driver.py:210 +#, python-format +msgid "FAKE AOE: %s" +msgstr "" + +#: nova/volume/driver.py:315 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "" + +#: nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "" + +#: nova/volume/manager.py:93 +#, python-format +msgid "volume %s: creating" +msgstr "" + +#: nova/volume/manager.py:102 +#, python-format +msgid "volume %s: creating lv of size %sG" +msgstr "" + +#: nova/volume/manager.py:106 +#, python-format +msgid "volume %s: creating export" +msgstr "" + +#: nova/volume/manager.py:113 +#, python-format +msgid "volume %s: created successfully" +msgstr "" + +#: nova/volume/manager.py:121 +msgid "Volume is still attached" +msgstr "" + +#: nova/volume/manager.py:123 +msgid "Volume is not local to this node" +msgstr "" + +#: nova/volume/manager.py:124 +#, python-format +msgid "volume %s: removing export" +msgstr "" + +#: nova/volume/manager.py:126 +#, python-format +msgid "volume %s: deleting" +msgstr "" + +#: nova/volume/manager.py:129 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "" diff --git a/locale/ru.po b/locale/ru.po new file mode 100644 index 000000000..c751f41b2 --- /dev/null +++ b/locale/ru.po @@ -0,0 +1,2136 @@ +# Russian translation for nova +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the nova package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: nova\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-10 11:25-0800\n" +"PO-Revision-Date: 2011-01-25 17:45+0000\n" +"Last-Translator: Ilya Alekseyev \n" +"Language-Team: Russian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Generator: Launchpad (build 12177)\n" + +#: nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "" + +#: nova/crypto.py:49 +msgid "Filename of private key" +msgstr "Имя файла секретного ключа" + +#: nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "" + +#: nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "" + +#: nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "" + +#: nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "" + +#: nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "" + +#: nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "" + +#: nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "Неожиданная ошибка при выполнении команды." + +#: nova/exception.py:36 +#, python-format +msgid "" +"%s\n" +"Command: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" +msgstr "" +"%s\n" +"Команда: %s\n" +"Код завершения: %s\n" +"Stdout: %r\n" +"Stderr: %r" + +#: nova/exception.py:86 +msgid "Uncaught exception" +msgstr "Необработанное исключение" + +#: nova/fakerabbit.py:48 +#, python-format +msgid "(%s) publish (key: %s) %s" +msgstr "" + +#: nova/fakerabbit.py:53 +#, python-format +msgid "Publishing to route %s" +msgstr "" + +#: nova/fakerabbit.py:83 +#, python-format +msgid "Declaring queue %s" +msgstr "Объявление очереди %s" + +#: nova/fakerabbit.py:89 +#, python-format +msgid "Declaring exchange %s" +msgstr "Объявление точки обмена %s" + +#: nova/fakerabbit.py:95 +#, python-format +msgid "Binding %s to %s with key %s" +msgstr "" + +#: nova/fakerabbit.py:120 +#, python-format +msgid "Getting from %s: %s" +msgstr "" + +#: nova/rpc.py:92 +#, python-format +msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." +msgstr "AMQP сервер %s:%d недоступен. Повторная попытка через %d секунд." + +#: nova/rpc.py:99 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "Не удалось подключиться к серверу AMQP после %d попыток. Выключение." + +#: nova/rpc.py:118 +msgid "Reconnected to queue" +msgstr "Переподлючено к очереди" + +#: nova/rpc.py:125 +msgid "Failed to fetch message from queue" +msgstr "Не удалось получить сообщение из очереди" + +#: nova/rpc.py:155 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "" + +#: nova/rpc.py:170 +#, python-format +msgid "received %s" +msgstr "получено %s" + +#: nova/rpc.py:183 +#, python-format +msgid "no method for message: %s" +msgstr "не определен метод для сообщения: %s" + +#: nova/rpc.py:184 +#, python-format +msgid "No method for message: %s" +msgstr "Не определен метод для сообщения: %s" + +#: nova/rpc.py:245 +#, python-format +msgid "Returning exception %s to caller" +msgstr "" + +#: nova/rpc.py:286 +#, python-format +msgid "unpacked context: %s" +msgstr "" + +#: nova/rpc.py:305 +msgid "Making asynchronous call..." +msgstr "Выполняется асинхронный вызов..." + +#: nova/rpc.py:308 +#, python-format +msgid "MSG_ID is %s" +msgstr "MSG_ID is %s" + +#: nova/rpc.py:356 +#, python-format +msgid "response %s" +msgstr "" + +#: nova/rpc.py:365 +#, python-format +msgid "topic is %s" +msgstr "тема %s" + +#: nova/rpc.py:366 +#, python-format +msgid "message %s" +msgstr "сообщение %s" + +#: nova/service.py:157 +#, python-format +msgid "Starting %s node" +msgstr "Запускается нода %s" + +#: nova/service.py:169 +msgid "Service killed that has no database entry" +msgstr "" + +#: nova/service.py:190 +msgid "The service database object disappeared, Recreating it." +msgstr "Объект сервиса в базе данных отсутствует, Повторное создание." + +#: nova/service.py:202 +msgid "Recovered model server connection!" +msgstr "" + +#: nova/service.py:208 +msgid "model server went away" +msgstr "" + +#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 +#, python-format +msgid "Data store %s is unreachable. Trying again in %d seconds." +msgstr "Хранилище данных %s недоступно. Повторная попытка через %d секунд." + +#: nova/service.py:232 nova/twistd.py:232 +#, python-format +msgid "Serving %s" +msgstr "" + +#: nova/service.py:234 nova/twistd.py:264 +msgid "Full set of FLAGS:" +msgstr "" + +#: nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "pidfile %s не обнаружен. Демон не запущен?\n" + +#: nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "Запускается %s" + +#: nova/utils.py:53 +#, python-format +msgid "Inner Exception: %s" +msgstr "Вложенное исключение: %s" + +#: nova/utils.py:54 +#, python-format +msgid "Class %s cannot be found" +msgstr "Класс %s не найден" + +#: nova/utils.py:113 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: nova/utils.py:125 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "" + +#: nova/utils.py:138 +#, python-format +msgid "Result was %s" +msgstr "Результат %s" + +#: nova/utils.py:171 +#, python-format +msgid "debug in callback: %s" +msgstr "" + +#: nova/utils.py:176 +#, python-format +msgid "Running %s" +msgstr "" + +#: nova/utils.py:207 +#, python-format +msgid "Couldn't get IP, using 127.0.0.1 %s" +msgstr "Не удалось получить IP, используем 127.0.0.1 %s" + +#: nova/utils.py:289 +#, python-format +msgid "Invalid backend: %s" +msgstr "" + +#: nova/utils.py:300 +#, python-format +msgid "backend %s" +msgstr "" + +#: nova/api/ec2/__init__.py:133 +msgid "Too many failed authentications." +msgstr "Слишком много неудачных попыток аутентификации." + +#: nova/api/ec2/__init__.py:142 +#, python-format +msgid "" +"Access key %s has had %d failed authentications and will be locked out for " +"%d minutes." +msgstr "" + +#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:190 +#, python-format +msgid "Authenticated Request For %s:%s)" +msgstr "" + +#: nova/api/ec2/__init__.py:227 +#, python-format +msgid "action: %s" +msgstr "действие: %s" + +#: nova/api/ec2/__init__.py:229 +#, python-format +msgid "arg: %s\t\tval: %s" +msgstr "arg: %s\t\tval: %s" + +#: nova/api/ec2/__init__.py:301 +#, python-format +msgid "Unauthorized request for controller=%s and action=%s" +msgstr "" + +#: nova/api/ec2/__init__.py:339 +#, python-format +msgid "NotFound raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:342 +#, python-format +msgid "ApiError raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:349 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:354 +msgid "An unknown error has occurred. Please try your request again." +msgstr "" +"Произошла неизвестная ошибка. Пожалуйста, попытайтесь повторить ваш запрос." + +#: nova/api/ec2/admin.py:84 +#, python-format +msgid "Creating new user: %s" +msgstr "Создание нового пользователя: %s" + +#: nova/api/ec2/admin.py:92 +#, python-format +msgid "Deleting user: %s" +msgstr "Удаление пользователя: %s" + +#: nova/api/ec2/admin.py:114 +#, python-format +msgid "Adding role %s to user %s for project %s" +msgstr "Добавление роли %s для пользователя %s для проекта %s" + +#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 +#, python-format +msgid "Adding sitewide role %s to user %s" +msgstr "" + +#: nova/api/ec2/admin.py:122 +#, python-format +msgid "Removing role %s from user %s for project %s" +msgstr "Удаление роли %s пользователя %s для проекта %s" + +#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 +#, python-format +msgid "Removing sitewide role %s from user %s" +msgstr "" + +#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 +msgid "operation must be add or remove" +msgstr "" + +#: nova/api/ec2/admin.py:142 +#, python-format +msgid "Getting x509 for user: %s on project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:159 +#, python-format +msgid "Create project %s managed by %s" +msgstr "Создать проект %s под управлением %s" + +#: nova/api/ec2/admin.py:170 +#, python-format +msgid "Delete project: %s" +msgstr "Удалить проект: %s" + +#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 +#, python-format +msgid "Adding user %s to project %s" +msgstr "Добавление пользователя %s к проекту %s" + +#: nova/api/ec2/admin.py:188 +#, python-format +msgid "Removing user %s from project %s" +msgstr "Удаление пользователя %s с проекта %s" + +#: nova/api/ec2/apirequest.py:95 +#, python-format +msgid "Unsupported API request: controller = %s,action = %s" +msgstr "" + +#: nova/api/ec2/cloud.py:117 +#, python-format +msgid "Generating root CA: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:277 +#, python-format +msgid "Create key pair %s" +msgstr "Создание пары ключей %s" + +#: nova/api/ec2/cloud.py:285 +#, python-format +msgid "Delete key pair %s" +msgstr "Удаление пары ключей %s" + +#: nova/api/ec2/cloud.py:357 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "" + +#: nova/api/ec2/cloud.py:361 +msgid "Invalid port range" +msgstr "Неверный диапазон портов" + +#: nova/api/ec2/cloud.py:392 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 +msgid "No rule for the specified parameters." +msgstr "" + +#: nova/api/ec2/cloud.py:421 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:432 +#, python-format +msgid "This rule already exists in group %s" +msgstr "Это правило уже существует в группе %s" + +#: nova/api/ec2/cloud.py:460 +#, python-format +msgid "Create Security Group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:463 +#, python-format +msgid "group %s already exists" +msgstr "группа %s уже существует" + +#: nova/api/ec2/cloud.py:475 +#, python-format +msgid "Delete security group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 +#, python-format +msgid "Get console output for instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:543 +#, python-format +msgid "Create volume of %s GB" +msgstr "Создание раздела %s ГБ" + +#: nova/api/ec2/cloud.py:567 +#, python-format +msgid "Attach volume %s to instacne %s at %s" +msgstr "" + +#: nova/api/ec2/cloud.py:579 +#, python-format +msgid "Detach volume %s" +msgstr "" + +#: nova/api/ec2/cloud.py:686 +msgid "Allocate address" +msgstr "" + +#: nova/api/ec2/cloud.py:691 +#, python-format +msgid "Release address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:696 +#, python-format +msgid "Associate address %s to instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:703 +#, python-format +msgid "Disassociate address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:730 +msgid "Going to start terminating instances" +msgstr "" + +#: nova/api/ec2/cloud.py:738 +#, python-format +msgid "Reboot instance %r" +msgstr "" + +#: nova/api/ec2/cloud.py:775 +#, python-format +msgid "De-registering image %s" +msgstr "" + +#: nova/api/ec2/cloud.py:783 +#, python-format +msgid "Registered image %s with id %s" +msgstr "" + +#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 +#, python-format +msgid "attribute not supported: %s" +msgstr "аттрибут не поддерживается: %s" + +#: nova/api/ec2/cloud.py:794 +#, python-format +msgid "invalid id: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:807 +msgid "user or group not specified" +msgstr "не указан пользователь или группа" + +#: nova/api/ec2/cloud.py:809 +msgid "only group \"all\" is supported" +msgstr "" + +#: nova/api/ec2/cloud.py:811 +msgid "operation_type must be add or remove" +msgstr "" + +#: nova/api/ec2/cloud.py:812 +#, python-format +msgid "Updating image %s publicity" +msgstr "" + +#: nova/api/ec2/metadatarequesthandler.py:75 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:70 +#, python-format +msgid "Caught error: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:86 +msgid "Including admin operations in API." +msgstr "" + +#: nova/api/openstack/servers.py:184 +#, python-format +msgid "Compute.api::lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:199 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "" + +#: nova/api/openstack/servers.py:213 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:224 +#, python-format +msgid "Compute.api::pause %s" +msgstr "" + +#: nova/api/openstack/servers.py:235 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "" + +#: nova/api/openstack/servers.py:246 +#, python-format +msgid "compute.api::suspend %s" +msgstr "" + +#: nova/api/openstack/servers.py:257 +#, python-format +msgid "compute.api::resume %s" +msgstr "" + +#: nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "Пользователь %s уже существует" + +#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "Проект не может быть создан поскольку менеджер %s не существует" + +#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "Проект не может быть созан поскольку проект %s уже существует" + +#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "Пользователь \"%s\" не существует" + +#: nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "Проект \"%s\" не найден" + +#: nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "" + +#: nova/auth/ldapdriver.py:181 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "Объект LDAP %s не существует" + +#: nova/auth/ldapdriver.py:218 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "Проект не может быть создан поскольку пользователь %s не существует" + +#: nova/auth/ldapdriver.py:478 +#, python-format +msgid "User %s is already a member of the group %s" +msgstr "Пользователь %s уже член группы %s" + +#: nova/auth/ldapdriver.py:507 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "" + +#: nova/auth/ldapdriver.py:528 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "" + +#: nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "" + +#: nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "" + +#: nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "" + +#: nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "" + +#: nova/auth/manager.py:275 +#, python-format +msgid "failed authorization: no project named %s (user=%s)" +msgstr "" + +#: nova/auth/manager.py:277 +#, python-format +msgid "No project called %s could be found" +msgstr "" + +#: nova/auth/manager.py:281 +#, python-format +msgid "Failed authorization: user %s not admin and not member of project %s" +msgstr "" + +#: nova/auth/manager.py:283 +#, python-format +msgid "User %s is not a member of project %s" +msgstr "Пользователь %s не является членом группы %s" + +#: nova/auth/manager.py:292 nova/auth/manager.py:303 +#, python-format +msgid "Invalid signature for user %s" +msgstr "Не допустимая подпись для пользователя %s" + +#: nova/auth/manager.py:293 nova/auth/manager.py:304 +msgid "Signature does not match" +msgstr "Подпись не совпадает" + +#: nova/auth/manager.py:374 +msgid "Must specify project" +msgstr "Необходимо указать проект" + +#: nova/auth/manager.py:408 +#, python-format +msgid "The %s role can not be found" +msgstr "Роль %s не может быть найдена" + +#: nova/auth/manager.py:410 +#, python-format +msgid "The %s role is global only" +msgstr "" + +#: nova/auth/manager.py:412 +#, python-format +msgid "Adding role %s to user %s in project %s" +msgstr "Добавление роли %s для пользователя %s в проект %s" + +#: nova/auth/manager.py:438 +#, python-format +msgid "Removing role %s from user %s on project %s" +msgstr "Удаление роли %s пользователя %s в проекте %s" + +#: nova/auth/manager.py:505 +#, python-format +msgid "Created project %s with manager %s" +msgstr "Создан проект %s под управлением %s" + +#: nova/auth/manager.py:523 +#, python-format +msgid "modifying project %s" +msgstr "изменение проекта %s" + +#: nova/auth/manager.py:553 +#, python-format +msgid "Remove user %s from project %s" +msgstr "Удалить пользователя %s из проекта %s" + +#: nova/auth/manager.py:581 +#, python-format +msgid "Deleting project %s" +msgstr "Удаление проекта %s" + +#: nova/auth/manager.py:637 +#, python-format +msgid "Created user %s (admin: %r)" +msgstr "Создан пользователь %s (администратор: %r)" + +#: nova/auth/manager.py:645 +#, python-format +msgid "Deleting user %s" +msgstr "Удаление пользователя %s" + +#: nova/auth/manager.py:655 +#, python-format +msgid "Access Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:657 +#, python-format +msgid "Secret Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:659 +#, python-format +msgid "Admin status set to %r for user %s" +msgstr "" + +#: nova/auth/manager.py:708 +#, python-format +msgid "No vpn data for project %s" +msgstr "Нет vpn данных для проекта %s" + +#: nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "" + +#: nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "Запуск VPN для %s" + +#: nova/compute/api.py:67 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "" + +#: nova/compute/api.py:73 +#, python-format +msgid "Instance %d has no host" +msgstr "" + +#: nova/compute/api.py:92 +#, python-format +msgid "Quota exceeeded for %s, tried to run %s instances" +msgstr "" + +#: nova/compute/api.py:94 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "" + +#: nova/compute/api.py:109 +msgid "Creating a raw instance" +msgstr "" + +#: nova/compute/api.py:156 +#, python-format +msgid "Going to run %s instances..." +msgstr "" + +#: nova/compute/api.py:180 +#, python-format +msgid "Casting to scheduler for %s/%s's instance %s" +msgstr "" + +#: nova/compute/api.py:279 +#, python-format +msgid "Going to try and terminate %s" +msgstr "" + +#: nova/compute/api.py:283 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "" + +#: nova/compute/api.py:288 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "" + +#: nova/compute/api.py:450 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "" + +#: nova/compute/api.py:465 +msgid "Volume isn't attached to anything!" +msgstr "" + +#: nova/compute/disk.py:71 +#, python-format +msgid "Input partition size not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:75 +#, python-format +msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "" + +#: nova/compute/disk.py:136 +#, python-format +msgid "Failed to load partition: %s" +msgstr "" + +#: nova/compute/disk.py:158 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "Ошибка монтирования файловой системы: %s" + +#: nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "" + +#: nova/compute/manager.py:69 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "" + +#: nova/compute/manager.py:71 +#, python-format +msgid "check_instance_lock: arguments: |%s| |%s| |%s|" +msgstr "" + +#: nova/compute/manager.py:75 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "" + +#: nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "" + +#: nova/compute/manager.py:82 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "" + +#: nova/compute/manager.py:86 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "" + +#: nova/compute/manager.py:157 +msgid "Instance has already been created" +msgstr "" + +#: nova/compute/manager.py:158 +#, python-format +msgid "instance %s: starting..." +msgstr "" + +#: nova/compute/manager.py:197 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "" + +#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 +#, python-format +msgid "Terminating instance %s" +msgstr "" + +#: nova/compute/manager.py:217 +#, python-format +msgid "Disassociating address %s" +msgstr "" + +#: nova/compute/manager.py:230 +#, python-format +msgid "Deallocating address %s" +msgstr "" + +#: nova/compute/manager.py:243 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "" + +#: nova/compute/manager.py:257 +#, python-format +msgid "Rebooting instance %s" +msgstr "" + +#: nova/compute/manager.py:260 +#, python-format +msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:286 +#, python-format +msgid "instance %s: snapshotting" +msgstr "" + +#: nova/compute/manager.py:289 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:301 +#, python-format +msgid "instance %s: rescuing" +msgstr "" + +#: nova/compute/manager.py:316 +#, python-format +msgid "instance %s: unrescuing" +msgstr "" + +#: nova/compute/manager.py:335 +#, python-format +msgid "instance %s: pausing" +msgstr "" + +#: nova/compute/manager.py:352 +#, python-format +msgid "instance %s: unpausing" +msgstr "" + +#: nova/compute/manager.py:369 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "" + +#: nova/compute/manager.py:382 +#, python-format +msgid "instance %s: suspending" +msgstr "" + +#: nova/compute/manager.py:401 +#, python-format +msgid "instance %s: resuming" +msgstr "" + +#: nova/compute/manager.py:420 +#, python-format +msgid "instance %s: locking" +msgstr "" + +#: nova/compute/manager.py:432 +#, python-format +msgid "instance %s: unlocking" +msgstr "" + +#: nova/compute/manager.py:442 +#, python-format +msgid "instance %s: getting locked state" +msgstr "" + +#: nova/compute/manager.py:462 +#, python-format +msgid "instance %s: attaching volume %s to %s" +msgstr "" + +#: nova/compute/manager.py:478 +#, python-format +msgid "instance %s: attach failed %s, removing" +msgstr "" + +#: nova/compute/manager.py:493 +#, python-format +msgid "Detach volume %s from mountpoint %s on instance %s" +msgstr "" + +#: nova/compute/manager.py:497 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "" + +#: nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "обновление %s..." + +#: nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "неожиданная ошибка во время обновления" + +#: nova/compute/monitor.py:355 +#, python-format +msgid "Cannot get blockstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:377 +#, python-format +msgid "Cannot get ifstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:412 +msgid "unexpected exception getting connection" +msgstr "" + +#: nova/compute/monitor.py:427 +#, python-format +msgid "Found instance: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:43 +msgid "Use of empty request context is deprecated" +msgstr "" + +#: nova/db/sqlalchemy/api.py:132 +#, python-format +msgid "No service for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:229 +#, python-format +msgid "No service for %s, %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:574 +#, python-format +msgid "No floating ip for address %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:668 +#, python-format +msgid "No instance for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 +#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 +#, python-format +msgid "Instance %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:891 +#, python-format +msgid "no keypair for user %s, name %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 +#, python-format +msgid "No network for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1036 +#, python-format +msgid "No network for bridge %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1050 +#, python-format +msgid "No network for instance %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1180 +#, python-format +msgid "Token %s does not exist" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1205 +#, python-format +msgid "No quota for project_id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1356 +#, python-format +msgid "No volume for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1401 +#, python-format +msgid "Volume %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1413 +#, python-format +msgid "No export device found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1426 +#, python-format +msgid "No target id found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1471 +#, python-format +msgid "No security group with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1488 +#, python-format +msgid "No security group named %s for project: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1576 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1650 +#, python-format +msgid "No user for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1666 +#, python-format +msgid "No user for access key %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1728 +#, python-format +msgid "No project with id %s" +msgstr "" + +#: nova/image/glance.py:78 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images" +msgstr "" + +#: nova/image/glance.py:97 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images/detail" +msgstr "" + +#: nova/image/s3.py:82 +#, python-format +msgid "Image %s could not be found" +msgstr "" + +#: nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "" + +#: nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "" + +#: nova/network/linux_net.py:176 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "" + +#: nova/network/linux_net.py:186 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "" + +#: nova/network/linux_net.py:254 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "" + +#: nova/network/linux_net.py:256 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "" + +#: nova/network/linux_net.py:334 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "" + +#: nova/network/manager.py:135 +msgid "setting network host" +msgstr "" + +#: nova/network/manager.py:190 +#, python-format +msgid "Leasing IP %s" +msgstr "" + +#: nova/network/manager.py:194 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "" + +#: nova/network/manager.py:197 +#, python-format +msgid "IP %s leased to bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:205 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "" + +#: nova/network/manager.py:214 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "" + +#: nova/network/manager.py:217 +#, python-format +msgid "IP %s released from bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:220 +#, python-format +msgid "IP %s released that was not leased" +msgstr "" + +#: nova/network/manager.py:442 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "" + +#: nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "" + +#: nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "" + +#: nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "" + +#: nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:271 +#, python-format +msgid "Getting object: %s / %s" +msgstr "Получение объекта: %s / %s" + +#: nova/objectstore/handler.py:274 +#, python-format +msgid "Unauthorized attempt to get object %s from bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:292 +#, python-format +msgid "Putting object: %s / %s" +msgstr "Вставка объекта: %s / %s" + +#: nova/objectstore/handler.py:295 +#, python-format +msgid "Unauthorized attempt to upload object %s to bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:314 +#, python-format +msgid "Deleting object: %s / %s" +msgstr "Удаление объекта: %s / %s" + +#: nova/objectstore/handler.py:393 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "" + +#: nova/objectstore/handler.py:401 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:406 +#, python-format +msgid "Starting image upload: %s" +msgstr "" + +#: nova/objectstore/handler.py:420 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "" + +#: nova/objectstore/handler.py:428 +#, python-format +msgid "Toggling publicity flag of image %s %r" +msgstr "" + +#: nova/objectstore/handler.py:433 +#, python-format +msgid "Updating user fields on image %s" +msgstr "" + +#: nova/objectstore/handler.py:447 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "" + +#: nova/objectstore/handler.py:452 +#, python-format +msgid "Deleted image: %s" +msgstr "Удаленное изображение: %s" + +#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 +#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 +msgid "No hosts found" +msgstr "" + +#: nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "" + +#: nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %s %s for %s" +msgstr "" + +#: nova/scheduler/simple.py:63 +msgid "All hosts have too many cores" +msgstr "" + +#: nova/scheduler/simple.py:95 +msgid "All hosts have too many gigabytes" +msgstr "" + +#: nova/scheduler/simple.py:115 +msgid "All hosts have too many networks" +msgstr "" + +#: nova/tests/test_cloud.py:198 +msgid "Can't test instances without a real virtual env." +msgstr "" + +#: nova/tests/test_cloud.py:210 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "" + +#: nova/tests/test_compute.py:104 +#, python-format +msgid "Running instances: %s" +msgstr "" + +#: nova/tests/test_compute.py:110 +#, python-format +msgid "After terminating instances: %s" +msgstr "" + +#: nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %s, %s" +msgstr "" + +#: nova/tests/test_rpc.py:94 +#, python-format +msgid "Nested return %s" +msgstr "" + +#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 +#, python-format +msgid "Received %s" +msgstr "Получено %s" + +#: nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "" + +#: nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "" + +#: nova/virt/fake.py:210 +#, python-format +msgid "Instance %s Not Found" +msgstr "" + +#: nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "" + +#: nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "" + +#: nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "Запускается VM %s " + +#: nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "Запущен VM %s " + +#: nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "" + +#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 +#, python-format +msgid "Created VM %s..." +msgstr "" + +#: nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %s by attaching disk file %s" +msgstr "" + +#: nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "" + +#: nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "Создан диск для %s" + +#: nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "" + +#: nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "" + +#: nova/virt/hyperv.py:275 +#, python-format +msgid "Created switch port %s on switch %s" +msgstr "" + +#: nova/virt/hyperv.py:285 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:287 +#, python-format +msgid "Created nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:320 +#, python-format +msgid "WMI job failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:322 +#, python-format +msgid "WMI job succeeded: %s, Elapsed=%s " +msgstr "" + +#: nova/virt/hyperv.py:358 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:383 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:389 +#, python-format +msgid "Del: disk %s vm %s" +msgstr "" + +#: nova/virt/hyperv.py:405 +#, python-format +msgid "" +"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " +"cpu_time=%s" +msgstr "" + +#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 +#, python-format +msgid "duplicate name found: %s" +msgstr "" + +#: nova/virt/hyperv.py:444 +#, python-format +msgid "Successfully changed vm state of %s to %s" +msgstr "" + +#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 +#, python-format +msgid "Failed to change vm state of %s to %s" +msgstr "" + +#: nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %s -- placed in %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:144 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:157 +msgid "Connection to libvirt broke" +msgstr "" + +#: nova/virt/libvirt_conn.py:229 +#, python-format +msgid "instance %s: deleting instance files %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:271 +#, python-format +msgid "No disk at %s" +msgstr "Нет диска в %s" + +#: nova/virt/libvirt_conn.py:278 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "" + +#: nova/virt/libvirt_conn.py:294 +#, python-format +msgid "instance %s: rebooted" +msgstr "" + +#: nova/virt/libvirt_conn.py:297 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:340 +#, python-format +msgid "instance %s: rescued" +msgstr "" + +#: nova/virt/libvirt_conn.py:343 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:370 +#, python-format +msgid "instance %s: is running" +msgstr "" + +#: nova/virt/libvirt_conn.py:381 +#, python-format +msgid "instance %s: booted" +msgstr "" + +#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 +#, python-format +msgid "instance %s: failed to boot" +msgstr "" + +#: nova/virt/libvirt_conn.py:395 +#, python-format +msgid "virsh said: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:399 +msgid "cool, it's a device" +msgstr "" + +#: nova/virt/libvirt_conn.py:407 +#, python-format +msgid "data: %r, fpath: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:415 +#, python-format +msgid "Contents of file %s: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:449 +#, python-format +msgid "instance %s: Creating image" +msgstr "" + +#: nova/virt/libvirt_conn.py:505 +#, python-format +msgid "instance %s: injecting key into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:508 +#, python-format +msgid "instance %s: injecting net into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:516 +#, python-format +msgid "instance %s: ignoring error injecting data into image %s (%s)" +msgstr "" + +#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "" + +#: nova/virt/libvirt_conn.py:589 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "" + +#: nova/virt/xenapi_conn.py:113 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" + +#: nova/virt/xenapi_conn.py:263 +#, python-format +msgid "Task [%s] %s status: success %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:271 +#, python-format +msgid "Task [%s] %s status: %s %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 +#, python-format +msgid "Got exception: %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:72 +#, python-format +msgid "%s: _db_content => %s" +msgstr "%s: _db_content => %s" + +#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 +#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 +msgid "Raising NotImplemented" +msgstr "" + +#: nova/virt/xenapi/fake.py:249 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:283 +#, python-format +msgid "Calling %s %s" +msgstr "Звонок %s %s" + +#: nova/virt/xenapi/fake.py:288 +#, python-format +msgid "Calling getter %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:340 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:127 +#, python-format +msgid "Created VM %s as %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:147 +#, python-format +msgid "Creating VBD for VM %s, VDI %s ... " +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:149 +#, python-format +msgid "Created VBD %s for VM %s, VDI %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:165 +#, python-format +msgid "VBD not found in instance %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:175 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:202 +#, python-format +msgid "Creating VIF for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:205 +#, python-format +msgid "Created VIF %s for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:216 +#, python-format +msgid "Snapshotting VM %s with label '%s'..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:229 +#, python-format +msgid "Created snapshot %s from VM %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:243 +#, python-format +msgid "Asking xapi to upload %s as '%s'" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:261 +#, python-format +msgid "Asking xapi to fetch %s as %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:279 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:290 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:318 +#, python-format +msgid "VDI %s is still available" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:331 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:333 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:390 +#, python-format +msgid "VHD %s has parent %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:407 +#, python-format +msgid "Re-scanning SR %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:431 +#, python-format +msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:448 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:452 +#, python-format +msgid "Unexpected number of VDIs (%s) found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:62 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:99 +#, python-format +msgid "Starting VM %s..." +msgstr "" + +#: nova/virt/xenapi/vmops.py:101 +#, python-format +msgid "Spawning VM %s created %s." +msgstr "" + +#: nova/virt/xenapi/vmops.py:112 +#, python-format +msgid "Instance %s: booted" +msgstr "" + +#: nova/virt/xenapi/vmops.py:137 +#, python-format +msgid "Instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:166 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:174 +#, python-format +msgid "Unable to Snapshot %s: %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:184 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:252 +#, python-format +msgid "suspend: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "resume: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:271 +#, python-format +msgid "Instance not found %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %s as %s." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %s when getting PBDs for %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %s when unplugging PBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %s when forgetting SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %s, %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %s, %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:81 +#, python-format +msgid "Unable to use SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:95 +#, python-format +msgid "Mountpoint %s attached to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:106 +#, python-format +msgid "Detach_volume: %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:113 +#, python-format +msgid "Unable to locate volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:121 +#, python-format +msgid "Unable to detach volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:128 +#, python-format +msgid "Mountpoint %s detached from instance %s" +msgstr "" + +#: nova/volume/api.py:44 +#, python-format +msgid "Quota exceeeded for %s, tried to create %sG volume" +msgstr "" + +#: nova/volume/api.py:46 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %s" +msgstr "" + +#: nova/volume/api.py:70 nova/volume/api.py:95 +msgid "Volume status must be available" +msgstr "" + +#: nova/volume/api.py:97 +msgid "Volume is already attached" +msgstr "" + +#: nova/volume/api.py:103 +msgid "Volume is already detached" +msgstr "" + +#: nova/volume/driver.py:76 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "" + +#: nova/volume/driver.py:85 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "" + +#: nova/volume/driver.py:210 +#, python-format +msgid "FAKE AOE: %s" +msgstr "" + +#: nova/volume/driver.py:315 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "" + +#: nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "" + +#: nova/volume/manager.py:93 +#, python-format +msgid "volume %s: creating" +msgstr "" + +#: nova/volume/manager.py:102 +#, python-format +msgid "volume %s: creating lv of size %sG" +msgstr "" + +#: nova/volume/manager.py:106 +#, python-format +msgid "volume %s: creating export" +msgstr "" + +#: nova/volume/manager.py:113 +#, python-format +msgid "volume %s: created successfully" +msgstr "" + +#: nova/volume/manager.py:121 +msgid "Volume is still attached" +msgstr "" + +#: nova/volume/manager.py:123 +msgid "Volume is not local to this node" +msgstr "" + +#: nova/volume/manager.py:124 +#, python-format +msgid "volume %s: removing export" +msgstr "" + +#: nova/volume/manager.py:126 +#, python-format +msgid "volume %s: deleting" +msgstr "" + +#: nova/volume/manager.py:129 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "" diff --git a/locale/uk.po b/locale/uk.po new file mode 100644 index 000000000..cdbffd130 --- /dev/null +++ b/locale/uk.po @@ -0,0 +1,2130 @@ +# Ukrainian translation for nova +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the nova package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: nova\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-10 11:25-0800\n" +"PO-Revision-Date: 2011-01-13 07:03+0000\n" +"Last-Translator: Wladimir Rossinski \n" +"Language-Team: Ukrainian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Generator: Launchpad (build 12177)\n" + +#: nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "" + +#: nova/crypto.py:49 +msgid "Filename of private key" +msgstr "" + +#: nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "" + +#: nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "" + +#: nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "" + +#: nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "" + +#: nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "" + +#: nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "" + +#: nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "" + +#: nova/exception.py:36 +#, python-format +msgid "" +"%s\n" +"Command: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" +msgstr "" + +#: nova/exception.py:86 +msgid "Uncaught exception" +msgstr "" + +#: nova/fakerabbit.py:48 +#, python-format +msgid "(%s) publish (key: %s) %s" +msgstr "" + +#: nova/fakerabbit.py:53 +#, python-format +msgid "Publishing to route %s" +msgstr "" + +#: nova/fakerabbit.py:83 +#, python-format +msgid "Declaring queue %s" +msgstr "" + +#: nova/fakerabbit.py:89 +#, python-format +msgid "Declaring exchange %s" +msgstr "" + +#: nova/fakerabbit.py:95 +#, python-format +msgid "Binding %s to %s with key %s" +msgstr "" + +#: nova/fakerabbit.py:120 +#, python-format +msgid "Getting from %s: %s" +msgstr "" + +#: nova/rpc.py:92 +#, python-format +msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." +msgstr "AMQP сервер %s:%d недоступний. Спроба під'єднання через %d секунд." + +#: nova/rpc.py:99 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "Не вдалось під'єднатися до серверу AMQP після %d спроб. Вимкнення." + +#: nova/rpc.py:118 +msgid "Reconnected to queue" +msgstr "" + +#: nova/rpc.py:125 +msgid "Failed to fetch message from queue" +msgstr "" + +#: nova/rpc.py:155 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "" + +#: nova/rpc.py:170 +#, python-format +msgid "received %s" +msgstr "отримано %s" + +#: nova/rpc.py:183 +#, python-format +msgid "no method for message: %s" +msgstr "без порядку для повідомлень: %s" + +#: nova/rpc.py:184 +#, python-format +msgid "No method for message: %s" +msgstr "Без порядку для повідомлень: %s" + +#: nova/rpc.py:245 +#, python-format +msgid "Returning exception %s to caller" +msgstr "" + +#: nova/rpc.py:286 +#, python-format +msgid "unpacked context: %s" +msgstr "" + +#: nova/rpc.py:305 +msgid "Making asynchronous call..." +msgstr "Створення асинхронного виклику..." + +#: nova/rpc.py:308 +#, python-format +msgid "MSG_ID is %s" +msgstr "MSG_ID %s" + +#: nova/rpc.py:356 +#, python-format +msgid "response %s" +msgstr "відповідь %s" + +#: nova/rpc.py:365 +#, python-format +msgid "topic is %s" +msgstr "заголовок %s" + +#: nova/rpc.py:366 +#, python-format +msgid "message %s" +msgstr "повідомлення %s" + +#: nova/service.py:157 +#, python-format +msgid "Starting %s node" +msgstr "" + +#: nova/service.py:169 +msgid "Service killed that has no database entry" +msgstr "" + +#: nova/service.py:190 +msgid "The service database object disappeared, Recreating it." +msgstr "" + +#: nova/service.py:202 +msgid "Recovered model server connection!" +msgstr "" + +#: nova/service.py:208 +msgid "model server went away" +msgstr "" + +#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 +#, python-format +msgid "Data store %s is unreachable. Trying again in %d seconds." +msgstr "" + +#: nova/service.py:232 nova/twistd.py:232 +#, python-format +msgid "Serving %s" +msgstr "Обслуговування %s" + +#: nova/service.py:234 nova/twistd.py:264 +msgid "Full set of FLAGS:" +msgstr "" + +#: nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "" + +#: nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "Запускається %s" + +#: nova/utils.py:53 +#, python-format +msgid "Inner Exception: %s" +msgstr "" + +#: nova/utils.py:54 +#, python-format +msgid "Class %s cannot be found" +msgstr "" + +#: nova/utils.py:113 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: nova/utils.py:125 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "" + +#: nova/utils.py:138 +#, python-format +msgid "Result was %s" +msgstr "" + +#: nova/utils.py:171 +#, python-format +msgid "debug in callback: %s" +msgstr "" + +#: nova/utils.py:176 +#, python-format +msgid "Running %s" +msgstr "Запускається %s" + +#: nova/utils.py:207 +#, python-format +msgid "Couldn't get IP, using 127.0.0.1 %s" +msgstr "Не вдалось отримати IP, використовуючи 127.0.0.1 %s" + +#: nova/utils.py:289 +#, python-format +msgid "Invalid backend: %s" +msgstr "" + +#: nova/utils.py:300 +#, python-format +msgid "backend %s" +msgstr "" + +#: nova/api/ec2/__init__.py:133 +msgid "Too many failed authentications." +msgstr "Занадто багато невдалих аутентифікацій." + +#: nova/api/ec2/__init__.py:142 +#, python-format +msgid "" +"Access key %s has had %d failed authentications and will be locked out for " +"%d minutes." +msgstr "" + +#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:190 +#, python-format +msgid "Authenticated Request For %s:%s)" +msgstr "" + +#: nova/api/ec2/__init__.py:227 +#, python-format +msgid "action: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:229 +#, python-format +msgid "arg: %s\t\tval: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:301 +#, python-format +msgid "Unauthorized request for controller=%s and action=%s" +msgstr "" + +#: nova/api/ec2/__init__.py:339 +#, python-format +msgid "NotFound raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:342 +#, python-format +msgid "ApiError raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:349 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:354 +msgid "An unknown error has occurred. Please try your request again." +msgstr "" + +#: nova/api/ec2/admin.py:84 +#, python-format +msgid "Creating new user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:92 +#, python-format +msgid "Deleting user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:114 +#, python-format +msgid "Adding role %s to user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 +#, python-format +msgid "Adding sitewide role %s to user %s" +msgstr "" + +#: nova/api/ec2/admin.py:122 +#, python-format +msgid "Removing role %s from user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 +#, python-format +msgid "Removing sitewide role %s from user %s" +msgstr "" + +#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 +msgid "operation must be add or remove" +msgstr "" + +#: nova/api/ec2/admin.py:142 +#, python-format +msgid "Getting x509 for user: %s on project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:159 +#, python-format +msgid "Create project %s managed by %s" +msgstr "" + +#: nova/api/ec2/admin.py:170 +#, python-format +msgid "Delete project: %s" +msgstr "Вилучити проект: %s" + +#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 +#, python-format +msgid "Adding user %s to project %s" +msgstr "Долучення користувача %s до проекту %s" + +#: nova/api/ec2/admin.py:188 +#, python-format +msgid "Removing user %s from project %s" +msgstr "Вилучення користувача %s з проекту %s" + +#: nova/api/ec2/apirequest.py:95 +#, python-format +msgid "Unsupported API request: controller = %s,action = %s" +msgstr "" + +#: nova/api/ec2/cloud.py:117 +#, python-format +msgid "Generating root CA: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:277 +#, python-format +msgid "Create key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:285 +#, python-format +msgid "Delete key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:357 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "%s не допустимий ipProtocol" + +#: nova/api/ec2/cloud.py:361 +msgid "Invalid port range" +msgstr "Невірний діапазон портів" + +#: nova/api/ec2/cloud.py:392 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 +msgid "No rule for the specified parameters." +msgstr "" + +#: nova/api/ec2/cloud.py:421 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:432 +#, python-format +msgid "This rule already exists in group %s" +msgstr "Це правило вже існує в групі %s" + +#: nova/api/ec2/cloud.py:460 +#, python-format +msgid "Create Security Group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:463 +#, python-format +msgid "group %s already exists" +msgstr "" + +#: nova/api/ec2/cloud.py:475 +#, python-format +msgid "Delete security group %s" +msgstr "Вилучити групу безпеки %s" + +#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 +#, python-format +msgid "Get console output for instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:543 +#, python-format +msgid "Create volume of %s GB" +msgstr "Створити розділ на %s ГБ" + +#: nova/api/ec2/cloud.py:567 +#, python-format +msgid "Attach volume %s to instacne %s at %s" +msgstr "" + +#: nova/api/ec2/cloud.py:579 +#, python-format +msgid "Detach volume %s" +msgstr "Від'єднати том %s" + +#: nova/api/ec2/cloud.py:686 +msgid "Allocate address" +msgstr "" + +#: nova/api/ec2/cloud.py:691 +#, python-format +msgid "Release address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:696 +#, python-format +msgid "Associate address %s to instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:703 +#, python-format +msgid "Disassociate address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:730 +msgid "Going to start terminating instances" +msgstr "" + +#: nova/api/ec2/cloud.py:738 +#, python-format +msgid "Reboot instance %r" +msgstr "" + +#: nova/api/ec2/cloud.py:775 +#, python-format +msgid "De-registering image %s" +msgstr "" + +#: nova/api/ec2/cloud.py:783 +#, python-format +msgid "Registered image %s with id %s" +msgstr "" + +#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 +#, python-format +msgid "attribute not supported: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:794 +#, python-format +msgid "invalid id: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:807 +msgid "user or group not specified" +msgstr "" + +#: nova/api/ec2/cloud.py:809 +msgid "only group \"all\" is supported" +msgstr "лише група \"всі\" підтримується" + +#: nova/api/ec2/cloud.py:811 +msgid "operation_type must be add or remove" +msgstr "" + +#: nova/api/ec2/cloud.py:812 +#, python-format +msgid "Updating image %s publicity" +msgstr "" + +#: nova/api/ec2/metadatarequesthandler.py:75 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:70 +#, python-format +msgid "Caught error: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:86 +msgid "Including admin operations in API." +msgstr "" + +#: nova/api/openstack/servers.py:184 +#, python-format +msgid "Compute.api::lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:199 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "" + +#: nova/api/openstack/servers.py:213 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:224 +#, python-format +msgid "Compute.api::pause %s" +msgstr "" + +#: nova/api/openstack/servers.py:235 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "" + +#: nova/api/openstack/servers.py:246 +#, python-format +msgid "compute.api::suspend %s" +msgstr "" + +#: nova/api/openstack/servers.py:257 +#, python-format +msgid "compute.api::resume %s" +msgstr "" + +#: nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "Користувач %s вже існує" + +#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "Користувач \"%s\" не знайдено" + +#: nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "Проект \"%s\" не знайдено" + +#: nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "" + +#: nova/auth/ldapdriver.py:181 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:218 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:478 +#, python-format +msgid "User %s is already a member of the group %s" +msgstr "" + +#: nova/auth/ldapdriver.py:507 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "" + +#: nova/auth/ldapdriver.py:528 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "" + +#: nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "" + +#: nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "" + +#: nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "" + +#: nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "" + +#: nova/auth/manager.py:275 +#, python-format +msgid "failed authorization: no project named %s (user=%s)" +msgstr "" + +#: nova/auth/manager.py:277 +#, python-format +msgid "No project called %s could be found" +msgstr "" + +#: nova/auth/manager.py:281 +#, python-format +msgid "Failed authorization: user %s not admin and not member of project %s" +msgstr "" + +#: nova/auth/manager.py:283 +#, python-format +msgid "User %s is not a member of project %s" +msgstr "" + +#: nova/auth/manager.py:292 nova/auth/manager.py:303 +#, python-format +msgid "Invalid signature for user %s" +msgstr "" + +#: nova/auth/manager.py:293 nova/auth/manager.py:304 +msgid "Signature does not match" +msgstr "" + +#: nova/auth/manager.py:374 +msgid "Must specify project" +msgstr "" + +#: nova/auth/manager.py:408 +#, python-format +msgid "The %s role can not be found" +msgstr "" + +#: nova/auth/manager.py:410 +#, python-format +msgid "The %s role is global only" +msgstr "" + +#: nova/auth/manager.py:412 +#, python-format +msgid "Adding role %s to user %s in project %s" +msgstr "" + +#: nova/auth/manager.py:438 +#, python-format +msgid "Removing role %s from user %s on project %s" +msgstr "" + +#: nova/auth/manager.py:505 +#, python-format +msgid "Created project %s with manager %s" +msgstr "" + +#: nova/auth/manager.py:523 +#, python-format +msgid "modifying project %s" +msgstr "" + +#: nova/auth/manager.py:553 +#, python-format +msgid "Remove user %s from project %s" +msgstr "" + +#: nova/auth/manager.py:581 +#, python-format +msgid "Deleting project %s" +msgstr "" + +#: nova/auth/manager.py:637 +#, python-format +msgid "Created user %s (admin: %r)" +msgstr "" + +#: nova/auth/manager.py:645 +#, python-format +msgid "Deleting user %s" +msgstr "" + +#: nova/auth/manager.py:655 +#, python-format +msgid "Access Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:657 +#, python-format +msgid "Secret Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:659 +#, python-format +msgid "Admin status set to %r for user %s" +msgstr "" + +#: nova/auth/manager.py:708 +#, python-format +msgid "No vpn data for project %s" +msgstr "" + +#: nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "" + +#: nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "" + +#: nova/compute/api.py:67 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "" + +#: nova/compute/api.py:73 +#, python-format +msgid "Instance %d has no host" +msgstr "" + +#: nova/compute/api.py:92 +#, python-format +msgid "Quota exceeeded for %s, tried to run %s instances" +msgstr "" + +#: nova/compute/api.py:94 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "" + +#: nova/compute/api.py:109 +msgid "Creating a raw instance" +msgstr "" + +#: nova/compute/api.py:156 +#, python-format +msgid "Going to run %s instances..." +msgstr "" + +#: nova/compute/api.py:180 +#, python-format +msgid "Casting to scheduler for %s/%s's instance %s" +msgstr "" + +#: nova/compute/api.py:279 +#, python-format +msgid "Going to try and terminate %s" +msgstr "" + +#: nova/compute/api.py:283 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "" + +#: nova/compute/api.py:288 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "" + +#: nova/compute/api.py:450 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "" + +#: nova/compute/api.py:465 +msgid "Volume isn't attached to anything!" +msgstr "" + +#: nova/compute/disk.py:71 +#, python-format +msgid "Input partition size not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:75 +#, python-format +msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "" + +#: nova/compute/disk.py:136 +#, python-format +msgid "Failed to load partition: %s" +msgstr "" + +#: nova/compute/disk.py:158 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "" + +#: nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "" + +#: nova/compute/manager.py:69 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "" + +#: nova/compute/manager.py:71 +#, python-format +msgid "check_instance_lock: arguments: |%s| |%s| |%s|" +msgstr "" + +#: nova/compute/manager.py:75 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "" + +#: nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "" + +#: nova/compute/manager.py:82 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "" + +#: nova/compute/manager.py:86 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "" + +#: nova/compute/manager.py:157 +msgid "Instance has already been created" +msgstr "" + +#: nova/compute/manager.py:158 +#, python-format +msgid "instance %s: starting..." +msgstr "" + +#: nova/compute/manager.py:197 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "" + +#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 +#, python-format +msgid "Terminating instance %s" +msgstr "" + +#: nova/compute/manager.py:217 +#, python-format +msgid "Disassociating address %s" +msgstr "" + +#: nova/compute/manager.py:230 +#, python-format +msgid "Deallocating address %s" +msgstr "" + +#: nova/compute/manager.py:243 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "" + +#: nova/compute/manager.py:257 +#, python-format +msgid "Rebooting instance %s" +msgstr "" + +#: nova/compute/manager.py:260 +#, python-format +msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:286 +#, python-format +msgid "instance %s: snapshotting" +msgstr "" + +#: nova/compute/manager.py:289 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:301 +#, python-format +msgid "instance %s: rescuing" +msgstr "" + +#: nova/compute/manager.py:316 +#, python-format +msgid "instance %s: unrescuing" +msgstr "" + +#: nova/compute/manager.py:335 +#, python-format +msgid "instance %s: pausing" +msgstr "" + +#: nova/compute/manager.py:352 +#, python-format +msgid "instance %s: unpausing" +msgstr "" + +#: nova/compute/manager.py:369 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "" + +#: nova/compute/manager.py:382 +#, python-format +msgid "instance %s: suspending" +msgstr "" + +#: nova/compute/manager.py:401 +#, python-format +msgid "instance %s: resuming" +msgstr "" + +#: nova/compute/manager.py:420 +#, python-format +msgid "instance %s: locking" +msgstr "" + +#: nova/compute/manager.py:432 +#, python-format +msgid "instance %s: unlocking" +msgstr "" + +#: nova/compute/manager.py:442 +#, python-format +msgid "instance %s: getting locked state" +msgstr "" + +#: nova/compute/manager.py:462 +#, python-format +msgid "instance %s: attaching volume %s to %s" +msgstr "" + +#: nova/compute/manager.py:478 +#, python-format +msgid "instance %s: attach failed %s, removing" +msgstr "" + +#: nova/compute/manager.py:493 +#, python-format +msgid "Detach volume %s from mountpoint %s on instance %s" +msgstr "" + +#: nova/compute/manager.py:497 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "" + +#: nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "" + +#: nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "" + +#: nova/compute/monitor.py:355 +#, python-format +msgid "Cannot get blockstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:377 +#, python-format +msgid "Cannot get ifstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:412 +msgid "unexpected exception getting connection" +msgstr "" + +#: nova/compute/monitor.py:427 +#, python-format +msgid "Found instance: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:43 +msgid "Use of empty request context is deprecated" +msgstr "" + +#: nova/db/sqlalchemy/api.py:132 +#, python-format +msgid "No service for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:229 +#, python-format +msgid "No service for %s, %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:574 +#, python-format +msgid "No floating ip for address %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:668 +#, python-format +msgid "No instance for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 +#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 +#, python-format +msgid "Instance %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:891 +#, python-format +msgid "no keypair for user %s, name %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 +#, python-format +msgid "No network for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1036 +#, python-format +msgid "No network for bridge %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1050 +#, python-format +msgid "No network for instance %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1180 +#, python-format +msgid "Token %s does not exist" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1205 +#, python-format +msgid "No quota for project_id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1356 +#, python-format +msgid "No volume for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1401 +#, python-format +msgid "Volume %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1413 +#, python-format +msgid "No export device found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1426 +#, python-format +msgid "No target id found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1471 +#, python-format +msgid "No security group with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1488 +#, python-format +msgid "No security group named %s for project: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1576 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1650 +#, python-format +msgid "No user for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1666 +#, python-format +msgid "No user for access key %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1728 +#, python-format +msgid "No project with id %s" +msgstr "" + +#: nova/image/glance.py:78 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images" +msgstr "" + +#: nova/image/glance.py:97 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images/detail" +msgstr "" + +#: nova/image/s3.py:82 +#, python-format +msgid "Image %s could not be found" +msgstr "" + +#: nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "" + +#: nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "" + +#: nova/network/linux_net.py:176 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "" + +#: nova/network/linux_net.py:186 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "" + +#: nova/network/linux_net.py:254 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "" + +#: nova/network/linux_net.py:256 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "" + +#: nova/network/linux_net.py:334 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "" + +#: nova/network/manager.py:135 +msgid "setting network host" +msgstr "" + +#: nova/network/manager.py:190 +#, python-format +msgid "Leasing IP %s" +msgstr "" + +#: nova/network/manager.py:194 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "" + +#: nova/network/manager.py:197 +#, python-format +msgid "IP %s leased to bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:205 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "" + +#: nova/network/manager.py:214 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "" + +#: nova/network/manager.py:217 +#, python-format +msgid "IP %s released from bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:220 +#, python-format +msgid "IP %s released that was not leased" +msgstr "" + +#: nova/network/manager.py:442 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "" + +#: nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "" + +#: nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "" + +#: nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "" + +#: nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:271 +#, python-format +msgid "Getting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:274 +#, python-format +msgid "Unauthorized attempt to get object %s from bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:292 +#, python-format +msgid "Putting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:295 +#, python-format +msgid "Unauthorized attempt to upload object %s to bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:314 +#, python-format +msgid "Deleting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:393 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "" + +#: nova/objectstore/handler.py:401 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:406 +#, python-format +msgid "Starting image upload: %s" +msgstr "" + +#: nova/objectstore/handler.py:420 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "" + +#: nova/objectstore/handler.py:428 +#, python-format +msgid "Toggling publicity flag of image %s %r" +msgstr "" + +#: nova/objectstore/handler.py:433 +#, python-format +msgid "Updating user fields on image %s" +msgstr "" + +#: nova/objectstore/handler.py:447 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "" + +#: nova/objectstore/handler.py:452 +#, python-format +msgid "Deleted image: %s" +msgstr "" + +#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 +#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 +msgid "No hosts found" +msgstr "" + +#: nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "" + +#: nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %s %s for %s" +msgstr "" + +#: nova/scheduler/simple.py:63 +msgid "All hosts have too many cores" +msgstr "" + +#: nova/scheduler/simple.py:95 +msgid "All hosts have too many gigabytes" +msgstr "" + +#: nova/scheduler/simple.py:115 +msgid "All hosts have too many networks" +msgstr "" + +#: nova/tests/test_cloud.py:198 +msgid "Can't test instances without a real virtual env." +msgstr "" + +#: nova/tests/test_cloud.py:210 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "" + +#: nova/tests/test_compute.py:104 +#, python-format +msgid "Running instances: %s" +msgstr "" + +#: nova/tests/test_compute.py:110 +#, python-format +msgid "After terminating instances: %s" +msgstr "" + +#: nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %s, %s" +msgstr "" + +#: nova/tests/test_rpc.py:94 +#, python-format +msgid "Nested return %s" +msgstr "" + +#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 +#, python-format +msgid "Received %s" +msgstr "" + +#: nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "" + +#: nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "" + +#: nova/virt/fake.py:210 +#, python-format +msgid "Instance %s Not Found" +msgstr "" + +#: nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "" + +#: nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "" + +#: nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "" + +#: nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "" + +#: nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "" + +#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 +#, python-format +msgid "Created VM %s..." +msgstr "" + +#: nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %s by attaching disk file %s" +msgstr "" + +#: nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "" + +#: nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "" + +#: nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "" + +#: nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "" + +#: nova/virt/hyperv.py:275 +#, python-format +msgid "Created switch port %s on switch %s" +msgstr "" + +#: nova/virt/hyperv.py:285 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:287 +#, python-format +msgid "Created nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:320 +#, python-format +msgid "WMI job failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:322 +#, python-format +msgid "WMI job succeeded: %s, Elapsed=%s " +msgstr "" + +#: nova/virt/hyperv.py:358 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:383 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:389 +#, python-format +msgid "Del: disk %s vm %s" +msgstr "" + +#: nova/virt/hyperv.py:405 +#, python-format +msgid "" +"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " +"cpu_time=%s" +msgstr "" + +#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 +#, python-format +msgid "duplicate name found: %s" +msgstr "" + +#: nova/virt/hyperv.py:444 +#, python-format +msgid "Successfully changed vm state of %s to %s" +msgstr "" + +#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 +#, python-format +msgid "Failed to change vm state of %s to %s" +msgstr "" + +#: nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %s -- placed in %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:144 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:157 +msgid "Connection to libvirt broke" +msgstr "" + +#: nova/virt/libvirt_conn.py:229 +#, python-format +msgid "instance %s: deleting instance files %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:271 +#, python-format +msgid "No disk at %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:278 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "" + +#: nova/virt/libvirt_conn.py:294 +#, python-format +msgid "instance %s: rebooted" +msgstr "" + +#: nova/virt/libvirt_conn.py:297 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:340 +#, python-format +msgid "instance %s: rescued" +msgstr "" + +#: nova/virt/libvirt_conn.py:343 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:370 +#, python-format +msgid "instance %s: is running" +msgstr "" + +#: nova/virt/libvirt_conn.py:381 +#, python-format +msgid "instance %s: booted" +msgstr "" + +#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 +#, python-format +msgid "instance %s: failed to boot" +msgstr "" + +#: nova/virt/libvirt_conn.py:395 +#, python-format +msgid "virsh said: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:399 +msgid "cool, it's a device" +msgstr "" + +#: nova/virt/libvirt_conn.py:407 +#, python-format +msgid "data: %r, fpath: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:415 +#, python-format +msgid "Contents of file %s: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:449 +#, python-format +msgid "instance %s: Creating image" +msgstr "" + +#: nova/virt/libvirt_conn.py:505 +#, python-format +msgid "instance %s: injecting key into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:508 +#, python-format +msgid "instance %s: injecting net into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:516 +#, python-format +msgid "instance %s: ignoring error injecting data into image %s (%s)" +msgstr "" + +#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "" + +#: nova/virt/libvirt_conn.py:589 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "" + +#: nova/virt/xenapi_conn.py:113 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" + +#: nova/virt/xenapi_conn.py:263 +#, python-format +msgid "Task [%s] %s status: success %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:271 +#, python-format +msgid "Task [%s] %s status: %s %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 +#, python-format +msgid "Got exception: %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:72 +#, python-format +msgid "%s: _db_content => %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 +#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 +msgid "Raising NotImplemented" +msgstr "" + +#: nova/virt/xenapi/fake.py:249 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:283 +#, python-format +msgid "Calling %s %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:288 +#, python-format +msgid "Calling getter %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:340 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:127 +#, python-format +msgid "Created VM %s as %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:147 +#, python-format +msgid "Creating VBD for VM %s, VDI %s ... " +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:149 +#, python-format +msgid "Created VBD %s for VM %s, VDI %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:165 +#, python-format +msgid "VBD not found in instance %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:175 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:202 +#, python-format +msgid "Creating VIF for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:205 +#, python-format +msgid "Created VIF %s for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:216 +#, python-format +msgid "Snapshotting VM %s with label '%s'..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:229 +#, python-format +msgid "Created snapshot %s from VM %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:243 +#, python-format +msgid "Asking xapi to upload %s as '%s'" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:261 +#, python-format +msgid "Asking xapi to fetch %s as %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:279 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:290 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:318 +#, python-format +msgid "VDI %s is still available" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:331 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:333 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:390 +#, python-format +msgid "VHD %s has parent %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:407 +#, python-format +msgid "Re-scanning SR %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:431 +#, python-format +msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:448 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:452 +#, python-format +msgid "Unexpected number of VDIs (%s) found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:62 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:99 +#, python-format +msgid "Starting VM %s..." +msgstr "" + +#: nova/virt/xenapi/vmops.py:101 +#, python-format +msgid "Spawning VM %s created %s." +msgstr "" + +#: nova/virt/xenapi/vmops.py:112 +#, python-format +msgid "Instance %s: booted" +msgstr "" + +#: nova/virt/xenapi/vmops.py:137 +#, python-format +msgid "Instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:166 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:174 +#, python-format +msgid "Unable to Snapshot %s: %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:184 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:252 +#, python-format +msgid "suspend: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "resume: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:271 +#, python-format +msgid "Instance not found %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %s as %s." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %s when getting PBDs for %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %s when unplugging PBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %s when forgetting SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %s, %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %s, %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:81 +#, python-format +msgid "Unable to use SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:95 +#, python-format +msgid "Mountpoint %s attached to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:106 +#, python-format +msgid "Detach_volume: %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:113 +#, python-format +msgid "Unable to locate volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:121 +#, python-format +msgid "Unable to detach volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:128 +#, python-format +msgid "Mountpoint %s detached from instance %s" +msgstr "" + +#: nova/volume/api.py:44 +#, python-format +msgid "Quota exceeeded for %s, tried to create %sG volume" +msgstr "" + +#: nova/volume/api.py:46 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %s" +msgstr "" + +#: nova/volume/api.py:70 nova/volume/api.py:95 +msgid "Volume status must be available" +msgstr "" + +#: nova/volume/api.py:97 +msgid "Volume is already attached" +msgstr "" + +#: nova/volume/api.py:103 +msgid "Volume is already detached" +msgstr "" + +#: nova/volume/driver.py:76 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "" + +#: nova/volume/driver.py:85 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "" + +#: nova/volume/driver.py:210 +#, python-format +msgid "FAKE AOE: %s" +msgstr "" + +#: nova/volume/driver.py:315 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "" + +#: nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "" + +#: nova/volume/manager.py:93 +#, python-format +msgid "volume %s: creating" +msgstr "" + +#: nova/volume/manager.py:102 +#, python-format +msgid "volume %s: creating lv of size %sG" +msgstr "" + +#: nova/volume/manager.py:106 +#, python-format +msgid "volume %s: creating export" +msgstr "" + +#: nova/volume/manager.py:113 +#, python-format +msgid "volume %s: created successfully" +msgstr "" + +#: nova/volume/manager.py:121 +msgid "Volume is still attached" +msgstr "" + +#: nova/volume/manager.py:123 +msgid "Volume is not local to this node" +msgstr "" + +#: nova/volume/manager.py:124 +#, python-format +msgid "volume %s: removing export" +msgstr "" + +#: nova/volume/manager.py:126 +#, python-format +msgid "volume %s: deleting" +msgstr "" + +#: nova/volume/manager.py:129 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "" diff --git a/locale/zh_CN.po b/locale/zh_CN.po new file mode 100644 index 000000000..4805eb327 --- /dev/null +++ b/locale/zh_CN.po @@ -0,0 +1,2135 @@ +# Chinese (Simplified) translation for nova +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the nova package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: nova\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-10 11:25-0800\n" +"PO-Revision-Date: 2011-01-22 03:11+0000\n" +"Last-Translator: combo \n" +"Language-Team: Chinese (Simplified) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-01-28 05:21+0000\n" +"X-Generator: Launchpad (build 12177)\n" + +#: nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "正在启动 %s" + +#: nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "根证书文件名" + +#: nova/crypto.py:49 +msgid "Filename of private key" +msgstr "私钥文件名" + +#: nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "" + +#: nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "保存密钥的位置" + +#: nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "保存根证书的位置" + +#: nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "是否所有项目都是用证书授权(CA)?" + +#: nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "用户证书的标题,%s依次分别为项目,用户,时间戳" + +#: nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "项目证书的标题,%s依次分别为项目,时间戳" + +#: nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "VPN证书的标题,%s依次分别为项目,时间戳" + +#: nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "Flag所在路径:%s" + +#: nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "运行命令时出现了意外错误。" + +#: nova/exception.py:36 +#, python-format +msgid "" +"%s\n" +"Command: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" +msgstr "" +"%s\n" +"命令:%s\n" +"退出代码:%s\n" +"标准输出(stdout):%r\n" +"标准错误(stderr):%r" + +#: nova/exception.py:86 +msgid "Uncaught exception" +msgstr "未捕获异常" + +#: nova/fakerabbit.py:48 +#, python-format +msgid "(%s) publish (key: %s) %s" +msgstr "(%s)发布(键值:%s)%s" + +#: nova/fakerabbit.py:53 +#, python-format +msgid "Publishing to route %s" +msgstr "发布并路由到 %s" + +#: nova/fakerabbit.py:83 +#, python-format +msgid "Declaring queue %s" +msgstr "正在声明队列%s" + +#: nova/fakerabbit.py:89 +#, python-format +msgid "Declaring exchange %s" +msgstr "正在声明交换(exchange)%s" + +#: nova/fakerabbit.py:95 +#, python-format +msgid "Binding %s to %s with key %s" +msgstr "将%s绑定到%s(以%s键值)" + +#: nova/fakerabbit.py:120 +#, python-format +msgid "Getting from %s: %s" +msgstr "从%s获得如下内容:%s" + +#: nova/rpc.py:92 +#, python-format +msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." +msgstr "位于%s:%d的AMQP服务器不可用。%d秒后重试。" + +#: nova/rpc.py:99 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "已尝试%d次,均无法连接到AMQP服务器。关闭中。" + +#: nova/rpc.py:118 +msgid "Reconnected to queue" +msgstr "重新与队列建立连接" + +#: nova/rpc.py:125 +msgid "Failed to fetch message from queue" +msgstr "从队列获取数据失败" + +#: nova/rpc.py:155 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "" + +#: nova/rpc.py:170 +#, python-format +msgid "received %s" +msgstr "已接收 %s" + +#: nova/rpc.py:183 +#, python-format +msgid "no method for message: %s" +msgstr "没有适用于消息%s的方法" + +#: nova/rpc.py:184 +#, python-format +msgid "No method for message: %s" +msgstr "没有适用于消息%s的方法" + +#: nova/rpc.py:245 +#, python-format +msgid "Returning exception %s to caller" +msgstr "返回%s异常给调用者" + +#: nova/rpc.py:286 +#, python-format +msgid "unpacked context: %s" +msgstr "" + +#: nova/rpc.py:305 +msgid "Making asynchronous call..." +msgstr "产生异步调用中……" + +#: nova/rpc.py:308 +#, python-format +msgid "MSG_ID is %s" +msgstr "消息ID(MSG_ID)是 %s" + +#: nova/rpc.py:356 +#, python-format +msgid "response %s" +msgstr "回复 %s" + +#: nova/rpc.py:365 +#, python-format +msgid "topic is %s" +msgstr "话题是 %s" + +#: nova/rpc.py:366 +#, python-format +msgid "message %s" +msgstr "消息 %s" + +#: nova/service.py:157 +#, python-format +msgid "Starting %s node" +msgstr "启动%s节点" + +#: nova/service.py:169 +msgid "Service killed that has no database entry" +msgstr "因无数据库记录,服务已被中止" + +#: nova/service.py:190 +msgid "The service database object disappeared, Recreating it." +msgstr "" + +#: nova/service.py:202 +msgid "Recovered model server connection!" +msgstr "与模型服务器(model server)的连接已恢复!" + +#: nova/service.py:208 +msgid "model server went away" +msgstr "失去与模型服务器的连接" + +#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 +#, python-format +msgid "Data store %s is unreachable. Trying again in %d seconds." +msgstr "数据储存服务%s不可用。%d秒之后继续尝试。" + +#: nova/service.py:232 nova/twistd.py:232 +#, python-format +msgid "Serving %s" +msgstr "正在为%s服务" + +#: nova/service.py:234 nova/twistd.py:264 +msgid "Full set of FLAGS:" +msgstr "FLAGS全集:" + +#: nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "pidfile %s不存在。后台服务没有运行?\n" + +#: nova/utils.py:53 +#, python-format +msgid "Inner Exception: %s" +msgstr "内层异常:%s" + +#: nova/utils.py:54 +#, python-format +msgid "Class %s cannot be found" +msgstr "无法找到%s类" + +#: nova/utils.py:113 +#, python-format +msgid "Fetching %s" +msgstr "正在抓取%s" + +#: nova/utils.py:125 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "正在运行(在子进程中)运行命令:%s" + +#: nova/utils.py:138 +#, python-format +msgid "Result was %s" +msgstr "运行结果为 %s" + +#: nova/utils.py:171 +#, python-format +msgid "debug in callback: %s" +msgstr "回调中debug:%s" + +#: nova/utils.py:176 +#, python-format +msgid "Running %s" +msgstr "正在运行 %s" + +#: nova/utils.py:207 +#, python-format +msgid "Couldn't get IP, using 127.0.0.1 %s" +msgstr "不能获取IP,将使用 127.0.0.1 %s" + +#: nova/utils.py:289 +#, python-format +msgid "Invalid backend: %s" +msgstr "无效的后台:%s" + +#: nova/utils.py:300 +#, python-format +msgid "backend %s" +msgstr "后台 %s" + +#: nova/api/ec2/__init__.py:133 +msgid "Too many failed authentications." +msgstr "较多失败的认证" + +#: nova/api/ec2/__init__.py:142 +#, python-format +msgid "" +"Access key %s has had %d failed authentications and will be locked out for " +"%d minutes." +msgstr "访问键 %s时,存在%d个失败的认证,将于%d分钟后解锁" + +#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "认证失败:%s" + +#: nova/api/ec2/__init__.py:190 +#, python-format +msgid "Authenticated Request For %s:%s)" +msgstr "为%s:%s申请认证" + +#: nova/api/ec2/__init__.py:227 +#, python-format +msgid "action: %s" +msgstr "执行: %s" + +#: nova/api/ec2/__init__.py:229 +#, python-format +msgid "arg: %s\t\tval: %s" +msgstr "键为: %s\t\t值为: %s" + +#: nova/api/ec2/__init__.py:301 +#, python-format +msgid "Unauthorized request for controller=%s and action=%s" +msgstr "对于控制器=%s和执行=%s的请求,未审核" + +#: nova/api/ec2/__init__.py:339 +#, python-format +msgid "NotFound raised: %s" +msgstr "引起没有找到的错误: %s" + +#: nova/api/ec2/__init__.py:342 +#, python-format +msgid "ApiError raised: %s" +msgstr "引发了Api错误: %s" + +#: nova/api/ec2/__init__.py:349 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "引发了未知的错误: %s" + +#: nova/api/ec2/__init__.py:354 +msgid "An unknown error has occurred. Please try your request again." +msgstr "发生了一个未知的错误. 请重试你的请求." + +#: nova/api/ec2/admin.py:84 +#, python-format +msgid "Creating new user: %s" +msgstr "创建新用户: %s" + +#: nova/api/ec2/admin.py:92 +#, python-format +msgid "Deleting user: %s" +msgstr "删除用户: %s" + +#: nova/api/ec2/admin.py:114 +#, python-format +msgid "Adding role %s to user %s for project %s" +msgstr "增加角色 %s给用户 %s,在工程 %s中" + +#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 +#, python-format +msgid "Adding sitewide role %s to user %s" +msgstr "增加站点范围的 %s角色给用户 %s" + +#: nova/api/ec2/admin.py:122 +#, python-format +msgid "Removing role %s from user %s for project %s" +msgstr "移除角色 %s从用户 %s中,在工程 %s" + +#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 +#, python-format +msgid "Removing sitewide role %s from user %s" +msgstr "移除站点范围的 %s角色从用户 %s中" + +#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 +msgid "operation must be add or remove" +msgstr "操作必须为增加或删除" + +#: nova/api/ec2/admin.py:142 +#, python-format +msgid "Getting x509 for user: %s on project: %s" +msgstr "为用户 %s从工程%s中获取 x509" + +#: nova/api/ec2/admin.py:159 +#, python-format +msgid "Create project %s managed by %s" +msgstr "创建工程%s,此工程由%s管理" + +#: nova/api/ec2/admin.py:170 +#, python-format +msgid "Delete project: %s" +msgstr "删除工程%s" + +#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 +#, python-format +msgid "Adding user %s to project %s" +msgstr "增加用户%s到%s工程" + +#: nova/api/ec2/admin.py:188 +#, python-format +msgid "Removing user %s from project %s" +msgstr "移除用户%s从工程%s中" + +#: nova/api/ec2/apirequest.py:95 +#, python-format +msgid "Unsupported API request: controller = %s,action = %s" +msgstr "不支持的API请求: 控制器 = %s,执行 = %s" + +#: nova/api/ec2/cloud.py:117 +#, python-format +msgid "Generating root CA: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:277 +#, python-format +msgid "Create key pair %s" +msgstr "创建键值对 %s" + +#: nova/api/ec2/cloud.py:285 +#, python-format +msgid "Delete key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:357 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "" + +#: nova/api/ec2/cloud.py:361 +msgid "Invalid port range" +msgstr "" + +#: nova/api/ec2/cloud.py:392 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 +msgid "No rule for the specified parameters." +msgstr "" + +#: nova/api/ec2/cloud.py:421 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:432 +#, python-format +msgid "This rule already exists in group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:460 +#, python-format +msgid "Create Security Group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:463 +#, python-format +msgid "group %s already exists" +msgstr "" + +#: nova/api/ec2/cloud.py:475 +#, python-format +msgid "Delete security group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 +#, python-format +msgid "Get console output for instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:543 +#, python-format +msgid "Create volume of %s GB" +msgstr "" + +#: nova/api/ec2/cloud.py:567 +#, python-format +msgid "Attach volume %s to instacne %s at %s" +msgstr "" + +#: nova/api/ec2/cloud.py:579 +#, python-format +msgid "Detach volume %s" +msgstr "" + +#: nova/api/ec2/cloud.py:686 +msgid "Allocate address" +msgstr "" + +#: nova/api/ec2/cloud.py:691 +#, python-format +msgid "Release address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:696 +#, python-format +msgid "Associate address %s to instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:703 +#, python-format +msgid "Disassociate address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:730 +msgid "Going to start terminating instances" +msgstr "" + +#: nova/api/ec2/cloud.py:738 +#, python-format +msgid "Reboot instance %r" +msgstr "" + +#: nova/api/ec2/cloud.py:775 +#, python-format +msgid "De-registering image %s" +msgstr "" + +#: nova/api/ec2/cloud.py:783 +#, python-format +msgid "Registered image %s with id %s" +msgstr "" + +#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 +#, python-format +msgid "attribute not supported: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:794 +#, python-format +msgid "invalid id: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:807 +msgid "user or group not specified" +msgstr "" + +#: nova/api/ec2/cloud.py:809 +msgid "only group \"all\" is supported" +msgstr "" + +#: nova/api/ec2/cloud.py:811 +msgid "operation_type must be add or remove" +msgstr "" + +#: nova/api/ec2/cloud.py:812 +#, python-format +msgid "Updating image %s publicity" +msgstr "" + +#: nova/api/ec2/metadatarequesthandler.py:75 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:70 +#, python-format +msgid "Caught error: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:86 +msgid "Including admin operations in API." +msgstr "" + +#: nova/api/openstack/servers.py:184 +#, python-format +msgid "Compute.api::lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:199 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "" + +#: nova/api/openstack/servers.py:213 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:224 +#, python-format +msgid "Compute.api::pause %s" +msgstr "" + +#: nova/api/openstack/servers.py:235 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "" + +#: nova/api/openstack/servers.py:246 +#, python-format +msgid "compute.api::suspend %s" +msgstr "" + +#: nova/api/openstack/servers.py:257 +#, python-format +msgid "compute.api::resume %s" +msgstr "" + +#: nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "" + +#: nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "" + +#: nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "" + +#: nova/auth/ldapdriver.py:181 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:218 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:478 +#, python-format +msgid "User %s is already a member of the group %s" +msgstr "" + +#: nova/auth/ldapdriver.py:507 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "" + +#: nova/auth/ldapdriver.py:528 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "" + +#: nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "" + +#: nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "" + +#: nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "" + +#: nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "" + +#: nova/auth/manager.py:275 +#, python-format +msgid "failed authorization: no project named %s (user=%s)" +msgstr "" + +#: nova/auth/manager.py:277 +#, python-format +msgid "No project called %s could be found" +msgstr "" + +#: nova/auth/manager.py:281 +#, python-format +msgid "Failed authorization: user %s not admin and not member of project %s" +msgstr "" + +#: nova/auth/manager.py:283 +#, python-format +msgid "User %s is not a member of project %s" +msgstr "" + +#: nova/auth/manager.py:292 nova/auth/manager.py:303 +#, python-format +msgid "Invalid signature for user %s" +msgstr "" + +#: nova/auth/manager.py:293 nova/auth/manager.py:304 +msgid "Signature does not match" +msgstr "" + +#: nova/auth/manager.py:374 +msgid "Must specify project" +msgstr "" + +#: nova/auth/manager.py:408 +#, python-format +msgid "The %s role can not be found" +msgstr "" + +#: nova/auth/manager.py:410 +#, python-format +msgid "The %s role is global only" +msgstr "" + +#: nova/auth/manager.py:412 +#, python-format +msgid "Adding role %s to user %s in project %s" +msgstr "" + +#: nova/auth/manager.py:438 +#, python-format +msgid "Removing role %s from user %s on project %s" +msgstr "" + +#: nova/auth/manager.py:505 +#, python-format +msgid "Created project %s with manager %s" +msgstr "" + +#: nova/auth/manager.py:523 +#, python-format +msgid "modifying project %s" +msgstr "" + +#: nova/auth/manager.py:553 +#, python-format +msgid "Remove user %s from project %s" +msgstr "" + +#: nova/auth/manager.py:581 +#, python-format +msgid "Deleting project %s" +msgstr "" + +#: nova/auth/manager.py:637 +#, python-format +msgid "Created user %s (admin: %r)" +msgstr "" + +#: nova/auth/manager.py:645 +#, python-format +msgid "Deleting user %s" +msgstr "" + +#: nova/auth/manager.py:655 +#, python-format +msgid "Access Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:657 +#, python-format +msgid "Secret Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:659 +#, python-format +msgid "Admin status set to %r for user %s" +msgstr "" + +#: nova/auth/manager.py:708 +#, python-format +msgid "No vpn data for project %s" +msgstr "" + +#: nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "" + +#: nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "" + +#: nova/compute/api.py:67 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "" + +#: nova/compute/api.py:73 +#, python-format +msgid "Instance %d has no host" +msgstr "" + +#: nova/compute/api.py:92 +#, python-format +msgid "Quota exceeeded for %s, tried to run %s instances" +msgstr "" + +#: nova/compute/api.py:94 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "" + +#: nova/compute/api.py:109 +msgid "Creating a raw instance" +msgstr "" + +#: nova/compute/api.py:156 +#, python-format +msgid "Going to run %s instances..." +msgstr "" + +#: nova/compute/api.py:180 +#, python-format +msgid "Casting to scheduler for %s/%s's instance %s" +msgstr "" + +#: nova/compute/api.py:279 +#, python-format +msgid "Going to try and terminate %s" +msgstr "" + +#: nova/compute/api.py:283 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "" + +#: nova/compute/api.py:288 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "" + +#: nova/compute/api.py:450 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "" + +#: nova/compute/api.py:465 +msgid "Volume isn't attached to anything!" +msgstr "" + +#: nova/compute/disk.py:71 +#, python-format +msgid "Input partition size not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:75 +#, python-format +msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "" + +#: nova/compute/disk.py:136 +#, python-format +msgid "Failed to load partition: %s" +msgstr "" + +#: nova/compute/disk.py:158 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "" + +#: nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "" + +#: nova/compute/manager.py:69 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "" + +#: nova/compute/manager.py:71 +#, python-format +msgid "check_instance_lock: arguments: |%s| |%s| |%s|" +msgstr "" + +#: nova/compute/manager.py:75 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "" + +#: nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "" + +#: nova/compute/manager.py:82 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "" + +#: nova/compute/manager.py:86 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "" + +#: nova/compute/manager.py:157 +msgid "Instance has already been created" +msgstr "" + +#: nova/compute/manager.py:158 +#, python-format +msgid "instance %s: starting..." +msgstr "" + +#: nova/compute/manager.py:197 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "" + +#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 +#, python-format +msgid "Terminating instance %s" +msgstr "" + +#: nova/compute/manager.py:217 +#, python-format +msgid "Disassociating address %s" +msgstr "" + +#: nova/compute/manager.py:230 +#, python-format +msgid "Deallocating address %s" +msgstr "" + +#: nova/compute/manager.py:243 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "" + +#: nova/compute/manager.py:257 +#, python-format +msgid "Rebooting instance %s" +msgstr "" + +#: nova/compute/manager.py:260 +#, python-format +msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:286 +#, python-format +msgid "instance %s: snapshotting" +msgstr "" + +#: nova/compute/manager.py:289 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:301 +#, python-format +msgid "instance %s: rescuing" +msgstr "" + +#: nova/compute/manager.py:316 +#, python-format +msgid "instance %s: unrescuing" +msgstr "" + +#: nova/compute/manager.py:335 +#, python-format +msgid "instance %s: pausing" +msgstr "" + +#: nova/compute/manager.py:352 +#, python-format +msgid "instance %s: unpausing" +msgstr "" + +#: nova/compute/manager.py:369 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "" + +#: nova/compute/manager.py:382 +#, python-format +msgid "instance %s: suspending" +msgstr "" + +#: nova/compute/manager.py:401 +#, python-format +msgid "instance %s: resuming" +msgstr "" + +#: nova/compute/manager.py:420 +#, python-format +msgid "instance %s: locking" +msgstr "" + +#: nova/compute/manager.py:432 +#, python-format +msgid "instance %s: unlocking" +msgstr "" + +#: nova/compute/manager.py:442 +#, python-format +msgid "instance %s: getting locked state" +msgstr "" + +#: nova/compute/manager.py:462 +#, python-format +msgid "instance %s: attaching volume %s to %s" +msgstr "" + +#: nova/compute/manager.py:478 +#, python-format +msgid "instance %s: attach failed %s, removing" +msgstr "" + +#: nova/compute/manager.py:493 +#, python-format +msgid "Detach volume %s from mountpoint %s on instance %s" +msgstr "" + +#: nova/compute/manager.py:497 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "" + +#: nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "" + +#: nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "" + +#: nova/compute/monitor.py:355 +#, python-format +msgid "Cannot get blockstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:377 +#, python-format +msgid "Cannot get ifstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:412 +msgid "unexpected exception getting connection" +msgstr "" + +#: nova/compute/monitor.py:427 +#, python-format +msgid "Found instance: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:43 +msgid "Use of empty request context is deprecated" +msgstr "" + +#: nova/db/sqlalchemy/api.py:132 +#, python-format +msgid "No service for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:229 +#, python-format +msgid "No service for %s, %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:574 +#, python-format +msgid "No floating ip for address %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:668 +#, python-format +msgid "No instance for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 +#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 +#, python-format +msgid "Instance %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:891 +#, python-format +msgid "no keypair for user %s, name %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 +#, python-format +msgid "No network for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1036 +#, python-format +msgid "No network for bridge %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1050 +#, python-format +msgid "No network for instance %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1180 +#, python-format +msgid "Token %s does not exist" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1205 +#, python-format +msgid "No quota for project_id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1356 +#, python-format +msgid "No volume for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1401 +#, python-format +msgid "Volume %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1413 +#, python-format +msgid "No export device found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1426 +#, python-format +msgid "No target id found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1471 +#, python-format +msgid "No security group with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1488 +#, python-format +msgid "No security group named %s for project: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1576 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1650 +#, python-format +msgid "No user for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1666 +#, python-format +msgid "No user for access key %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1728 +#, python-format +msgid "No project with id %s" +msgstr "" + +#: nova/image/glance.py:78 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images" +msgstr "" + +#: nova/image/glance.py:97 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images/detail" +msgstr "" + +#: nova/image/s3.py:82 +#, python-format +msgid "Image %s could not be found" +msgstr "" + +#: nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "" + +#: nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "" + +#: nova/network/linux_net.py:176 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "" + +#: nova/network/linux_net.py:186 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "" + +#: nova/network/linux_net.py:254 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "" + +#: nova/network/linux_net.py:256 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "" + +#: nova/network/linux_net.py:334 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "" + +#: nova/network/manager.py:135 +msgid "setting network host" +msgstr "" + +#: nova/network/manager.py:190 +#, python-format +msgid "Leasing IP %s" +msgstr "" + +#: nova/network/manager.py:194 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "" + +#: nova/network/manager.py:197 +#, python-format +msgid "IP %s leased to bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:205 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "" + +#: nova/network/manager.py:214 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "" + +#: nova/network/manager.py:217 +#, python-format +msgid "IP %s released from bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:220 +#, python-format +msgid "IP %s released that was not leased" +msgstr "" + +#: nova/network/manager.py:442 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "" + +#: nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "" + +#: nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "" + +#: nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "" + +#: nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:271 +#, python-format +msgid "Getting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:274 +#, python-format +msgid "Unauthorized attempt to get object %s from bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:292 +#, python-format +msgid "Putting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:295 +#, python-format +msgid "Unauthorized attempt to upload object %s to bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:314 +#, python-format +msgid "Deleting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:393 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "" + +#: nova/objectstore/handler.py:401 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:406 +#, python-format +msgid "Starting image upload: %s" +msgstr "" + +#: nova/objectstore/handler.py:420 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "" + +#: nova/objectstore/handler.py:428 +#, python-format +msgid "Toggling publicity flag of image %s %r" +msgstr "" + +#: nova/objectstore/handler.py:433 +#, python-format +msgid "Updating user fields on image %s" +msgstr "" + +#: nova/objectstore/handler.py:447 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "" + +#: nova/objectstore/handler.py:452 +#, python-format +msgid "Deleted image: %s" +msgstr "" + +#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 +#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 +msgid "No hosts found" +msgstr "" + +#: nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "" + +#: nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %s %s for %s" +msgstr "" + +#: nova/scheduler/simple.py:63 +msgid "All hosts have too many cores" +msgstr "" + +#: nova/scheduler/simple.py:95 +msgid "All hosts have too many gigabytes" +msgstr "" + +#: nova/scheduler/simple.py:115 +msgid "All hosts have too many networks" +msgstr "" + +#: nova/tests/test_cloud.py:198 +msgid "Can't test instances without a real virtual env." +msgstr "" + +#: nova/tests/test_cloud.py:210 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "" + +#: nova/tests/test_compute.py:104 +#, python-format +msgid "Running instances: %s" +msgstr "" + +#: nova/tests/test_compute.py:110 +#, python-format +msgid "After terminating instances: %s" +msgstr "" + +#: nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %s, %s" +msgstr "" + +#: nova/tests/test_rpc.py:94 +#, python-format +msgid "Nested return %s" +msgstr "" + +#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 +#, python-format +msgid "Received %s" +msgstr "" + +#: nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "" + +#: nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "" + +#: nova/virt/fake.py:210 +#, python-format +msgid "Instance %s Not Found" +msgstr "" + +#: nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "" + +#: nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "" + +#: nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "" + +#: nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "" + +#: nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "" + +#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 +#, python-format +msgid "Created VM %s..." +msgstr "" + +#: nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %s by attaching disk file %s" +msgstr "" + +#: nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "" + +#: nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "" + +#: nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "" + +#: nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "" + +#: nova/virt/hyperv.py:275 +#, python-format +msgid "Created switch port %s on switch %s" +msgstr "" + +#: nova/virt/hyperv.py:285 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:287 +#, python-format +msgid "Created nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:320 +#, python-format +msgid "WMI job failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:322 +#, python-format +msgid "WMI job succeeded: %s, Elapsed=%s " +msgstr "" + +#: nova/virt/hyperv.py:358 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:383 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:389 +#, python-format +msgid "Del: disk %s vm %s" +msgstr "" + +#: nova/virt/hyperv.py:405 +#, python-format +msgid "" +"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " +"cpu_time=%s" +msgstr "" + +#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 +#, python-format +msgid "duplicate name found: %s" +msgstr "" + +#: nova/virt/hyperv.py:444 +#, python-format +msgid "Successfully changed vm state of %s to %s" +msgstr "" + +#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 +#, python-format +msgid "Failed to change vm state of %s to %s" +msgstr "" + +#: nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %s -- placed in %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:144 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:157 +msgid "Connection to libvirt broke" +msgstr "" + +#: nova/virt/libvirt_conn.py:229 +#, python-format +msgid "instance %s: deleting instance files %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:271 +#, python-format +msgid "No disk at %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:278 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "" + +#: nova/virt/libvirt_conn.py:294 +#, python-format +msgid "instance %s: rebooted" +msgstr "" + +#: nova/virt/libvirt_conn.py:297 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:340 +#, python-format +msgid "instance %s: rescued" +msgstr "" + +#: nova/virt/libvirt_conn.py:343 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:370 +#, python-format +msgid "instance %s: is running" +msgstr "" + +#: nova/virt/libvirt_conn.py:381 +#, python-format +msgid "instance %s: booted" +msgstr "" + +#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 +#, python-format +msgid "instance %s: failed to boot" +msgstr "" + +#: nova/virt/libvirt_conn.py:395 +#, python-format +msgid "virsh said: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:399 +msgid "cool, it's a device" +msgstr "" + +#: nova/virt/libvirt_conn.py:407 +#, python-format +msgid "data: %r, fpath: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:415 +#, python-format +msgid "Contents of file %s: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:449 +#, python-format +msgid "instance %s: Creating image" +msgstr "" + +#: nova/virt/libvirt_conn.py:505 +#, python-format +msgid "instance %s: injecting key into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:508 +#, python-format +msgid "instance %s: injecting net into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:516 +#, python-format +msgid "instance %s: ignoring error injecting data into image %s (%s)" +msgstr "" + +#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "" + +#: nova/virt/libvirt_conn.py:589 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "" + +#: nova/virt/xenapi_conn.py:113 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" + +#: nova/virt/xenapi_conn.py:263 +#, python-format +msgid "Task [%s] %s status: success %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:271 +#, python-format +msgid "Task [%s] %s status: %s %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 +#, python-format +msgid "Got exception: %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:72 +#, python-format +msgid "%s: _db_content => %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 +#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 +msgid "Raising NotImplemented" +msgstr "" + +#: nova/virt/xenapi/fake.py:249 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:283 +#, python-format +msgid "Calling %s %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:288 +#, python-format +msgid "Calling getter %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:340 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:127 +#, python-format +msgid "Created VM %s as %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:147 +#, python-format +msgid "Creating VBD for VM %s, VDI %s ... " +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:149 +#, python-format +msgid "Created VBD %s for VM %s, VDI %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:165 +#, python-format +msgid "VBD not found in instance %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:175 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:202 +#, python-format +msgid "Creating VIF for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:205 +#, python-format +msgid "Created VIF %s for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:216 +#, python-format +msgid "Snapshotting VM %s with label '%s'..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:229 +#, python-format +msgid "Created snapshot %s from VM %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:243 +#, python-format +msgid "Asking xapi to upload %s as '%s'" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:261 +#, python-format +msgid "Asking xapi to fetch %s as %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:279 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:290 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:318 +#, python-format +msgid "VDI %s is still available" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:331 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:333 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:390 +#, python-format +msgid "VHD %s has parent %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:407 +#, python-format +msgid "Re-scanning SR %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:431 +#, python-format +msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:448 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:452 +#, python-format +msgid "Unexpected number of VDIs (%s) found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:62 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:99 +#, python-format +msgid "Starting VM %s..." +msgstr "" + +#: nova/virt/xenapi/vmops.py:101 +#, python-format +msgid "Spawning VM %s created %s." +msgstr "" + +#: nova/virt/xenapi/vmops.py:112 +#, python-format +msgid "Instance %s: booted" +msgstr "" + +#: nova/virt/xenapi/vmops.py:137 +#, python-format +msgid "Instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:166 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:174 +#, python-format +msgid "Unable to Snapshot %s: %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:184 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:252 +#, python-format +msgid "suspend: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "resume: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:271 +#, python-format +msgid "Instance not found %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %s as %s." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %s when getting PBDs for %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %s when unplugging PBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %s when forgetting SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %s, %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %s, %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:81 +#, python-format +msgid "Unable to use SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:95 +#, python-format +msgid "Mountpoint %s attached to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:106 +#, python-format +msgid "Detach_volume: %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:113 +#, python-format +msgid "Unable to locate volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:121 +#, python-format +msgid "Unable to detach volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:128 +#, python-format +msgid "Mountpoint %s detached from instance %s" +msgstr "" + +#: nova/volume/api.py:44 +#, python-format +msgid "Quota exceeeded for %s, tried to create %sG volume" +msgstr "" + +#: nova/volume/api.py:46 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %s" +msgstr "" + +#: nova/volume/api.py:70 nova/volume/api.py:95 +msgid "Volume status must be available" +msgstr "" + +#: nova/volume/api.py:97 +msgid "Volume is already attached" +msgstr "" + +#: nova/volume/api.py:103 +msgid "Volume is already detached" +msgstr "" + +#: nova/volume/driver.py:76 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "" + +#: nova/volume/driver.py:85 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "" + +#: nova/volume/driver.py:210 +#, python-format +msgid "FAKE AOE: %s" +msgstr "" + +#: nova/volume/driver.py:315 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "" + +#: nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "" + +#: nova/volume/manager.py:93 +#, python-format +msgid "volume %s: creating" +msgstr "" + +#: nova/volume/manager.py:102 +#, python-format +msgid "volume %s: creating lv of size %sG" +msgstr "" + +#: nova/volume/manager.py:106 +#, python-format +msgid "volume %s: creating export" +msgstr "" + +#: nova/volume/manager.py:113 +#, python-format +msgid "volume %s: created successfully" +msgstr "" + +#: nova/volume/manager.py:121 +msgid "Volume is still attached" +msgstr "" + +#: nova/volume/manager.py:123 +msgid "Volume is not local to this node" +msgstr "" + +#: nova/volume/manager.py:124 +#, python-format +msgid "volume %s: removing export" +msgstr "" + +#: nova/volume/manager.py:126 +#, python-format +msgid "volume %s: deleting" +msgstr "" + +#: nova/volume/manager.py:129 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "" -- cgit From eabc4c00eea8859c37efed3f180edbc41fd3b71d Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 27 Jan 2011 23:53:28 -0600 Subject: Working on api / manager / db support for zones --- bin/nova-manage | 10 ++++++++++ nova/api/openstack/__init__.py | 5 +++++ nova/db/api.py | 8 ++++++++ nova/db/sqlalchemy/api.py | 9 +++++++++ 4 files changed, 32 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index 7835ca551..b62687aec 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -569,6 +569,15 @@ class DbCommands(object): print migration.db_version() +class ZoneCommands(object): + """Methods for defining zones.""" + + def create(self, name): + """Create a new Zone for this deployment.""" + ctxt = context.get_admin_context() + db.create_zone(ctxt, name) + + class VolumeCommands(object): """Methods for dealing with a cloud in an odd state""" @@ -620,6 +629,7 @@ CATEGORIES = [ ('service', ServiceCommands), ('log', LogCommands), ('db', DbCommands), + ('zone', ZoneCommands), ('volume', VolumeCommands)] diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index c70bb39ed..025fa12a4 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -34,6 +34,7 @@ from nova.api.openstack import flavors from nova.api.openstack import images from nova.api.openstack import servers from nova.api.openstack import shared_ip_groups +from nova.api.openstack import zones LOG = logging.getLogger('nova.api.openstack') @@ -79,6 +80,10 @@ class APIRouter(wsgi.Router): server_members["actions"] = "GET" server_members['suspend'] = 'POST' server_members['resume'] = 'POST' + + mapper.resource("zone", "zones", controller=zones.Controller(), + collection={'detail': 'GET'}, + member=zone_members) mapper.resource("server", "servers", controller=servers.Controller(), collection={'detail': 'GET'}, diff --git a/nova/db/api.py b/nova/db/api.py index 789cb8ebb..dc35f20b2 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -980,3 +980,11 @@ def console_get_all_by_instance(context, instance_id): def console_get(context, console_id, instance_id=None): """Get a specific console (possibly on a given instance).""" return IMPL.console_get(context, console_id, instance_id) + + +#################### + + +def create_zone(context, name): + """Create a new Zone entry for this deployment.""" + return IMPL.create_zone(context, name) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 895e7eabe..ec36c481e 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1996,3 +1996,12 @@ def console_get(context, console_id, instance_id=None): raise exception.NotFound(_("No console with id %(console_id)s" " %(idesc)s") % locals()) return result + + +################## + + +@require_admin_context +def create_zone(context, zone): + session = get_session() + print "Creating Zone", zone -- cgit From f76a0a9d4d8aa3d8cc2669da1a8eea7d610a8616 Mon Sep 17 00:00:00 2001 From: termie Date: Sun, 30 Jan 2011 15:55:48 -0800 Subject: trivial cleanup for context.py --- nova/context.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nova/context.py b/nova/context.py index f2669c9f1..76e46703a 100644 --- a/nova/context.py +++ b/nova/context.py @@ -28,7 +28,6 @@ from nova import utils class RequestContext(object): - def __init__(self, user, project, is_admin=None, read_deleted=False, remote_address=None, timestamp=None, request_id=None): if hasattr(user, 'id'): @@ -53,7 +52,7 @@ class RequestContext(object): self.read_deleted = read_deleted self.remote_address = remote_address if not timestamp: - timestamp = datetime.datetime.utcnow() + timestampe = utils.utcnow() if isinstance(timestamp, str) or isinstance(timestamp, unicode): timestamp = utils.parse_isotime(timestamp) self.timestamp = timestamp @@ -101,7 +100,7 @@ class RequestContext(object): return cls(**values) def elevated(self, read_deleted=False): - """Return a version of this context with admin flag set""" + """Return a version of this context with admin flag set.""" return RequestContext(self.user_id, self.project_id, True, -- cgit From 701c71999a135996575dd76a7171eb707b4d74ef Mon Sep 17 00:00:00 2001 From: termie Date: Sun, 30 Jan 2011 16:04:52 -0800 Subject: woops --- nova/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/context.py b/nova/context.py index 76e46703a..0256bf448 100644 --- a/nova/context.py +++ b/nova/context.py @@ -52,7 +52,7 @@ class RequestContext(object): self.read_deleted = read_deleted self.remote_address = remote_address if not timestamp: - timestampe = utils.utcnow() + timestamp = utils.utcnow() if isinstance(timestamp, str) or isinstance(timestamp, unicode): timestamp = utils.parse_isotime(timestamp) self.timestamp = timestamp -- cgit From 85fe11e11a6332f95a9dac300994e77e7e48dfec Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 31 Jan 2011 14:38:45 -0800 Subject: change ensure_bridge so it doesn't overwrite existing ips --- nova/network/linux_net.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index cdd1f666a..87eefa8b2 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -191,18 +191,19 @@ def ensure_bridge(bridge, interface, net_attrs=None): _execute("sudo brctl stp %s off" % bridge) if interface: _execute("sudo brctl addif %s %s" % (bridge, interface)) + _execute("sudo ifconfig %s up" % bridge) if net_attrs: - _execute("sudo ifconfig %s %s broadcast %s netmask %s up" % \ - (bridge, - net_attrs['gateway'], + # NOTE(vish): use ip addr add so it doesn't overwrite + # manual addresses on the bridge. + suffix = net_attrs['cidr'].rpartition('/')[0] + _execute("sudo ip addr add %s/%s brd %s dev %s" % + (net_attrs['gateway'], + suffix, net_attrs['broadcast'], - net_attrs['netmask'])) + bridge)) if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) - _execute("sudo ifconfig %s up" % bridge) - else: - _execute("sudo ifconfig %s up" % bridge) if FLAGS.use_nova_chains: (out, err) = _execute("sudo iptables -N nova_forward", check_exit_code=False) -- cgit From e4356ceab8b2627dda0b02c7ebbba6d033129360 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 31 Jan 2011 14:43:33 -0800 Subject: rpartition sticks the rhs in [2] --- nova/network/linux_net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 87eefa8b2..db3ecc7f9 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -195,7 +195,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): if net_attrs: # NOTE(vish): use ip addr add so it doesn't overwrite # manual addresses on the bridge. - suffix = net_attrs['cidr'].rpartition('/')[0] + suffix = net_attrs['cidr'].rpartition('/')[2] _execute("sudo ip addr add %s/%s brd %s dev %s" % (net_attrs['gateway'], suffix, -- cgit From c021c985660aa37861b6c01bba9db914f349d13d Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Tue, 1 Feb 2011 05:19:59 +0000 Subject: Launchpad automatic translations update. --- locale/ru.po | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/locale/ru.po b/locale/ru.po index c751f41b2..6a75c1727 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-01-10 11:25-0800\n" -"PO-Revision-Date: 2011-01-25 17:45+0000\n" -"Last-Translator: Ilya Alekseyev \n" +"PO-Revision-Date: 2011-01-31 06:53+0000\n" +"Last-Translator: Andrey Olykainen \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Launchpad-Export-Date: 2011-02-01 05:19+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 @@ -31,7 +31,7 @@ msgstr "" #: nova/crypto.py:53 msgid "Where we keep our keys" -msgstr "" +msgstr "Путь к ключам" #: nova/crypto.py:55 msgid "Where we keep our root CA" @@ -112,7 +112,7 @@ msgstr "" #: nova/fakerabbit.py:120 #, python-format msgid "Getting from %s: %s" -msgstr "" +msgstr "Получение из %s: %s" #: nova/rpc.py:92 #, python-format @@ -174,7 +174,7 @@ msgstr "MSG_ID is %s" #: nova/rpc.py:356 #, python-format msgid "response %s" -msgstr "" +msgstr "ответ %s" #: nova/rpc.py:365 #, python-format @@ -264,7 +264,7 @@ msgstr "" #: nova/utils.py:176 #, python-format msgid "Running %s" -msgstr "" +msgstr "Выполняется %s" #: nova/utils.py:207 #, python-format @@ -291,16 +291,18 @@ msgid "" "Access key %s has had %d failed authentications and will be locked out for " "%d minutes." msgstr "" +"Ключ доступа %s имеет %d неудачных попыток аутентификации и будет " +"заблокирован на %d минут." #: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 #, python-format msgid "Authentication Failure: %s" -msgstr "" +msgstr "Ошибка аутентификации: %s" #: nova/api/ec2/__init__.py:190 #, python-format msgid "Authenticated Request For %s:%s)" -msgstr "" +msgstr "Запрос аутентификации для %s:%s)" #: nova/api/ec2/__init__.py:227 #, python-format @@ -547,7 +549,7 @@ msgstr "" #: nova/api/ec2/metadatarequesthandler.py:75 #, python-format msgid "Failed to get metadata for ip: %s" -msgstr "" +msgstr "Ошибка получения метаданных для ip: %s" #: nova/api/openstack/__init__.py:70 #, python-format -- cgit From 620eba09a96f25a059249c23a5e73efd18aaf89a Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Tue, 1 Feb 2011 14:11:21 -0600 Subject: forgot context param for network_get_all --- nova/virt/xenapi/vmops.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 68fa1ecd6..da2e5c672 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -95,9 +95,10 @@ class VMOps(object): VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True) # write network info - network = db.network_get_by_instance(context.get_admin_context(), + admin_context = context.get_admin_context() + network = db.network_get_by_instance(admin_context, instance['id']) - for network in db.network_get_all(): + for network in db.network_get_all(admin_context): mapping = {'label': network['label'], 'gateway': network['gateway'], 'mac': instance.mac_address, -- cgit From 0e6b1c02b3ae82526f3cf83ce70213e7a107701d Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Tue, 1 Feb 2011 15:41:53 -0600 Subject: added to inject networking data into the xenstore --- nova/virt/xenapi/vmops.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index da2e5c672..6edeae5c0 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -99,14 +99,16 @@ class VMOps(object): network = db.network_get_by_instance(admin_context, instance['id']) for network in db.network_get_all(admin_context): + mac_id = instance.mac_address.replace(':', '') + location = 'vm-data/networking/%s' % mac_id mapping = {'label': network['label'], 'gateway': network['gateway'], 'mac': instance.mac_address, - 'dns': network['dns'], + 'dns': [network['dns']], 'ips': [{'netmask': network['netmask'], 'enabled': '1', 'ip': '192.168.3.3'}]} # <===== CHANGE!!!! - self.write_network_config_to_xenstore(vm_ref, mapping) + self.write_to_param_xenstore(vm_ref, {location: mapping}) bridge = network['bridge'] network_ref = \ @@ -392,10 +394,6 @@ class VMOps(object): args = {'id': str(uuid.uuid4())} resp = self._make_agent_call('resetnetwork', vm, '', args) - def write_network_config_to_xenstore(self, instance, mapping): - vm = self._get_vm_opaque_ref(instance) - self.write_to_param_xenstore(vm, mapping) - def list_from_xenstore(self, vm, path): """Runs the xenstore-ls command to get a listing of all records from 'path' downward. Returns a dict with the sub-paths as keys, -- cgit From 48a0e252be8b001705db98de8143e1c1ad6294ad Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 1 Feb 2011 17:55:14 -0800 Subject: better setup for flatdhcp --- nova/network/linux_net.py | 40 +++++++++++++++++++++++++++++++++------- nova/network/manager.py | 10 ++++++---- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index db3ecc7f9..1a63b759f 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -20,6 +20,7 @@ Implements vlans, bridges, and iptables rules using linux utilities. import os from nova import db +from nova import exception from nova import flags from nova import log as logging from nova import utils @@ -164,7 +165,7 @@ def remove_floating_forward(floating_ip, fixed_ip): % (fixed_ip, floating_ip)) -def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): +def ensure_vlan_bridge(vlan_num, bridge, net_attrs, set_ip=False): """Create a vlan and bridge unless they already exist""" interface = ensure_vlan(vlan_num) ensure_bridge(bridge, interface, net_attrs) @@ -181,7 +182,7 @@ def ensure_vlan(vlan_num): return interface -def ensure_bridge(bridge, interface, net_attrs=None): +def ensure_bridge(bridge, interface, net_attrs, set_ip=False): """Create a bridge unless it already exists""" if not _device_exists(bridge): LOG.debug(_("Starting Bridge interface for %s"), interface) @@ -189,12 +190,10 @@ def ensure_bridge(bridge, interface, net_attrs=None): _execute("sudo brctl setfd %s 0" % bridge) # _execute("sudo brctl setageing %s 10" % bridge) _execute("sudo brctl stp %s off" % bridge) - if interface: - _execute("sudo brctl addif %s %s" % (bridge, interface)) _execute("sudo ifconfig %s up" % bridge) - if net_attrs: - # NOTE(vish): use ip addr add so it doesn't overwrite - # manual addresses on the bridge. + if set_ip: + # NOTE(vish): The ip for dnsmasq has to be the first address on the + # bridge for it to respond to reqests properly suffix = net_attrs['cidr'].rpartition('/')[2] _execute("sudo ip addr add %s/%s brd %s dev %s" % (net_attrs['gateway'], @@ -204,6 +203,33 @@ def ensure_bridge(bridge, interface, net_attrs=None): if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) + else: + # NOTE(vish): if we don't give an ip to the bridge, we set up a route + # for the guests + out, err = _execute("sudo route add -net %s dev %s" % + (net_attrs['cidr'], bridge), + check_exit_code=False) + if err and err != "SIOCADDRT: File exists\n": + raise exception.Error("Failed to add route: %s" % err) + if interface: + # NOTE(vish): This will break if there is already an ip on the + # interface, so we move any ips to the bridge + out, err = _execute("sudo ip addr show dev %s scope global" % + interface) + for line in out.split("\n"): + fields = line.split() + if fields and fields[0] == "inet": + params = ' '.join(fields[1:-2]) + _execute("sudo ip addr del %s dev %s" % (params, fields[-1])) + _execute("sudo ip addr add %s dev %s" % (params, bridge)) + out, err = _execute("sudo brctl addif %s %s" % + (bridge, interface), + check_exit_code=False) + + if (err and err != "device %s is already a member of a bridge; can't " + "enslave it to bridge %s.\n" % (interface, bridge)): + raise exception.Error("Failed to add interface: %s" % err) + if FLAGS.use_nova_chains: (out, err) = _execute("sudo iptables -N nova_forward", check_exit_code=False) diff --git a/nova/network/manager.py b/nova/network/manager.py index fbcbea131..735327230 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -402,7 +402,8 @@ class FlatDHCPManager(FlatManager): """Sets up matching network for compute hosts.""" network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_bridge(network_ref['bridge'], - FLAGS.flat_interface) + FLAGS.flat_interface, + network_ref) def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Setup dhcp for this network.""" @@ -427,7 +428,7 @@ class FlatDHCPManager(FlatManager): network_ref = db.network_get(context, network_id) self.driver.ensure_bridge(network_ref['bridge'], FLAGS.flat_interface, - network_ref) + network_ref, True) if not FLAGS.fake_network: self.driver.update_dhcp(context, network_id) if(FLAGS.use_ipv6): @@ -498,7 +499,8 @@ class VlanManager(NetworkManager): """Sets up matching network for compute hosts.""" network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_vlan_bridge(network_ref['vlan'], - network_ref['bridge']) + network_ref['bridge'], + network_ref) def create_networks(self, context, cidr, num_networks, network_size, cidr_v6, vlan_start, vpn_start): @@ -565,7 +567,7 @@ class VlanManager(NetworkManager): address = network_ref['vpn_public_address'] self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge'], - network_ref) + network_ref, True) # NOTE(vish): only ensure this forward if the address hasn't been set # manually. -- cgit From a776844e38c7e747397785a6ce6b1de1b043d850 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Tue, 1 Feb 2011 18:34:46 -0800 Subject: initial support for dynamic instance_types: db migration and model, stub tests and stub methods. --- bin/nova-manage | 41 +++++++++++- nova/compute/instance_types.py | 12 ++++ .../sqlalchemy/migrate_repo/versions/003_cactus.py | 76 ++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 10 +++ nova/tests/api/openstack/test_flavors.py | 10 +++ 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py diff --git a/bin/nova-manage b/bin/nova-manage index 1b70ebf17..952bf4fd1 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -609,6 +609,44 @@ class VolumeCommands(object): "mountpoint": volume['mountpoint']}}) +class InstanceTypesCommands(object): + """Class for managing instance types / flavors.""" + + def create(self, name, memory, vcpus, localstorage): + """Creates instance types / flavors + arguments: name memory vcpus localstorage""" + #for address in IPy.IP(range): + # db.floating_ip_create(context.get_admin_context(), + # {'address': str(address), + # 'host': host}) + + def delete(self, name): + """Deletes instance types / flavors + arguments: name""" + #for address in IPy.IP(ip_range): + # db.floating_ip_destroy(context.get_admin_context(), + # str(address)) + + def list(self): + """Lists all instance types / flavors + arguments: """ + #ctxt = context.get_admin_context() + #if host == None: + # floating_ips = db.floating_ip_get_all(ctxt) + #else: + # floating_ips = db.floating_ip_get_all_by_host(ctxt, host) + #for floating_ip in floating_ips: + # instance = None + # if floating_ip['fixed_ip']: + # instance = floating_ip['fixed_ip']['instance']['ec2_id'] + # print "%s\t%s\t%s" % (floating_ip['host'], + # floating_ip['address'], + # instance) + # print "%-10s %-10s %-8s %s %s" % (svc['host'], svc['binary'], + # active, art, + # svc['updated_at']) + + CATEGORIES = [ ('user', UserCommands), ('project', ProjectCommands), @@ -620,7 +658,8 @@ CATEGORIES = [ ('service', ServiceCommands), ('log', LogCommands), ('db', DbCommands), - ('volume', VolumeCommands)] + ('volume', VolumeCommands), + ('instance_types', InstanceTypesCommands)] def lazy_match(name, key_value_tuples): diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 196d6a8df..1b3d0d0eb 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -48,3 +48,15 @@ def get_by_flavor_id(flavor_id): if details['flavorid'] == flavor_id: return instance_type return FLAGS.default_instance_type + + +def list_flavors(): + return instance_type + + +def create_flavor(): + return instance_type + + +def delete_flavor(): + return instance_type diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py new file mode 100644 index 000000000..cc4a7aec0 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -0,0 +1,76 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + + +from nova import log as logging + + +meta = MetaData() + + +# +# New Tables +# +# Here are the old static instance types +# INSTANCE_TYPES = { +# 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), +# 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), +# 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), +# 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), +# 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} +instance_types = Table('instance_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('memory_mb', Integer(), nullable=False), + Column('vcpus', Integer(), nullable=False), + Column('local_gb', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False), + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here + # Don't create your own engine; bind migrate_engine + # to your metadata + meta.bind = migrate_engine + try: + instance_types.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise + + # TODO(ken-pepple) fix this to pre-populate the default EC2 types + #INSTANCE_TYPES = { + # 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + # 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + # 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + # 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + # 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + # for instance_type in INSTANCE_TYPES: + # try: + # prepopulate tables with EC2 types + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + # # Operations to reverse the above upgrade go here. + # for table in (instance_types): + # table.drop() + pass diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index c54ebe3ba..762425670 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -210,6 +210,16 @@ class InstanceActions(BASE, NovaBase): error = Column(Text) +class InstanceTypes(BASE, NovaBase): + """Represent possible instance_types or flavor of VM offered""" + __tablename__ = "instance_types" + id = Column(Integer, primary_key=True) + memory_mb = Column(Integer) + vcpus = Column(Integer) + local_gb = Column(Integer) + flavorid = Column(Integer) + + class Volume(BASE, NovaBase): """Represents a block storage device that can be attached to a vm.""" __tablename__ = 'volumes' diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 1bdaea161..05624c5a9 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -44,5 +44,15 @@ class FlavorsTest(unittest.TestCase): def test_get_flavor_by_id(self): pass + def test_create_favor(self): + pass + + def test_delete_flavor(self): + pass + + def test_list_flavors(self): + pass + + if __name__ == '__main__': unittest.main() -- cgit From 08794dba04c3919f9abbbfea1615b651394e5ee8 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 1 Feb 2011 18:42:12 -0800 Subject: don't fail on ip add exists and recreate default route on ip move if needed --- nova/network/linux_net.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 1a63b759f..efeed5bc7 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -195,11 +195,14 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): # NOTE(vish): The ip for dnsmasq has to be the first address on the # bridge for it to respond to reqests properly suffix = net_attrs['cidr'].rpartition('/')[2] - _execute("sudo ip addr add %s/%s brd %s dev %s" % - (net_attrs['gateway'], - suffix, - net_attrs['broadcast'], - bridge)) + out, err = _execute("sudo ip addr add %s/%s brd %s dev %s" % + (net_attrs['gateway'], + suffix, + net_attrs['broadcast'], + bridge), + check_exit_code=False) + if err and err != "RTNETLINK answers: File exists\n": + raise exception.Error("Failed to add ip: %s" % err) if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) @@ -214,14 +217,22 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): if interface: # NOTE(vish): This will break if there is already an ip on the # interface, so we move any ips to the bridge + gateway = None + out, err = _execute("sudo route") + for line in out.split("\n"): + fields = line.split() + if fields and fields[0] == "default" and fields[-1] == interface: + gateway = fields[1] out, err = _execute("sudo ip addr show dev %s scope global" % interface) for line in out.split("\n"): fields = line.split() if fields and fields[0] == "inet": - params = ' '.join(fields[1:-2]) + params = ' '.join(fields[1:-1]) _execute("sudo ip addr del %s dev %s" % (params, fields[-1])) _execute("sudo ip addr add %s dev %s" % (params, bridge)) + if gateway: + _execute("sudo route add default gw %s" % gateway) out, err = _execute("sudo brctl addif %s %s" % (bridge, interface), check_exit_code=False) -- cgit From d647a067acb884ff2c324afa5a962a8dae71c6c6 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 1 Feb 2011 19:31:43 -0800 Subject: pass the set_ip from ensure_vlan_bridge --- nova/network/linux_net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index efeed5bc7..c5cf761f2 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -168,7 +168,7 @@ def remove_floating_forward(floating_ip, fixed_ip): def ensure_vlan_bridge(vlan_num, bridge, net_attrs, set_ip=False): """Create a vlan and bridge unless they already exist""" interface = ensure_vlan(vlan_num) - ensure_bridge(bridge, interface, net_attrs) + ensure_bridge(bridge, interface, net_attrs, set_ip) def ensure_vlan(vlan_num): -- cgit From ef9217548b1ae74d3e4f7282d26e0d9fee5470ce Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 2 Feb 2011 08:15:57 -0800 Subject: moves driver.init_host into the base class so it happens before floating forwards and sets up proper iptables chains --- nova/network/manager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index fbcbea131..8eb9f041b 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -118,6 +118,10 @@ class NetworkManager(manager.Manager): super(NetworkManager, self).__init__(*args, **kwargs) def init_host(self): + """Do any initialization that needs to be run if this is a + standalone service. + """ + self.driver.init_host() # Set up networking for the projects for which we're already # the designated network host. ctxt = context.get_admin_context() @@ -395,7 +399,6 @@ class FlatDHCPManager(FlatManager): standalone service. """ super(FlatDHCPManager, self).init_host() - self.driver.init_host() self.driver.metadata_forward() def setup_compute_network(self, context, instance_id): @@ -465,7 +468,6 @@ class VlanManager(NetworkManager): standalone service. """ super(VlanManager, self).init_host() - self.driver.init_host() self.driver.metadata_forward() def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): -- cgit From 6d2e2c52012abac8cab322357ce0ffd0ffc2fbaf Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 2 Feb 2011 10:49:02 -0600 Subject: Casting to the scheduler --- nova/compute/api.py | 10 +++++++--- nova/rpc.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 0faa066b5..283845709 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -404,10 +404,14 @@ class API(base.Base): process.""" raise NotImplemented() - def resize(self, context, instance_id, flavor_id): + def resize(self, context, instance_id, flavor): """Resize a running instance.""" - self._cast_compute_message('resize_instance', context, instance_id, - flavor_id) + rpc.cast(context, + FLAGS.scheduler_topic, + {"method": "resize_instance", + "args": {"topic": FLAGS.compute_topic, + "instance_id": instance_id, + "flavor": flavor}}) def pause(self, context, instance_id): """Pause the given instance.""" diff --git a/nova/rpc.py b/nova/rpc.py index 01fc6d44b..c4c938f4d 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -119,7 +119,7 @@ class Consumer(messaging.Consumer): LOG.error(_("Reconnected to queue")) self.failed_connection = False # NOTE(vish): This is catching all errors because we really don't - # exceptions to be logged 10 times a second if some + # want exceptions to be logged 10 times a second if some # persistent failure occurs. except Exception: # pylint: disable-msg=W0703 if not self.failed_connection: -- cgit From 4b121d03b27fd2ab3e5a2df0833ff8dfae5dfad3 Mon Sep 17 00:00:00 2001 From: termie Date: Wed, 2 Feb 2011 13:13:10 -0800 Subject: some updates to HACKING to describe the docstrings --- HACKING | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/HACKING b/HACKING index 3af2381bf..4e0dfc835 100644 --- a/HACKING +++ b/HACKING @@ -47,3 +47,18 @@ Human Alphabetical Order Examples from nova.auth import users from nova.endpoint import api from nova.endpoint import cloud + +Docstrings +---------- + """Summary of the function, class or method, less than 80 characters. + + New paragraph after newline that explains in more detail any general + information about the function, class or method. After this, if defining + parameters and return types use the Sphinx format. After that an extra + newline then close the quotations. + + :param foo: the foo parameter + :param bar: the bar parameter + :rtype: the return type + + """ -- cgit From 321f581660aad3fc9da5f88276bfdf11f6960d97 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 3 Feb 2011 13:10:19 -0800 Subject: Don't need a route for guests. Turns out the issue with routing from the guests was due to duplicate macs --- nova/network/linux_net.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index c5cf761f2..3a8bd9435 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -206,14 +206,6 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) - else: - # NOTE(vish): if we don't give an ip to the bridge, we set up a route - # for the guests - out, err = _execute("sudo route add -net %s dev %s" % - (net_attrs['cidr'], bridge), - check_exit_code=False) - if err and err != "SIOCADDRT: File exists\n": - raise exception.Error("Failed to add route: %s" % err) if interface: # NOTE(vish): This will break if there is already an ip on the # interface, so we move any ips to the bridge -- cgit From 563a77fd4aa80da9bddac5cf7f8f27ed2dedb39d Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 3 Feb 2011 17:52:19 -0800 Subject: added seed data to migration --- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 53 ++++++++++++---------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index cc4a7aec0..c7e61dc05 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -15,9 +15,11 @@ from sqlalchemy import * from migrate import * - +from nova import api +from nova import db from nova import log as logging +import time meta = MetaData() @@ -25,17 +27,14 @@ meta = MetaData() # # New Tables # -# Here are the old static instance types -# INSTANCE_TYPES = { -# 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), -# 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), -# 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), -# 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), -# 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} instance_types = Table('instance_types', meta, Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), Column('deleted_at', DateTime(timezone=False)), Column('deleted', Boolean(create_constraint=True, name=None)), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), Column('id', Integer(), primary_key=True, nullable=False), Column('memory_mb', Integer(), nullable=False), Column('vcpus', Integer(), nullable=False), @@ -53,24 +52,32 @@ def upgrade(migrate_engine): instance_types.create() except Exception: logging.info(repr(table)) - logging.exception('Exception while creating table') + logging.exception('Exception while creating instance_types table') raise - # TODO(ken-pepple) fix this to pre-populate the default EC2 types - #INSTANCE_TYPES = { - # 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - # 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - # 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - # 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - # 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - # for instance_type in INSTANCE_TYPES: - # try: - # prepopulate tables with EC2 types + # Here are the old static instance types + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + try: + i = instance_types.insert() + for name, values in INSTANCE_TYPES.iteritems(): + # FIXME(kpepple) should we be seeding created_at / updated_at ? + # the_time = time.strftime("%Y-%m-%d %H:%M:%S") + i.execute({'name': name, 'memory_mb': values["memory_mb"], + 'vcpus': values["vcpus"], + 'local_gb': values["local_gb"], + 'flavorid': values["flavorid"]}) + except Exception: + logging.info(repr(table)) + logging.exception('Exception while seeding instance_types table') + raise def downgrade(migrate_engine): # Operations to reverse the above upgrade go here. - # # Operations to reverse the above upgrade go here. - # for table in (instance_types): - # table.drop() - pass + for table in (instance_types): + table.drop() -- cgit From 9be0770208b0e75c7d93ba10165b82d5be11be27 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 3 Feb 2011 17:57:46 -0800 Subject: flagged all INSTANCE_TYPES usage with FIXME comment. Added basic usage to nova-manage (needs formatting). created api methods. --- bin/nova-manage | 39 ++++++++++++-------------------- nova/api/ec2/admin.py | 1 + nova/api/openstack/flavors.py | 2 ++ nova/compute/api.py | 2 ++ nova/compute/instance_types.py | 17 ++++---------- nova/db/api.py | 20 ++++++++++++++++ nova/db/sqlalchemy/api.py | 31 +++++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 1 + nova/tests/api/openstack/test_flavors.py | 30 +++++++++++++++--------- nova/tests/db/fakes.py | 1 + nova/tests/test_quota.py | 3 +++ nova/tests/test_xenapi.py | 1 + nova/virt/libvirt_conn.py | 2 ++ nova/virt/xenapi/vm_utils.py | 1 + 14 files changed, 102 insertions(+), 49 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 952bf4fd1..0406a2dd9 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -611,40 +611,29 @@ class VolumeCommands(object): class InstanceTypesCommands(object): """Class for managing instance types / flavors.""" + def usage(self): + print "$ nova-manage instance_type NAME MEMORY_MB VCPUS LOCAL_GB" - def create(self, name, memory, vcpus, localstorage): + def create(self, name, memory, vcpus, local_gb): """Creates instance types / flavors - arguments: name memory vcpus localstorage""" - #for address in IPy.IP(range): - # db.floating_ip_create(context.get_admin_context(), - # {'address': str(address), - # 'host': host}) + arguments: name memory_mb vcpus local_gb""" + db.instance_type_create(context.get_admin_context(), name, memory, vcpus, local_gb) def delete(self, name): - """Deletes instance types / flavors + """Marks instance types / flavors as deleted arguments: name""" - #for address in IPy.IP(ip_range): - # db.floating_ip_destroy(context.get_admin_context(), - # str(address)) + ctxt = context.get_admin_context() + # check to see if it exists + db.instance_type_delete(context,name) def list(self): """Lists all instance types / flavors arguments: """ - #ctxt = context.get_admin_context() - #if host == None: - # floating_ips = db.floating_ip_get_all(ctxt) - #else: - # floating_ips = db.floating_ip_get_all_by_host(ctxt, host) - #for floating_ip in floating_ips: - # instance = None - # if floating_ip['fixed_ip']: - # instance = floating_ip['fixed_ip']['instance']['ec2_id'] - # print "%s\t%s\t%s" % (floating_ip['host'], - # floating_ip['address'], - # instance) - # print "%-10s %-10s %-8s %s %s" % (svc['host'], svc['binary'], - # active, art, - # svc['updated_at']) + instance_types = db.instance_type_get_all(context.get_admin_context()) + for instance in instance_types: + print "%s : %s memory (MB), %s vcpus, %s storage(GB)" % (instance.name, + instance.memory_mb, instance.vcpus, + instance.local_gb) CATEGORIES = [ diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index d7e899d12..55cca1041 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -79,6 +79,7 @@ class AdminController(object): def __str__(self): return 'AdminController' + # FIX-ME(kpepple) for dynamic flavors def describe_instance_types(self, _context, **_kwargs): return {'instanceTypeSet': [instance_dict(n, v) for n, v in instance_types.INSTANCE_TYPES.iteritems()]} diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index f620d4107..1f5185134 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -45,6 +45,7 @@ class Controller(wsgi.Controller): def show(self, req, id): """Return data about the given flavor id.""" + # FIX-ME(kpepple) for dynamic flavors for name, val in instance_types.INSTANCE_TYPES.iteritems(): if val['flavorid'] == int(id): item = dict(ram=val['memory_mb'], disk=val['local_gb'], @@ -54,4 +55,5 @@ class Controller(wsgi.Controller): def _all_ids(self): """Return the list of all flavorids.""" + # FIX-ME(kpepple) for dynamic flavors return [i['flavorid'] for i in instance_types.INSTANCE_TYPES.values()] diff --git a/nova/compute/api.py b/nova/compute/api.py index 1d8b9d79f..3ae722226 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -89,6 +89,8 @@ class API(base.Base): """Create the number of instances requested if quota and other arguments check out ok.""" + # FIXME(kpepple) this needs to be changed from using the old constant + #type_data = db.instance_type_get_by_name(context.get_admin_context(), instance_type) type_data = instance_types.INSTANCE_TYPES[instance_type] num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 1b3d0d0eb..0594aa063 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -25,6 +25,7 @@ from nova import flags from nova import exception FLAGS = flags.FLAGS +# FIX-ME(kpepple) for dynamic flavors INSTANCE_TYPES = { 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), @@ -35,8 +36,9 @@ INSTANCE_TYPES = { def get_by_type(instance_type): """Build instance data structure and save it to the data store.""" + # FIX-ME(kpepple) for dynamic flavors if instance_type is None: - return FLAGS.default_instance_type + return FLAGS.default_instance_type if instance_type not in INSTANCE_TYPES: raise exception.ApiError(_("Unknown instance type: %s"), instance_type) @@ -44,19 +46,8 @@ def get_by_type(instance_type): def get_by_flavor_id(flavor_id): + # FIX-ME(kpepple) for dynamic flavors for instance_type, details in INSTANCE_TYPES.iteritems(): if details['flavorid'] == flavor_id: return instance_type return FLAGS.default_instance_type - - -def list_flavors(): - return instance_type - - -def create_flavor(): - return instance_type - - -def delete_flavor(): - return instance_type diff --git a/nova/db/api.py b/nova/db/api.py index c6c03fb0e..ffb1d08ce 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -985,3 +985,23 @@ def console_get_all_by_instance(context, instance_id): def console_get(context, console_id, instance_id=None): """Get a specific console (possibly on a given instance).""" return IMPL.console_get(context, console_id, instance_id) + + + ################## + + +def instance_type_create(context, values): + """Create a new instance type""" + return IMPL.instance_type_create(context, values) + +def instance_type_get_all(context): + """Get all instance types""" + return IMPL.instance_type_get_all(context) + +def instance_type_get_by_name(context, name): + """Get instance type by name""" + return IMPL.instance_type_get_by_name(context, name) + +def instance_type_destroy(context, name): + """Delete a instance type""" + return IMPL.instance_type_destroy(context,name) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index fa060228f..aa6434b65 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2018,3 +2018,34 @@ def console_get(context, console_id, instance_id=None): raise exception.NotFound(_("No console with id %(console_id)s" " %(idesc)s") % locals()) return result + + + ################## + + +@require_admin_context +def instance_type_create(context, values): + instance_type_ref = models.InstanceTypes() + instance_type_ref.update(values) + instance_type_ref.save() + return instance_type_ref + +def instance_type_get_all(context): + session = get_session() + return session.query(models.InstanceTypes).\ + filter_by(deleted=0) + +def instance_type_get_by_name(context, name): + session = get_session() + return session.query(models.InstanceTypes).\ + filter_by(name=name).\ + first() + +@require_admin_context +def instance_type_destroy(context,name): + session = get_session() + instance_type_ref = session.query(models.InstanceTypes).\ + filter_by(name=name) + rows = instance_type_ref.update(dict(deleted=1)) + return instance_type_ref + diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 762425670..44583861b 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -214,6 +214,7 @@ class InstanceTypes(BASE, NovaBase): """Represent possible instance_types or flavor of VM offered""" __tablename__ = "instance_types" id = Column(Integer, primary_key=True) + name = Column(String(255), unique=True) memory_mb = Column(Integer) vcpus = Column(Integer) local_gb = Column(Integer) diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 05624c5a9..0f0ed2e84 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -21,6 +21,8 @@ import stubout import webob import nova.api +from nova import context +from nova import db from nova.api.openstack import flavors from nova.tests.api.openstack import fakes @@ -33,6 +35,7 @@ class FlavorsTest(unittest.TestCase): fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) fakes.stub_out_auth(self.stubs) + self.context = context.get_admin_context() def tearDown(self): self.stubs.UnsetAll() @@ -41,17 +44,22 @@ class FlavorsTest(unittest.TestCase): req = webob.Request.blank('/v1.0/flavors') res = req.get_response(fakes.wsgi_app()) - def test_get_flavor_by_id(self): - pass - - def test_create_favor(self): - pass - - def test_delete_flavor(self): - pass - - def test_list_flavors(self): - pass + def test_create_list_delete_favor(self): + # create a new flavor + starting_flavors = db.instance_type_get_all(self.context) + new_instance_type = dict(name="os1.big",memory_mb=512, vcpus=1, local_gb=120, flavorid=25) + new_flavor = db.instance_type_create(self.context, new_instance_type) + self.assertEqual(new_flavor["name"], new_instance_type["name"]) + # retrieve the newly created flavor + retrieved_new_flavor = db.instance_type_get_by_name(self.context, new_instance_type["name"]) + # self.assertEqual(len(tuple(retrieved_new_flavor)),1) + self.assertEqual(retrieved_new_flavor["memory_mb"], new_instance_type["memory_mb"]) + flavors = db.instance_type_get_all(self.context) + self.assertNotEqual(starting_flavors, flavors) + # delete the newly created flavor + delete_query = db.instance_type_destroy(self.context,new_instance_type["name"]) + retrieve_deleted_flavor = db.instance_type_get_by_name(self.context, new_instance_type["name"]) + self.assertEqual(retrieve_deleted_flavor["deleted"], 1) if __name__ == '__main__': diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 05bdd172e..6cf917d76 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -44,6 +44,7 @@ def stub_out_db_instance_api(stubs): def fake_instance_create(values): """ Stubs out the db.instance_create method """ + # FIX-ME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[values['instance_type']] base_options = { diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 9548a8c13..f2c9c456f 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -75,15 +75,18 @@ class QuotaTestCase(test.TestCase): def test_quota_overrides(self): """Make sure overriding a projects quotas works""" + # FIX-ME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 2) db.quota_create(self.context, {'project_id': self.project.id, 'instances': 10}) + # FIX-ME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 4) db.quota_update(self.context, self.project.id, {'cores': 100}) + # FIX-ME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 10) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 9f5b266f3..e38bd4dab 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -225,6 +225,7 @@ class XenAPIVMTestCase(test.TestCase): vm = vms[0] # Check that m1.large above turned into the right thing. + # FIX-ME(kpepple) for dynamic flavors instance_type = instance_types.INSTANCE_TYPES['m1.large'] mem_kib = long(instance_type['memory_mb']) << 10 mem_bytes = str(mem_kib << 10) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index bd5c9c4ee..9ed6d4a71 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -611,6 +611,7 @@ class LibvirtConnection(object): user=user, project=project, size=size) + # FIX-ME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[inst['instance_type']] if type_data['local_gb']: @@ -671,6 +672,7 @@ class LibvirtConnection(object): network = db.network_get_by_instance(context.get_admin_context(), instance['id']) # FIXME(vish): stick this in db + # FIX-ME(kpepple) for dynamic flavors instance_type = instance['instance_type'] instance_type = instance_types.INSTANCE_TYPES[instance_type] ip_address = db.instance_get_fixed_address(context.get_admin_context(), diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4afd28dd8..4d09b2afa 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -82,6 +82,7 @@ class VMHelper(HelperBase): the pv_kernel flag indicates whether the guest is HVM or PV """ + # FIX-ME(kpepple) for dynamic flavors instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] mem = str(long(instance_type['memory_mb']) * 1024 * 1024) vcpus = str(instance_type['vcpus']) -- cgit From 60891ed6f3a978ce77575e8710b695aa9828adcc Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Fri, 4 Feb 2011 05:31:40 +0000 Subject: Launchpad automatic translations update. --- locale/ast.po | 2 +- locale/da.po | 2 +- locale/es.po | 2 +- locale/it.po | 2 +- locale/ja.po | 2 +- locale/pt_BR.po | 16 ++++++++-------- locale/ru.po | 2 +- locale/uk.po | 25 +++++++++++++++---------- locale/zh_CN.po | 2 +- 9 files changed, 30 insertions(+), 25 deletions(-) diff --git a/locale/ast.po b/locale/ast.po index c887bbc91..310f299be 100644 --- a/locale/ast.po +++ b/locale/ast.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/da.po b/locale/da.po index 524b27a64..ac1b593b0 100644 --- a/locale/da.po +++ b/locale/da.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/es.po b/locale/es.po index a1cf5b7f6..28f10c481 100644 --- a/locale/es.po +++ b/locale/es.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/it.po b/locale/it.po index f2f6a6b87..f08497bac 100644 --- a/locale/it.po +++ b/locale/it.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/ja.po b/locale/ja.po index 919625e9a..b11bb67b0 100644 --- a/locale/ja.po +++ b/locale/ja.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/pt_BR.po b/locale/pt_BR.po index a58ccc182..c778bb631 100644 --- a/locale/pt_BR.po +++ b/locale/pt_BR.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-01-10 11:25-0800\n" -"PO-Revision-Date: 2011-01-13 18:44+0000\n" -"Last-Translator: Gustavo Morozowski \n" +"PO-Revision-Date: 2011-02-03 20:32+0000\n" +"Last-Translator: André Gondim \n" "Language-Team: Brazilian Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-01-28 05:21+0000\n" +"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 @@ -60,7 +60,7 @@ msgstr "Sujeito do certificado para vpns, %s para projeto, timestamp" #: nova/crypto.py:258 #, python-format msgid "Flags path: %s" -msgstr "" +msgstr "Caminho da sinalização: %s" #: nova/exception.py:33 msgid "Unexpected error while running command." @@ -103,7 +103,7 @@ msgstr "Declarando fila %s" #: nova/fakerabbit.py:89 #, python-format msgid "Declaring exchange %s" -msgstr "" +msgstr "Declarando troca %s" #: nova/fakerabbit.py:95 #, python-format @@ -432,7 +432,7 @@ msgstr "%s não é um ipProtocol válido" #: nova/api/ec2/cloud.py:361 msgid "Invalid port range" -msgstr "" +msgstr "Intervalo de porta inválido" #: nova/api/ec2/cloud.py:392 #, python-format @@ -767,7 +767,7 @@ msgstr "Criado usuário %s (administrador: %r)" #: nova/auth/manager.py:645 #, python-format msgid "Deleting user %s" -msgstr "" +msgstr "Apagando usuário %s" #: nova/auth/manager.py:655 #, python-format @@ -804,7 +804,7 @@ msgstr "" #: nova/cloudpipe/pipelib.py:97 #, python-format msgid "Launching VPN for %s" -msgstr "" +msgstr "Executando VPN para %s" #: nova/compute/api.py:67 #, python-format diff --git a/locale/ru.po b/locale/ru.po index 6a75c1727..fc97d5603 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-01 05:19+0000\n" +"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/uk.po b/locale/uk.po index cdbffd130..be2371dbc 100644 --- a/locale/uk.po +++ b/locale/uk.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-01-10 11:25-0800\n" -"PO-Revision-Date: 2011-01-13 07:03+0000\n" +"PO-Revision-Date: 2011-02-03 22:02+0000\n" "Last-Translator: Wladimir Rossinski \n" "Language-Team: Ukrainian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-01-28 05:20+0000\n" +"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 @@ -23,7 +23,7 @@ msgstr "" #: nova/crypto.py:49 msgid "Filename of private key" -msgstr "" +msgstr "Ім'я файлу секретного ключа" #: nova/crypto.py:51 msgid "Filename of root Certificate Revokation List" @@ -31,7 +31,7 @@ msgstr "" #: nova/crypto.py:53 msgid "Where we keep our keys" -msgstr "" +msgstr "Шлях до збережених ключів" #: nova/crypto.py:55 msgid "Where we keep our root CA" @@ -63,7 +63,7 @@ msgstr "" #: nova/exception.py:33 msgid "Unexpected error while running command." -msgstr "" +msgstr "Неочікувана помилка при виконанні команди." #: nova/exception.py:36 #, python-format @@ -74,10 +74,15 @@ msgid "" "Stdout: %r\n" "Stderr: %r" msgstr "" +"%s\n" +"Команда: %s\n" +"Код завершення: %s\n" +"Stdout: %r\n" +"Stderr: %r" #: nova/exception.py:86 msgid "Uncaught exception" -msgstr "" +msgstr "Необроблене виключення" #: nova/fakerabbit.py:48 #, python-format @@ -92,12 +97,12 @@ msgstr "" #: nova/fakerabbit.py:83 #, python-format msgid "Declaring queue %s" -msgstr "" +msgstr "Оголошення черги %s" #: nova/fakerabbit.py:89 #, python-format msgid "Declaring exchange %s" -msgstr "" +msgstr "Оголошення точки обміну %s" #: nova/fakerabbit.py:95 #, python-format @@ -107,7 +112,7 @@ msgstr "" #: nova/fakerabbit.py:120 #, python-format msgid "Getting from %s: %s" -msgstr "" +msgstr "Отримання з %s: %s" #: nova/rpc.py:92 #, python-format @@ -121,7 +126,7 @@ msgstr "Не вдалось під'єднатися до серверу AMQP п #: nova/rpc.py:118 msgid "Reconnected to queue" -msgstr "" +msgstr "Оновлено з'єднання до черги" #: nova/rpc.py:125 msgid "Failed to fetch message from queue" diff --git a/locale/zh_CN.po b/locale/zh_CN.po index 4805eb327..feee54cfe 100644 --- a/locale/zh_CN.po +++ b/locale/zh_CN.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-01-28 05:21+0000\n" +"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/twistd.py:268 -- cgit From 62d2a1a101dc97e80cb0a582197a6b7468abf593 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 4 Feb 2011 11:04:55 -0600 Subject: Implementation of 'SAN' volumes A SAN volume is 'special' because the volume service probably won't run on the iSCSI target. Initial support is for Solaris with COMSTAR (Solaris 11) --- nova/volume/driver.py | 10 +- nova/volume/manager.py | 2 +- nova/volume/san.py | 373 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 379 insertions(+), 6 deletions(-) create mode 100644 nova/volume/san.py diff --git a/nova/volume/driver.py b/nova/volume/driver.py index da7307733..82f4c2f54 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -294,8 +294,10 @@ class ISCSIDriver(VolumeDriver): self._execute("sudo ietadm --op delete --tid=%s" % iscsi_target) - def _get_name_and_portal(self, volume_name, host): + def _get_name_and_portal(self, volume): """Gets iscsi name and portal from volume name and host.""" + volume_name = volume['name'] + host = volume['host'] (out, _err) = self._execute("sudo iscsiadm -m discovery -t " "sendtargets -p %s" % host) for target in out.splitlines(): @@ -307,8 +309,7 @@ class ISCSIDriver(VolumeDriver): def discover_volume(self, volume): """Discover volume on a remote host.""" - iscsi_name, iscsi_portal = self._get_name_and_portal(volume['name'], - volume['host']) + iscsi_name, iscsi_portal = self._get_name_and_portal(volume) self._execute("sudo iscsiadm -m node -T %s -p %s --login" % (iscsi_name, iscsi_portal)) self._execute("sudo iscsiadm -m node -T %s -p %s --op update " @@ -319,8 +320,7 @@ class ISCSIDriver(VolumeDriver): def undiscover_volume(self, volume): """Undiscover volume on a remote host.""" - iscsi_name, iscsi_portal = self._get_name_and_portal(volume['name'], - volume['host']) + iscsi_name, iscsi_portal = self._get_name_and_portal(volume) self._execute("sudo iscsiadm -m node -T %s -p %s --op update " "-n node.startup -v manual" % (iscsi_name, iscsi_portal)) diff --git a/nova/volume/manager.py b/nova/volume/manager.py index 6f8e25e19..6e70ec881 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -87,7 +87,7 @@ class VolumeManager(manager.Manager): if volume['status'] in ['available', 'in-use']: self.driver.ensure_export(ctxt, volume) else: - LOG.info(_("volume %s: skipping export"), volume_ref['name']) + LOG.info(_("volume %s: skipping export"), volume['name']) def create_volume(self, context, volume_id): """Creates and exports the volume.""" diff --git a/nova/volume/san.py b/nova/volume/san.py new file mode 100644 index 000000000..22a0da13f --- /dev/null +++ b/nova/volume/san.py @@ -0,0 +1,373 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Justin Santa Barbara +# 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. +""" +Drivers for san-stored volumes. +The unique thing about a SAN is that we don't expect that we can run the volume + controller on the SAN hardware. We expect to access it over SSH or some API. +""" + +import os +import paramiko + +from nova import exception +from nova import flags +from nova import log as logging +from nova.volume.driver import ISCSIDriver + +LOG = logging.getLogger("nova.volume.driver") +FLAGS = flags.FLAGS +flags.DEFINE_boolean('san_thin_provision', 'true', + 'Use thin provisioning for SAN volumes?') +flags.DEFINE_string('san_ip', '', + 'IP address of SAN controller') +flags.DEFINE_string('san_login', 'admin', + 'Username for SAN controller') +flags.DEFINE_string('san_password', '', + 'Password for SAN controller') +flags.DEFINE_string('san_cluster_name', '', + 'Cluster name for creating SAN volumes') +flags.DEFINE_string('san_privatekey', '', + 'Filename of private key to use for SSH authentication') + +class SanISCSIDriver(ISCSIDriver): + """ Base class for SAN-style storage volumes + (storage providers we access over SSH)""" + #Override because SAN ip != host ip + def _get_name_and_portal(self, volume): + """Gets iscsi name and portal from volume name and host.""" + volume_name = volume['name'] + + # TODO(justinsb): store in volume, remerge with generic iSCSI code + host = FLAGS.san_ip + + (out, _err) = self._execute("sudo iscsiadm -m discovery -t " + "sendtargets -p %s" % host) + + location = None + find_iscsi_name = self._build_iscsi_target_name(volume) + for target in out.splitlines(): + if find_iscsi_name in target: + (location, _sep, iscsi_name) = target.partition(" ") + break + if not location: + raise exception.Error(_("Could not find iSCSI export " + " for volume %s") % + volume_name) + + iscsi_portal = location.split(",")[0] + LOG.debug("iscsi_name=%s, iscsi_portal=%s" % (iscsi_name, iscsi_portal)) + return (iscsi_name, iscsi_portal) + + def _build_iscsi_target_name(self, volume): + return "%s%s" % (FLAGS.iscsi_target_prefix, volume['name']) + + # discover_volume is still OK + # undiscover_volume is still OK + + def _connect_to_ssh(self): + ssh = paramiko.SSHClient() + #TODO(justinsb): We need a better SSH key policy + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + if FLAGS.san_password: + ssh.connect(FLAGS.san_ip, + username=FLAGS.san_login, + password=FLAGS.san_password) + elif FLAGS.san_privatekey: + privatekeyfile = os.path.expanduser(FLAGS.san_privatekey) + # It sucks that paramiko doesn't support DSA keys + privatekey = paramiko.RSAKey.from_private_key_file(privatekeyfile) + ssh.connect(FLAGS.san_ip, username=FLAGS.san_login, pkey=privatekey) + else: + raise exception.Error("Specify san_password or san_privatekey") + return ssh + + def _run_ssh(self, command, check_exit_code=True): + #TODO(justinsb): SSH connection caching (?) + ssh = self._connect_to_ssh() + + #TODO(justinsb): Reintroduce the retry hack + ret = ssh_execute(ssh, command, check_exit_code=check_exit_code) + + ssh.close() + + return ret + + def ensure_export(self, context, volume): + """Synchronously recreates an export for a logical volume.""" + pass + + def create_export(self, context, volume): + """Exports the volume.""" + pass + + def remove_export(self, context, volume): + """Removes an export for a logical volume.""" + pass + + def check_for_setup_error(self): + """Returns an error if prerequisites aren't met""" + if not (FLAGS.san_password or FLAGS.san_privatekey): + raise exception.Error("Specify san_password or san_privatekey") + + if not (FLAGS.san_ip): + raise exception.Error("san_ip must be set") + +def _collect_lines(data): + """ Split lines from data into an array, trimming them """ + matches = [] + for line in data.splitlines(): + match = line.strip() + matches.append(match) + + return matches + +def _get_prefixed_values(data, prefix): + """Collect lines which start with prefix; with trimming""" + matches = [] + for line in data.splitlines(): + line = line.strip() + if line.startswith(prefix): + match = line[len(prefix):] + match = match.strip() + matches.append(match) + + return matches + +class SolarisISCSIDriver(SanISCSIDriver): + """Executes commands relating to Solaris-hosted ISCSI volumes.""" + + #pkg install storage-server SUNWiscsit + #svcadm enable stmf + #zfs allow justinsb create,mount,destroy rpool + #usermod -P'File System Management' justinsb + # I don't know which perms are needed for sbdadm... + #usermod -P'Primary Administrator' justinsb + # svcadm enable -r svc:/network/iscsi/target:default + # pfexec itadm create-tpg e1000g0 10.1.184.98 + # pfexec itadm create-target -t e1000g0 + + def _view_exists(self, luid): + (out, _err) = self._run_ssh("pfexec /usr/sbin/stmfadm list-view -l %s" % + (luid), + check_exit_code=False) + if "no views found" in out: + return False + + if "View Entry:" in out: + return True + + raise exception.Error("Cannot parse list-view output: %s" % (out)) + + def _get_target_groups(self): + """Gets list of target groups from host.""" + (out, _err) = self._run_ssh("pfexec /usr/sbin/stmfadm list-tg") + matches = _get_prefixed_values(out, 'Target group: ') + LOG.debug("target_groups=%s" % matches) + return matches + + def _target_group_exists(self, target_group_name): + return target_group_name not in self._get_target_groups() + + def _get_target_group_members(self, target_group_name): + (out, _err) = self._run_ssh("pfexec /usr/sbin/stmfadm list-tg -v %s" % + (target_group_name)) + matches = _get_prefixed_values(out, 'Member: ') + LOG.debug("members of %s=%s" % (target_group_name, matches)) + return matches + + def _is_target_group_member(self, target_group_name, iscsi_target_name): + return iscsi_target_name in ( + self._get_target_group_members(target_group_name)) + + def _get_iscsi_targets(self): + cmd = ("pfexec /usr/sbin/itadm list-target | " + "awk '{print $1}' | grep -v ^TARGET") + (out, _err) = self._run_ssh(cmd) + matches = _collect_lines(out) + LOG.debug("_get_iscsi_targets=%s" % (matches)) + return matches + + def _iscsi_target_exists(self, iscsi_target_name): + return iscsi_target_name in self._get_iscsi_targets() + + def _build_zfs_poolname(self, volume): + #TODO(justinsb): rpool should be configurable + zfs_poolname = 'rpool/%s' % (volume['name']) + return zfs_poolname + + def create_volume(self, volume): + """Creates a volume.""" + if int(volume['size']) == 0: + sizestr = '100M' + else: + sizestr = '%sG' % volume['size'] + + zfs_poolname = self._build_zfs_poolname(volume) + + thin_provision_arg = '-s' if FLAGS.san_thin_provision else '' + # Create a zfs volume + self._run_ssh("pfexec /usr/sbin/zfs create %s -V %s %s" % + (thin_provision_arg, + sizestr, + zfs_poolname)) + + return { # 'target_ip': FLAGS.san_ip + } + + def _get_luid(self, volume): + zfs_poolname = self._build_zfs_poolname(volume) + + cmd = ("pfexec /usr/sbin/sbdadm list-lu | " + "grep -w %s | awk '{print $1}'" % + (zfs_poolname)) + + (stdout, _stderr) = self._run_ssh(cmd) + + luid = stdout.strip() + return luid + + def _is_lu_created(self, volume): + luid = self._get_luid(volume) + return luid + + def delete_volume(self, volume): + """Deletes a volume.""" + zfs_poolname = self._build_zfs_poolname(volume) + self._run_ssh("pfexec /usr/sbin/zfs destroy %s" % + (zfs_poolname)) + + def local_path(self, volume): + # TODO(justinsb): Is this needed here? + escaped_group = FLAGS.volume_group.replace('-', '--') + escaped_name = volume['name'].replace('-', '--') + return "/dev/mapper/%s-%s" % (escaped_group, escaped_name) + + def ensure_export(self, context, volume): + """Synchronously recreates an export for a logical volume.""" + #TODO(justinsb): On bootup, this is called for every volume. + # It then runs ~5 SSH commands for each volume, + # most of which fetch the same info each time + # This makes initial start stupid-slow + self._do_export(volume, force_create=False) + + def create_export(self, context, volume): + self._do_export(volume, force_create=True) + + def _do_export(self, volume, force_create): + # Create a Logical Unit (LU) backed by the zfs volume + zfs_poolname = self._build_zfs_poolname(volume) + + if force_create or not self._is_lu_created(volume): + cmd = ("pfexec /usr/sbin/sbdadm create-lu /dev/zvol/rdsk/%s" % + (zfs_poolname)) + self._run_ssh(cmd) + + luid = self._get_luid(volume) + iscsi_name = self._build_iscsi_target_name(volume) + target_group_name = 'tg-%s' % volume['name'] + + # The sequence of commands looks like this: + #pfexec stmfadm create-tg tg-vol2 + # Yes, we add it before we create it! + #pfexec stmfadm add-tg-member -g tg-vol2 iqn.2010-10.org.openstack:vol2 + #pfexec itadm create-target -n iqn.2010-10.org.openstack:vol2 + #pfexec stmfadm add-view -t tg-vol2 600144F051E2440000004D4A0F730004 + + # Create a iSCSI target, mapped to just this volume + if force_create or not self._target_group_exists(target_group_name): + self._run_ssh("pfexec /usr/sbin/stmfadm create-tg %s" % + (target_group_name)) + + # Yes, we add the initiatior before we create it! + if force_create or not self._is_target_group_member(target_group_name, + iscsi_name): + self._run_ssh("pfexec /usr/sbin/stmfadm add-tg-member -g %s %s" % + (target_group_name, iscsi_name)) + if force_create or not self._iscsi_target_exists(iscsi_name): + self._run_ssh("pfexec /usr/sbin/itadm create-target -n %s" % + (iscsi_name)) + if force_create or not self._view_exists(luid): + self._run_ssh("pfexec /usr/sbin/stmfadm add-view -t %s %s" % + (target_group_name, luid)) + + def remove_export(self, context, volume): + """Removes an export for a logical volume.""" + + # This is the reverse of _do_export + luid = self._get_luid(volume) + iscsi_name = self._build_iscsi_target_name(volume) + target_group_name = 'tg-%s' % volume['name'] + + if self._view_exists(luid): + self._run_ssh("pfexec /usr/sbin/stmfadm remove-view -l %s -a" % + (luid)) + + if self._iscsi_target_exists(iscsi_name): + self._run_ssh("pfexec /usr/sbin/stmfadm offline-target %s" % + (iscsi_name)) + self._run_ssh("pfexec /usr/sbin/itadm delete-target %s" % + (iscsi_name)) + + # We don't delete the tg-member; we delete the whole tg! + + if self._target_group_exists(target_group_name): + self._run_ssh("pfexec /usr/sbin/stmfadm delete-tg %s" % + (target_group_name)) + + if self._is_lu_created(volume): + self._run_ssh("pfexec /usr/sbin/sbdadm delete-lu %s" % + (luid)) + + +def ssh_execute(ssh, cmd, process_input=None, + addl_env=None, check_exit_code=True): + LOG.debug(_("Running cmd (subprocess): %s"), cmd) + if addl_env: + raise exception.Error("Environment not supported over SSH") + + if process_input: + # This is (probably) fixable if we need it... + raise exception.Error("process_input not supported over SSH") + + stdin_stream, stdout_stream, stderr_stream = ssh.exec_command(cmd) + channel = stdout_stream.channel + + #stdin.write('process_input would go here') + #stdin.flush() + + # I'm suspicious of this... + # ...other SSH clients have buffering issues with this approach + stdout = stdout_stream.read() + stderr = stderr_stream.read() + stdin_stream.close() + + exit_status = channel.recv_exit_status() + + # exit_status == -1 if no exit code was returned + if exit_status != -1: + LOG.debug(_("Result was %s") % exit_status) + if check_exit_code and exit_status != 0: + raise exception.ProcessExecutionError(exit_code=exit_status, + stdout=stdout, + stderr=stderr, + cmd=cmd) + + # Removed, although this is workaround is needed in utils.execute: + # (no reason to believe it's needed here!) + # greenthread.sleep(0) + + return (stdout, stderr) -- cgit From 3cae5a2573c96900f224d0145cee5077b01424b5 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 4 Feb 2011 11:36:09 -0600 Subject: Don't swallow exception stack traces by doing 'raise e'; just use 'raise' --- nova/compute/api.py | 4 ++-- nova/volume/manager.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index ac02dbcfa..b409dc1e0 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -70,7 +70,7 @@ class API(base.Base): except exception.NotFound as e: LOG.warning(_("Instance %d was not found in get_network_topic"), instance_id) - raise e + raise host = instance['host'] if not host: @@ -296,7 +296,7 @@ class API(base.Base): except exception.NotFound as e: LOG.warning(_("Instance %d was not found during terminate"), instance_id) - raise e + raise if (instance['state_description'] == 'terminating'): LOG.warning(_("Instance %d is already being terminated"), diff --git a/nova/volume/manager.py b/nova/volume/manager.py index 6f8e25e19..3e63e7b26 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -114,7 +114,7 @@ class VolumeManager(manager.Manager): except Exception as e: self.db.volume_update(context, volume_ref['id'], {'status': 'error'}) - raise e + raise now = datetime.datetime.utcnow() self.db.volume_update(context, @@ -141,7 +141,7 @@ class VolumeManager(manager.Manager): self.db.volume_update(context, volume_ref['id'], {'status': 'error_deleting'}) - raise e + raise self.db.volume_destroy(context, volume_id) LOG.debug(_("volume %s: deleted successfully"), volume_ref['name']) -- cgit From 5315d5068c6250ccf71c1aa67e7e0109a0718f2a Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 4 Feb 2011 13:07:17 -0600 Subject: Fixes for Vish & Devin's feedback --- nova/utils.py | 35 +++++++++++++++++++++++++ nova/volume/san.py | 77 +++++++++++++----------------------------------------- 2 files changed, 53 insertions(+), 59 deletions(-) diff --git a/nova/utils.py b/nova/utils.py index 5f5225289..100e26f70 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -152,6 +152,41 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): return result +def ssh_execute(ssh, cmd, process_input=None, + addl_env=None, check_exit_code=True): + LOG.debug(_("Running cmd (SSH): %s"), cmd) + if addl_env: + raise exception.Error("Environment not supported over SSH") + + if process_input: + # This is (probably) fixable if we need it... + raise exception.Error("process_input not supported over SSH") + + stdin_stream, stdout_stream, stderr_stream = ssh.exec_command(cmd) + channel = stdout_stream.channel + + #stdin.write('process_input would go here') + #stdin.flush() + + # NOTE(justinsb): This seems suspicious... + # ...other SSH clients have buffering issues with this approach + stdout = stdout_stream.read() + stderr = stderr_stream.read() + stdin_stream.close() + + exit_status = channel.recv_exit_status() + + # exit_status == -1 if no exit code was returned + if exit_status != -1: + LOG.debug(_("Result was %s") % exit_status) + if check_exit_code and exit_status != 0: + raise exception.ProcessExecutionError(exit_code=exit_status, + stdout=stdout, + stderr=stderr, + cmd=cmd) + + return (stdout, stderr) + def abspath(s): return os.path.join(os.path.dirname(__file__), s) diff --git a/nova/volume/san.py b/nova/volume/san.py index 22a0da13f..1f3417ae5 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -26,6 +26,7 @@ import paramiko from nova import exception from nova import flags from nova import log as logging +from nova.utils import ssh_execute from nova.volume.driver import ISCSIDriver LOG = logging.getLogger("nova.volume.driver") @@ -38,8 +39,6 @@ flags.DEFINE_string('san_login', 'admin', 'Username for SAN controller') flags.DEFINE_string('san_password', '', 'Password for SAN controller') -flags.DEFINE_string('san_cluster_name', '', - 'Cluster name for creating SAN volumes') flags.DEFINE_string('san_privatekey', '', 'Filename of private key to use for SSH authentication') @@ -148,17 +147,22 @@ def _get_prefixed_values(data, prefix): return matches class SolarisISCSIDriver(SanISCSIDriver): - """Executes commands relating to Solaris-hosted ISCSI volumes.""" - - #pkg install storage-server SUNWiscsit - #svcadm enable stmf - #zfs allow justinsb create,mount,destroy rpool - #usermod -P'File System Management' justinsb - # I don't know which perms are needed for sbdadm... - #usermod -P'Primary Administrator' justinsb - # svcadm enable -r svc:/network/iscsi/target:default - # pfexec itadm create-tpg e1000g0 10.1.184.98 - # pfexec itadm create-target -t e1000g0 + """Executes commands relating to Solaris-hosted ISCSI volumes. + Basic setup for a Solaris iSCSI server: + pkg install storage-server SUNWiscsit + svcadm enable stmf + svcadm enable -r svc:/network/iscsi/target:default + pfexec itadm create-tpg e1000g0 ${MYIP} + pfexec itadm create-target -t e1000g0 + + Then grant the user that will be logging on lots of permissions. + I'm not sure exactly which though: + zfs allow justinsb create,mount,destroy rpool + usermod -P'File System Management' justinsb + usermod -P'Primary Administrator' justinsb + + Also make sure you can login using san_login & san_password/san_privatekey + """ def _view_exists(self, luid): (out, _err) = self._run_ssh("pfexec /usr/sbin/stmfadm list-view -l %s" % @@ -280,19 +284,13 @@ class SolarisISCSIDriver(SanISCSIDriver): iscsi_name = self._build_iscsi_target_name(volume) target_group_name = 'tg-%s' % volume['name'] - # The sequence of commands looks like this: - #pfexec stmfadm create-tg tg-vol2 - # Yes, we add it before we create it! - #pfexec stmfadm add-tg-member -g tg-vol2 iqn.2010-10.org.openstack:vol2 - #pfexec itadm create-target -n iqn.2010-10.org.openstack:vol2 - #pfexec stmfadm add-view -t tg-vol2 600144F051E2440000004D4A0F730004 - # Create a iSCSI target, mapped to just this volume if force_create or not self._target_group_exists(target_group_name): self._run_ssh("pfexec /usr/sbin/stmfadm create-tg %s" % (target_group_name)) # Yes, we add the initiatior before we create it! + # Otherwise, it complains that the target is already active if force_create or not self._is_target_group_member(target_group_name, iscsi_name): self._run_ssh("pfexec /usr/sbin/stmfadm add-tg-member -g %s %s" % @@ -332,42 +330,3 @@ class SolarisISCSIDriver(SanISCSIDriver): self._run_ssh("pfexec /usr/sbin/sbdadm delete-lu %s" % (luid)) - -def ssh_execute(ssh, cmd, process_input=None, - addl_env=None, check_exit_code=True): - LOG.debug(_("Running cmd (subprocess): %s"), cmd) - if addl_env: - raise exception.Error("Environment not supported over SSH") - - if process_input: - # This is (probably) fixable if we need it... - raise exception.Error("process_input not supported over SSH") - - stdin_stream, stdout_stream, stderr_stream = ssh.exec_command(cmd) - channel = stdout_stream.channel - - #stdin.write('process_input would go here') - #stdin.flush() - - # I'm suspicious of this... - # ...other SSH clients have buffering issues with this approach - stdout = stdout_stream.read() - stderr = stderr_stream.read() - stdin_stream.close() - - exit_status = channel.recv_exit_status() - - # exit_status == -1 if no exit code was returned - if exit_status != -1: - LOG.debug(_("Result was %s") % exit_status) - if check_exit_code and exit_status != 0: - raise exception.ProcessExecutionError(exit_code=exit_status, - stdout=stdout, - stderr=stderr, - cmd=cmd) - - # Removed, although this is workaround is needed in utils.execute: - # (no reason to believe it's needed here!) - # greenthread.sleep(0) - - return (stdout, stderr) -- cgit From b65e994d9597f0a989b30eafc7a51bc34c4c361f Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 4 Feb 2011 15:13:18 -0600 Subject: Added a bunch of stubbed out functionality --- nova/compute/api.py | 22 +++++--- nova/compute/manager.py | 65 +++++++++++++++++++--- nova/db/api.py | 22 +++++++- nova/db/sqlalchemy/api.py | 53 ++++++++++++++++++ nova/db/sqlalchemy/models.py | 12 +++- nova/virt/xenapi/vmops.py | 18 +++++- nova/virt/xenapi_conn.py | 11 ++++ plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 4 ++ 8 files changed, 187 insertions(+), 20 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 283845709..f0d5ff2cb 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -379,6 +379,10 @@ class API(base.Base): kwargs = {'method': method, 'args': params} return rpc.call(context, queue, kwargs) + def _cast_scheduler_message(self, context, args) + """Generic handler for RPC calls to the scheduler""" + rpc.cast(context, FLAGS.scheduler_topic, args) + def snapshot(self, context, instance_id, name): """Snapshot the given instance. @@ -397,21 +401,23 @@ class API(base.Base): def revert_resize(self, context, instance_id): """Reverts a resize, deleting the 'new' instance in the process""" - raise NotImplemented() + instance_ref = self.db.instance_get(instance_id) + self._cast_compute_message('revert_resize', context, instance_id, + instance_ref['host']) def confirm_resize(self, context, instance_id): """Confirms a migration/resize, deleting the 'old' instance in the process.""" - raise NotImplemented() + migration_ref = self.db.get_migration_by_instance_id(instance_id) + self._cast_compute_message('confirm_resize', context, instance_id, + migration_ref['source_host']) def resize(self, context, instance_id, flavor): """Resize a running instance.""" - rpc.cast(context, - FLAGS.scheduler_topic, - {"method": "resize_instance", - "args": {"topic": FLAGS.compute_topic, - "instance_id": instance_id, - "flavor": flavor}}) + self._cast_scheduler_message(context, + {"method": "prep_resize", + "args": {"topic": FLAGS.compute_topic, + "instance_id": instance_id, }},) def pause(self, context, instance_id): """Pause the given instance.""" diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 41ef23980..140db0d3e 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -380,18 +380,67 @@ class ComputeManager(manager.Manager): """Update instance state when async task completes.""" self._update_state(context, instance_id) + @exception.wrap_exception @checks_instance_lock - def resize_instance(self, context, instance_id, flavor_id): - """Moves a running instance to another host, possibly changing the RAM - and disk size in the process""" + def prep_resize(self, context, instance_id): + """Initiates the process of moving a running instance to another + host, possibly changing the RAM and disk size in the process""" context = context.elevated() instance_ref = self.db.instance_get(context. instance_id) - LOG.audit(_('instance %s: migrating'), instance_id, context=context) - self.db.instance_set_state(context, instance_id, power_state.RUNNING, - 'migrating') - self.driver.resize(instance_ref, flavor_id) - self._update_state(context, instance_id) + migration_ref = self.db.migration_create(context, + { 'instance_id': instance_id, + 'source_host': instance_ref['host'], + 'dest_host': socket.gethostbyname(socket.gethostname()), + 'status': 'pre-migrating' } + LOG.audit(_('instance %s: migrating to '), instance_id, context=context) + service = self.db.service_get_by_host_and_topic(context, + instance_ref['host'], topic) + topic = self.db.queue_get_for(context, topic, service['id']) + rpc.cast(context, topic, + { 'method': 'resize_instance', + 'migration_id': migration_ref['id'], } + + @exception.wrap_exception + @checks_instance_lock + def resize_instance(self, context, migration_id): + """Starts the migration of a running instance to another host""" + migration_ref = self.db.migration_get(context, migration_id) + self.db.migration_update(context, migration_id, + { 'status': 'migrating', }) + self.driver.transfer_disk(context, instance_id, + migration_ref['dest_host']) + self.db.migration_update(context, migration_id, + { 'status': 'post-migrating', }) + + self.driver.power_off(context, migration_ref['instance_id']) + # This is where we would update the VM record after resizing + + service = self.db.service_get_by_host_and_topic(context, + migration_ref['dest_host'], topic) + topic = self.db.queue_get_for(context, topic, service['id']) + rpc.cast(context, topic, + { 'method': 'finish_resize', + 'migration_id': migration_ref['id'], } + + @exception.wrap_exception + @checks_instance_lock + def finish_resize(self, context, migration_id): + """Completes the migration process by setting up the newly transferred + disk and turning on the instance on its new host machine""" + migration_ref = self.db.migration_get(context, migration_id) + instance_ref = self.db.instance_get(context, + migration_ref['instance_id']) + + # this may get passed into the following spawn instead + self.driver.attach_disk(context, migration_ref['instance_id']) + self.driver.spawn(context, instance_ref, preexisting=True) + + self.db.migration_update(context, migration_id, + {'status': 'finished', }) + + # Cleans up any transferred files and unmounts things + self.driver.cleanup_disk_transfer(context, instance_ref['id']) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/api.py b/nova/db/api.py index 789cb8ebb..5da0e9840 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -80,10 +80,15 @@ def service_destroy(context, instance_id): def service_get(context, service_id): - """Get an service or raise if it does not exist.""" + """Get a service or raise if it does not exist.""" return IMPL.service_get(context, service_id) +def service_get_by_host_and_topic(context, host, topic): + """Get a service by host it's on and topic it listens to""" + return IMPL.service_get(context, host, topic) + + def service_get_all(context, disabled=False): """Get all service.""" return IMPL.service_get_all(context, None, disabled) @@ -255,6 +260,21 @@ def floating_ip_get_by_address(context, address): #################### +def migration_create(context, values): + """Create a migration record""" + return IMPL.migration_create(context, values) + +def migration_get(context, migration_id): + """Finds a migration by the id""" + return IMPL.migration_get(context, migration_id) + +def migration_get_by_instance_id(context, instance_id): + """Finds a migration by the instance id its migrating""" + return IMPL.migration_get_by_instance_id(context, instance_id) + +#################### + + def fixed_ip_associate(context, address, instance_id): """Associate fixed ip to instance. diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 85250d56e..e94f9f4d2 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -156,6 +156,15 @@ def service_get_all_by_topic(context, topic): filter_by(topic=topic).\ all() +@require_admin_context +def service_get_by_host_and_topic(context, host, topic): + session = get_session() + return session.query(models.Service).\ + filter_by(deleted=False).\ + filter_by(disabled=False).\ + filter_by(host=host).\ + filter_by(topic=topic).\ + all() @require_admin_context def service_get_all_by_host(context, host): @@ -1909,6 +1918,50 @@ def host_get_networks(context, host): all() +################### + + +@require_admin_context +def migration_create(context, values): + migration = models.Migration() + migration.update(values) + migration.save() + return migration + + +@require_admin_context +def migration_update(context, migration_id, values): + session = get_session() + with session.begin(): + migration = migration_get(context, migration_id, session=session) + migration.update(values) + return migration + + +@require_admin_context +def migration_get(context, migration_id): + session = get_session() + result = session.query(models.Migration.\ + filter_by(migration_id=migration_id)). + first() + if not result: + raise exception.NotFound(_("No migration found with id %s") + % migration_id) + return result + + +@require_admin_context +def migration_get_by_instance(context, instance_id): + session = get_session() + result = session.query(models.Migration.\ + filter_by(instance_id=instance_id)). + first() + if not result: + raise exception.NotFound(_("No migration found with instance id %s") + % migration_id) + return result + + ################## diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 7efb36c0e..499275504 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -366,6 +366,15 @@ class KeyPair(BASE, NovaBase): public_key = Column(Text) +class Migration(BASE, NovaBase): + """Represents a running host-to-host migration.""" + __tablename__ = 'migrations' + source_host = Column(String(255)) + dest_host = Column(String(255)) + instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True) + status = Column(String(255)) #TODO(_cerberus_): enum + + class Network(BASE, NovaBase): """Represents a network.""" __tablename__ = 'networks' @@ -547,7 +556,8 @@ def register_models(): Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, - Project, Certificate, ConsolePool, Console) # , Image, Host + Project, Certificate, ConsolePool, Console, + Migration) # , Image, Host engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: model.metadata.create_all(engine) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3f9eb39d1..468881355 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -60,6 +60,15 @@ class VMOps(object): vms.append(rec["name_label"]) return vms + def power_on(self, instance): + """Power on a VM instance""" + vm = VMHelper.lookup(self._session, instance.name) + if vm is None: + raise exception(_('Attempted to power on non-existent instance' + ' bad instance id %s') % instance.id) + LOG.debug(_("Starting instance %s"), instance.name) + self._session.call_xenapi('VM.start', vm, False, False) + def spawn(self, instance): """Create VM instance""" vm = VMHelper.lookup(self._session, instance.name) @@ -259,7 +268,8 @@ class VMOps(object): raise RuntimeError(resp_dict['message']) return resp_dict['message'] - def _shutdown(self, instance, vm): + + def _shutdown(self, instance, vm, method='hard'): """Shutdown an instance """ state = self.get_info(instance['name'])['state'] if state == power_state.SHUTDOWN: @@ -268,7 +278,11 @@ class VMOps(object): return try: - task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) + task = None + if method == 'clean': + task = self._session.call_xenapi('Async.VM.clean_shutdown', vm) + else: + task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) self._session.wait_for_task(instance.id, task) except self.XenAPI.Failure, exc: LOG.exception(exc) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 4637b4c29..628291764 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -184,6 +184,17 @@ class XenAPIConnection(object): """Unpause paused VM instance""" self._vmops.unpause(instance, callback) + def power_off(self, instance): + """Shuts down a running VM instance""" + self._vmops._shutdown(instance, method='clean') + + def power_on(self, instance): + """powers on a powered off VM instance""" + self._vmops.power_on(instance) + + def transfer_disk(self, instance, dest, callback): + self._vmops.transfer_disk( + def suspend(self, instance, callback): """suspend the specified instance""" self._vmops.suspend(instance, callback) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index aadacce57..3b7ceacc9 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -54,6 +54,10 @@ def copy_kernel_vdi(session,args): _copy_kernel_vdi('/dev/%s' % dev,copy_args)) return filename +def transfer_disk(dest, args): + vdi = exists(args, 'vdi-ref') + + def _copy_kernel_vdi(dest,copy_args): vdi_uuid=copy_args['vdi_uuid'] vdi_size=copy_args['vdi_size'] -- cgit From 855b9443cf109302e9882d527f237049b9624a05 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 4 Feb 2011 15:43:41 -0600 Subject: Didn't mean to actually make changes to the glance plugin --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 3b7ceacc9..aadacce57 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -54,10 +54,6 @@ def copy_kernel_vdi(session,args): _copy_kernel_vdi('/dev/%s' % dev,copy_args)) return filename -def transfer_disk(dest, args): - vdi = exists(args, 'vdi-ref') - - def _copy_kernel_vdi(dest,copy_args): vdi_uuid=copy_args['vdi_uuid'] vdi_size=copy_args['vdi_size'] -- cgit From 645f4733081dfe03554cc30221ccc1a8b359d1ea Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 4 Feb 2011 16:20:24 -0600 Subject: Removed (newly) unused exception variables --- nova/compute/api.py | 4 ++-- nova/volume/manager.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index b409dc1e0..4a7c1c9e9 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -67,7 +67,7 @@ class API(base.Base): """Get the network topic for an instance.""" try: instance = self.get(context, instance_id) - except exception.NotFound as e: + except exception.NotFound: LOG.warning(_("Instance %d was not found in get_network_topic"), instance_id) raise @@ -293,7 +293,7 @@ class API(base.Base): LOG.debug(_("Going to try to terminate %s"), instance_id) try: instance = self.get(context, instance_id) - except exception.NotFound as e: + except exception.NotFound: LOG.warning(_("Instance %d was not found during terminate"), instance_id) raise diff --git a/nova/volume/manager.py b/nova/volume/manager.py index 3e63e7b26..5771a6384 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -111,7 +111,7 @@ class VolumeManager(manager.Manager): LOG.debug(_("volume %s: creating export"), volume_ref['name']) self.driver.create_export(context, volume_ref) - except Exception as e: + except Exception: self.db.volume_update(context, volume_ref['id'], {'status': 'error'}) raise @@ -137,7 +137,7 @@ class VolumeManager(manager.Manager): self.driver.remove_export(context, volume_ref) LOG.debug(_("volume %s: deleting"), volume_ref['name']) self.driver.delete_volume(volume_ref) - except Exception as e: + except Exception: self.db.volume_update(context, volume_ref['id'], {'status': 'error_deleting'}) -- cgit From d86f1af6326d4276e9cbfb3274c211ff3f5629cb Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 4 Feb 2011 17:03:37 -0800 Subject: allow for bridge to be the public interface --- nova/network/linux_net.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 3a8bd9435..69389b333 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -206,6 +206,11 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) + # NOTE(vish): If the public interface is the same as the + # bridge, then the bridge has to be in promiscuous + # to forward packets properly. + if(FLAGS.public_interface == bridge): + _execute("sudo ifconfig %s promisc" % bridge) if interface: # NOTE(vish): This will break if there is already an ip on the # interface, so we move any ips to the bridge -- cgit From e283bd21babc245f691e3ca394c5c2b2484a4022 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Sat, 5 Feb 2011 05:36:48 +0000 Subject: Launchpad automatic translations update. --- locale/ast.po | 2 +- locale/da.po | 2 +- locale/es.po | 2 +- locale/it.po | 2 +- locale/ja.po | 2 +- locale/pt_BR.po | 2 +- locale/ru.po | 2 +- locale/uk.po | 2 +- locale/zh_CN.po | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/locale/ast.po b/locale/ast.po index 310f299be..6e224f235 100644 --- a/locale/ast.po +++ b/locale/ast.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" +"X-Launchpad-Export-Date: 2011-02-05 05:36+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/da.po b/locale/da.po index ac1b593b0..f845f11b0 100644 --- a/locale/da.po +++ b/locale/da.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" +"X-Launchpad-Export-Date: 2011-02-05 05:36+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/es.po b/locale/es.po index 28f10c481..8d4f90b26 100644 --- a/locale/es.po +++ b/locale/es.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" +"X-Launchpad-Export-Date: 2011-02-05 05:36+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/it.po b/locale/it.po index f08497bac..3f439f9dd 100644 --- a/locale/it.po +++ b/locale/it.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" +"X-Launchpad-Export-Date: 2011-02-05 05:36+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/ja.po b/locale/ja.po index b11bb67b0..2cea24640 100644 --- a/locale/ja.po +++ b/locale/ja.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" +"X-Launchpad-Export-Date: 2011-02-05 05:36+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/pt_BR.po b/locale/pt_BR.po index c778bb631..e57f7304a 100644 --- a/locale/pt_BR.po +++ b/locale/pt_BR.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" +"X-Launchpad-Export-Date: 2011-02-05 05:36+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/ru.po b/locale/ru.po index fc97d5603..5d031ac08 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" +"X-Launchpad-Export-Date: 2011-02-05 05:36+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/uk.po b/locale/uk.po index be2371dbc..f3e217690 100644 --- a/locale/uk.po +++ b/locale/uk.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" +"X-Launchpad-Export-Date: 2011-02-05 05:36+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 diff --git a/locale/zh_CN.po b/locale/zh_CN.po index feee54cfe..6bc231e50 100644 --- a/locale/zh_CN.po +++ b/locale/zh_CN.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-04 05:31+0000\n" +"X-Launchpad-Export-Date: 2011-02-05 05:36+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/twistd.py:268 -- cgit From 25a5afbb783e28bd5303853bf09e4b254c938302 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 01:14:45 -0800 Subject: added FIXME(kpepple) comments for all constant usage of INSTANCE_TYPES. updated api/ec2/admin.py to use the new instance_types db table --- bin/nova-manage | 11 ++++++----- nova/api/ec2/admin.py | 19 ++++++++++++++----- nova/api/openstack/flavors.py | 4 ++-- nova/compute/api.py | 3 ++- nova/compute/instance_types.py | 8 ++++---- nova/db/api.py | 5 ++++- nova/db/sqlalchemy/api.py | 9 ++++++--- .../db/sqlalchemy/migrate_repo/versions/003_cactus.py | 2 +- nova/tests/api/openstack/test_flavors.py | 18 ++++++++++++------ nova/tests/db/fakes.py | 2 +- nova/tests/test_quota.py | 6 +++--- nova/tests/test_xenapi.py | 2 +- nova/virt/libvirt_conn.py | 4 ++-- nova/virt/xenapi/vm_utils.py | 2 +- 14 files changed, 59 insertions(+), 36 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 0406a2dd9..5a4875907 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -617,23 +617,24 @@ class InstanceTypesCommands(object): def create(self, name, memory, vcpus, local_gb): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" - db.instance_type_create(context.get_admin_context(), name, memory, vcpus, local_gb) + db.instance_type_create(context.get_admin_context(), + name, memory, vcpus, local_gb) def delete(self, name): """Marks instance types / flavors as deleted arguments: name""" ctxt = context.get_admin_context() # check to see if it exists - db.instance_type_delete(context,name) + db.instance_type_delete(context, name) def list(self): """Lists all instance types / flavors arguments: """ instance_types = db.instance_type_get_all(context.get_admin_context()) for instance in instance_types: - print "%s : %s memory (MB), %s vcpus, %s storage(GB)" % (instance.name, - instance.memory_mb, instance.vcpus, - instance.local_gb) + print "%s : %s memory (MB), %s vcpus, %s storage(GB)" % + (instance.name, instance.memory_mb, instance.vcpus, + instance.local_gb) CATEGORIES = [ diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 55cca1041..5a9e22bf7 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -63,8 +63,8 @@ def host_dict(host): return {} -def instance_dict(name, inst): - return {'name': name, +def instance_dict(inst): + return {'name': inst['name'], 'memory_mb': inst['memory_mb'], 'vcpus': inst['vcpus'], 'disk_gb': inst['local_gb'], @@ -79,10 +79,19 @@ class AdminController(object): def __str__(self): return 'AdminController' - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) this is untested code path. def describe_instance_types(self, _context, **_kwargs): - return {'instanceTypeSet': [instance_dict(n, v) for n, v in - instance_types.INSTANCE_TYPES.iteritems()]} + """Returns all active instance types data (vcpus, memory, etc.)""" + # return {'instanceTypeSet': [instance_dict(n, v) for n, v in + # instance_types.INSTANCE_TYPES.iteritems()]} + return {'instanceTypeSet': + [for i in db.instance_type_get_all(): instance_dict(i)]} + + # FIXME(kpepple) this is untested code path. + def describe_instance_type(self, _context, name, **_kwargs): + """Returns a specific active instance types data""" + return {'instanceTypeSet': + [instance_dict(db.instance_type_get_by_name(name))]} def describe_user(self, _context, name, **_kwargs): """Returns user data, including access and secret keys.""" diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 1f5185134..2416088f9 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -45,7 +45,7 @@ class Controller(wsgi.Controller): def show(self, req, id): """Return data about the given flavor id.""" - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors for name, val in instance_types.INSTANCE_TYPES.iteritems(): if val['flavorid'] == int(id): item = dict(ram=val['memory_mb'], disk=val['local_gb'], @@ -55,5 +55,5 @@ class Controller(wsgi.Controller): def _all_ids(self): """Return the list of all flavorids.""" - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors return [i['flavorid'] for i in instance_types.INSTANCE_TYPES.values()] diff --git a/nova/compute/api.py b/nova/compute/api.py index 3ae722226..78a34dff2 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -90,7 +90,8 @@ class API(base.Base): other arguments check out ok.""" # FIXME(kpepple) this needs to be changed from using the old constant - #type_data = db.instance_type_get_by_name(context.get_admin_context(), instance_type) + #type_data = db.instance_type_get_by_name(context.get_admin_context(), + # instance_type) type_data = instance_types.INSTANCE_TYPES[instance_type] num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 0594aa063..449ec1d2e 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -25,7 +25,7 @@ from nova import flags from nova import exception FLAGS = flags.FLAGS -# FIX-ME(kpepple) for dynamic flavors +# FIXME(kpepple) for dynamic flavors INSTANCE_TYPES = { 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), @@ -36,9 +36,9 @@ INSTANCE_TYPES = { def get_by_type(instance_type): """Build instance data structure and save it to the data store.""" - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors if instance_type is None: - return FLAGS.default_instance_type + return FLAGS.default_instance_type if instance_type not in INSTANCE_TYPES: raise exception.ApiError(_("Unknown instance type: %s"), instance_type) @@ -46,7 +46,7 @@ def get_by_type(instance_type): def get_by_flavor_id(flavor_id): - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors for instance_type, details in INSTANCE_TYPES.iteritems(): if details['flavorid'] == flavor_id: return instance_type diff --git a/nova/db/api.py b/nova/db/api.py index ffb1d08ce..d5091794b 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -994,14 +994,17 @@ def instance_type_create(context, values): """Create a new instance type""" return IMPL.instance_type_create(context, values) + def instance_type_get_all(context): """Get all instance types""" return IMPL.instance_type_get_all(context) + def instance_type_get_by_name(context, name): """Get instance type by name""" return IMPL.instance_type_get_by_name(context, name) + def instance_type_destroy(context, name): """Delete a instance type""" - return IMPL.instance_type_destroy(context,name) + return IMPL.instance_type_destroy(context, name) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index aa6434b65..142b1aa33 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2030,10 +2030,13 @@ def instance_type_create(context, values): instance_type_ref.save() return instance_type_ref + def instance_type_get_all(context): session = get_session() return session.query(models.InstanceTypes).\ - filter_by(deleted=0) + filter_by(deleted=0).\ + all() + def instance_type_get_by_name(context, name): session = get_session() @@ -2041,11 +2044,11 @@ def instance_type_get_by_name(context, name): filter_by(name=name).\ first() + @require_admin_context -def instance_type_destroy(context,name): +def instance_type_destroy(context, name): session = get_session() instance_type_ref = session.query(models.InstanceTypes).\ filter_by(name=name) rows = instance_type_ref.update(dict(deleted=1)) return instance_type_ref - diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index c7e61dc05..6523b6b38 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -68,7 +68,7 @@ def upgrade(migrate_engine): # FIXME(kpepple) should we be seeding created_at / updated_at ? # the_time = time.strftime("%Y-%m-%d %H:%M:%S") i.execute({'name': name, 'memory_mb': values["memory_mb"], - 'vcpus': values["vcpus"], + 'vcpus': values["vcpus"], 'deleted': 0, 'local_gb': values["local_gb"], 'flavorid': values["flavorid"]}) except Exception: diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 0f0ed2e84..b416e02d6 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -47,19 +47,25 @@ class FlavorsTest(unittest.TestCase): def test_create_list_delete_favor(self): # create a new flavor starting_flavors = db.instance_type_get_all(self.context) - new_instance_type = dict(name="os1.big",memory_mb=512, vcpus=1, local_gb=120, flavorid=25) + new_instance_type = dict(name="os1.big", memory_mb=512, + vcpus=1, local_gb=120, flavorid=25) new_flavor = db.instance_type_create(self.context, new_instance_type) self.assertEqual(new_flavor["name"], new_instance_type["name"]) # retrieve the newly created flavor - retrieved_new_flavor = db.instance_type_get_by_name(self.context, new_instance_type["name"]) + retrieved_new_flavor = db.instance_type_get_by_name( + self.context, + new_instance_type["name"]) # self.assertEqual(len(tuple(retrieved_new_flavor)),1) - self.assertEqual(retrieved_new_flavor["memory_mb"], new_instance_type["memory_mb"]) + self.assertEqual(retrieved_new_flavor["memory_mb"], + new_instance_type["memory_mb"]) flavors = db.instance_type_get_all(self.context) self.assertNotEqual(starting_flavors, flavors) # delete the newly created flavor - delete_query = db.instance_type_destroy(self.context,new_instance_type["name"]) - retrieve_deleted_flavor = db.instance_type_get_by_name(self.context, new_instance_type["name"]) - self.assertEqual(retrieve_deleted_flavor["deleted"], 1) + delete_query = db.instance_type_destroy(self.context, + new_instance_type["name"]) + deleted_flavor = db.instance_type_get_by_name(self.context, + new_instance_type["name"]) + self.assertEqual(deleted_flavor["deleted"], 1) if __name__ == '__main__': diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 6cf917d76..3b47fc867 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -44,7 +44,7 @@ def stub_out_db_instance_api(stubs): def fake_instance_create(values): """ Stubs out the db.instance_create method """ - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[values['instance_type']] base_options = { diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index f2c9c456f..c70803e05 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -75,18 +75,18 @@ class QuotaTestCase(test.TestCase): def test_quota_overrides(self): """Make sure overriding a projects quotas works""" - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 2) db.quota_create(self.context, {'project_id': self.project.id, 'instances': 10}) - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 4) db.quota_update(self.context, self.project.id, {'cores': 100}) - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 10) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index e38bd4dab..1d42f8da3 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -225,7 +225,7 @@ class XenAPIVMTestCase(test.TestCase): vm = vms[0] # Check that m1.large above turned into the right thing. - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors instance_type = instance_types.INSTANCE_TYPES['m1.large'] mem_kib = long(instance_type['memory_mb']) << 10 mem_bytes = str(mem_kib << 10) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9ed6d4a71..e66115464 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -611,7 +611,7 @@ class LibvirtConnection(object): user=user, project=project, size=size) - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[inst['instance_type']] if type_data['local_gb']: @@ -672,7 +672,7 @@ class LibvirtConnection(object): network = db.network_get_by_instance(context.get_admin_context(), instance['id']) # FIXME(vish): stick this in db - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors instance_type = instance['instance_type'] instance_type = instance_types.INSTANCE_TYPES[instance_type] ip_address = db.instance_get_fixed_address(context.get_admin_context(), diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4d09b2afa..3f0112609 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -82,7 +82,7 @@ class VMHelper(HelperBase): the pv_kernel flag indicates whether the guest is HVM or PV """ - # FIX-ME(kpepple) for dynamic flavors + # FIXME(kpepple) for dynamic flavors instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] mem = str(long(instance_type['memory_mb']) * 1024 * 1024) vcpus = str(instance_type['vcpus']) -- cgit From 79ea4533df3bd8c58b96177c2979fab2987a842a Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 02:45:53 -0800 Subject: converted openstack flavors over to use instance_types table. a few pep changes. --- bin/nova-manage | 5 ++--- nova/api/openstack/flavors.py | 22 ++++++++++++++-------- nova/db/api.py | 5 +++++ nova/db/sqlalchemy/api.py | 7 +++++++ .../sqlalchemy/migrate_repo/versions/003_cactus.py | 4 ++-- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 5a4875907..69285a42a 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -632,9 +632,8 @@ class InstanceTypesCommands(object): arguments: """ instance_types = db.instance_type_get_all(context.get_admin_context()) for instance in instance_types: - print "%s : %s memory (MB), %s vcpus, %s storage(GB)" % - (instance.name, instance.memory_mb, instance.vcpus, - instance.local_gb) + print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % (instance.name, + instance.memory_mb, instance.vcpus, instance.local_gb) CATEGORIES = [ diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 2416088f9..7440af0b4 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -17,6 +17,8 @@ from webob import exc +from nova import db +from nova import context from nova.api.openstack import faults from nova.api.openstack import common from nova.compute import instance_types @@ -45,15 +47,19 @@ class Controller(wsgi.Controller): def show(self, req, id): """Return data about the given flavor id.""" - # FIXME(kpepple) for dynamic flavors - for name, val in instance_types.INSTANCE_TYPES.iteritems(): - if val['flavorid'] == int(id): - item = dict(ram=val['memory_mb'], disk=val['local_gb'], - id=val['flavorid'], name=name) - return dict(flavor=item) + # FIXME(kpepple) do we need admin context here ? + ctxt = context.get_admin_context() + val = db.instance_type_get_by_flavor_id(ctxt, id) + item = dict(ram=val['memory_mb'], disk=val['local_gb'], + id=val['flavorid'], name=val['name']) + return dict(flavor=item) raise faults.Fault(exc.HTTPNotFound()) def _all_ids(self): """Return the list of all flavorids.""" - # FIXME(kpepple) for dynamic flavors - return [i['flavorid'] for i in instance_types.INSTANCE_TYPES.values()] + # FIXME(kpepple) do we need admin context here ? + ctxt = context.get_admin_context() + flavor_ids = [] + for i in db.instance_type_get_all(ctxt): + flavor_ids.append(i['flavorid']) + return flavor_ids diff --git a/nova/db/api.py b/nova/db/api.py index d5091794b..2f1903ade 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1005,6 +1005,11 @@ def instance_type_get_by_name(context, name): return IMPL.instance_type_get_by_name(context, name) +def instance_type_get_by_flavor_id(context, id): + """Get instance type by name""" + return IMPL.instance_type_get_by_flavor_id(context, id) + + def instance_type_destroy(context, name): """Delete a instance type""" return IMPL.instance_type_destroy(context, name) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 142b1aa33..6c9af6e19 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2045,6 +2045,13 @@ def instance_type_get_by_name(context, name): first() +def instance_type_get_by_flavor_id(context, id): + session = get_session() + return session.query(models.InstanceTypes).\ + filter_by(flavorid=int(id)).\ + first() + + @require_admin_context def instance_type_destroy(context, name): session = get_session() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index 6523b6b38..c5ae9ea72 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -19,7 +19,7 @@ from nova import api from nova import db from nova import log as logging -import time +import datetime meta = MetaData() @@ -66,7 +66,7 @@ def upgrade(migrate_engine): i = instance_types.insert() for name, values in INSTANCE_TYPES.iteritems(): # FIXME(kpepple) should we be seeding created_at / updated_at ? - # the_time = time.strftime("%Y-%m-%d %H:%M:%S") + # now = datetime.datatime.utcnow() i.execute({'name': name, 'memory_mb': values["memory_mb"], 'vcpus': values["vcpus"], 'deleted': 0, 'local_gb': values["local_gb"], -- cgit From fcd0a7b245470054718c94adf0da6a528a01f173 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 13:49:38 -0800 Subject: corrected db.instance_types to return expect dict instead of lists. updated openstack flavors to expect dicts instead of lists. added deleted column to returned dict. --- bin/nova-manage | 5 ++-- nova/api/openstack/flavors.py | 12 +++++---- nova/db/sqlalchemy/api.py | 42 ++++++++++++++++++++++++++++---- nova/tests/api/openstack/test_flavors.py | 5 ++-- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 69285a42a..2db1c67bf 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -632,8 +632,9 @@ class InstanceTypesCommands(object): arguments: """ instance_types = db.instance_type_get_all(context.get_admin_context()) for instance in instance_types: - print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % (instance.name, - instance.memory_mb, instance.vcpus, instance.local_gb) + print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ + (instance.name, instance.memory_mb, instance.vcpus, + instance.local_gb) CATEGORIES = [ diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 7440af0b4..da38dd34d 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -17,7 +17,7 @@ from webob import exc -from nova import db +from nova import db from nova import context from nova.api.openstack import faults from nova.api.openstack import common @@ -50,8 +50,9 @@ class Controller(wsgi.Controller): # FIXME(kpepple) do we need admin context here ? ctxt = context.get_admin_context() val = db.instance_type_get_by_flavor_id(ctxt, id) - item = dict(ram=val['memory_mb'], disk=val['local_gb'], - id=val['flavorid'], name=val['name']) + v = val.values()[0] + item = dict(ram=v['memory_mb'], disk=v['local_gb'], + id=v['flavorid'], name=val.keys()[0]) return dict(flavor=item) raise faults.Fault(exc.HTTPNotFound()) @@ -60,6 +61,7 @@ class Controller(wsgi.Controller): # FIXME(kpepple) do we need admin context here ? ctxt = context.get_admin_context() flavor_ids = [] - for i in db.instance_type_get_all(ctxt): - flavor_ids.append(i['flavorid']) + inst_types = db.instance_type_get_all(ctxt) + for i in inst_types.keys(): + flavor_ids.append(inst_types[i]['flavorid']) return flavor_ids diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 6c9af6e19..83c88a3a0 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2032,24 +2032,56 @@ def instance_type_create(context, values): def instance_type_get_all(context): + """Returns a dict of all instance_types: + { 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3)} + """ session = get_session() - return session.query(models.InstanceTypes).\ + inst_types = session.query(models.InstanceTypes).\ filter_by(deleted=0).\ all() + inst_dict = {} + for i in inst_types: + inst_dict[i['name']] = dict(memory_mb=i['memory_mb'], + vcpus=i['vcpus'], + local_gb=i['local_gb'], + flavorid=i['flavorid'], + deleted=i['deleted']) + + return inst_dict def instance_type_get_by_name(context, name): + """ + Returns a dict of specific instance_type: + {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1)} + """ session = get_session() - return session.query(models.InstanceTypes).\ + inst_type = session.query(models.InstanceTypes).\ filter_by(name=name).\ first() + return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], + vcpus=inst_type['vcpus'], + local_gb=inst_type['local_gb'], + flavorid=inst_type['flavorid'], + deleted=inst_type['deleted'])} def instance_type_get_by_flavor_id(context, id): + """ + Returns a dict of specific flavor_id: + {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1)} + """ session = get_session() - return session.query(models.InstanceTypes).\ - filter_by(flavorid=int(id)).\ - first() + inst_type = session.query(models.InstanceTypes).\ + filter_by(flavorid=int(id)).\ + first() + return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], + vcpus=inst_type['vcpus'], + local_gb=inst_type['local_gb'], + flavorid=inst_type['flavorid'], + deleted=inst_type['deleted'])} @require_admin_context diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index b416e02d6..9287e8adf 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -55,8 +55,7 @@ class FlavorsTest(unittest.TestCase): retrieved_new_flavor = db.instance_type_get_by_name( self.context, new_instance_type["name"]) - # self.assertEqual(len(tuple(retrieved_new_flavor)),1) - self.assertEqual(retrieved_new_flavor["memory_mb"], + self.assertEqual(retrieved_new_flavor.values()[0]["memory_mb"], new_instance_type["memory_mb"]) flavors = db.instance_type_get_all(self.context) self.assertNotEqual(starting_flavors, flavors) @@ -65,7 +64,7 @@ class FlavorsTest(unittest.TestCase): new_instance_type["name"]) deleted_flavor = db.instance_type_get_by_name(self.context, new_instance_type["name"]) - self.assertEqual(deleted_flavor["deleted"], 1) + self.assertEqual(deleted_flavor.values()[0]["deleted"], 1) if __name__ == '__main__': -- cgit From 5cd5d4e9682848cba60a8dec352fe0f74aaa9eac Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 18:23:01 -0800 Subject: flavorid and name need to be unique in the database for the ec2 and openstack apis, repectively --- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index c5ae9ea72..f95996042 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -34,12 +34,13 @@ instance_types = Table('instance_types', meta, Column('deleted', Boolean(create_constraint=True, name=None)), Column('name', String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), + unicode_error=None, _warn_on_bytestring=False), + unique=True), Column('id', Integer(), primary_key=True, nullable=False), Column('memory_mb', Integer(), nullable=False), Column('vcpus', Integer(), nullable=False), Column('local_gb', Integer(), nullable=False), - Column('flavorid', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False, unique=True), ) -- cgit From d5a5324fee480152fd4e77f19fce5b025e1b4987 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 18:26:53 -0800 Subject: instance_types should return in predicatable order (by name currently) --- nova/db/sqlalchemy/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 83c88a3a0..0e2675a9f 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2040,6 +2040,7 @@ def instance_type_get_all(context): session = get_session() inst_types = session.query(models.InstanceTypes).\ filter_by(deleted=0).\ + order_by("name").\ all() inst_dict = {} for i in inst_types: @@ -2090,4 +2091,4 @@ def instance_type_destroy(context, name): instance_type_ref = session.query(models.InstanceTypes).\ filter_by(name=name) rows = instance_type_ref.update(dict(deleted=1)) - return instance_type_ref + return rows -- cgit From 12a9db3e767b6b88fac5ad1d16c0aef39c4a801f Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 18:27:51 -0800 Subject: rewrote nova-manage instance_type to use correct db.api returned objects and have more robust error handling --- bin/nova-manage | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 2db1c67bf..8e2c7962f 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -611,30 +611,51 @@ class VolumeCommands(object): class InstanceTypesCommands(object): """Class for managing instance types / flavors.""" - def usage(self): - print "$ nova-manage instance_type NAME MEMORY_MB VCPUS LOCAL_GB" - def create(self, name, memory, vcpus, local_gb): + def create(self, name, memory, vcpus, local_gb, flavorid): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" + # FIXME(kpepple) check for absurb arguments (?) + for option in [memory, flavorid, local_gb, vcpus]: + if option <= 0: + print "Instance type parameters must be positive \ + numbers: %s" % option + sys.exit(1) db.instance_type_create(context.get_admin_context(), - name, memory, vcpus, local_gb) + dict(name=name, memory_mb=memory, + vcpus=vcpus, local_gb=local_gb, + flavorid=flavorid)) + print "%s created" % name + return def delete(self, name): """Marks instance types / flavors as deleted arguments: name""" - ctxt = context.get_admin_context() - # check to see if it exists - db.instance_type_delete(context, name) + if name == None: + print "Instance type name must be supplied" + exit(1) + else: + records = db.instance_type_destroy(context.get_admin_context(),\ + name) + if records != 1: + sys.exit(1) + return - def list(self): + def list(self, name=None): """Lists all instance types / flavors - arguments: """ - instance_types = db.instance_type_get_all(context.get_admin_context()) - for instance in instance_types: + arguments: [name]""" + ctxt = context.get_admin_context() + if name == None: + instance_types = db.instance_type_get_all(ctxt) + if len(instance_types) < 1: + sys.exit(1) + else: + instance_types = db.instance_type_get_by_name(ctxt, name) + for k, v in instance_types.iteritems(): print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (instance.name, instance.memory_mb, instance.vcpus, - instance.local_gb) + (k, v["memory_mb"], + v["vcpus"], v["local_gb"]) + return CATEGORIES = [ -- cgit From 2acc31a293b067644f26877dc52bacb7ef9e9bd1 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 18:28:26 -0800 Subject: added preliminary testing for bin/nova-manage while i am somewhat conflicted about the path these tests have taken, i think it is better than no tests at all --- nova/tests/test_nova_manage.py | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 nova/tests/test_nova_manage.py diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py new file mode 100644 index 000000000..09ed8163b --- /dev/null +++ b/nova/tests/test_nova_manage.py @@ -0,0 +1,72 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +Tests For Nova-Manage +""" + +import os +import subprocess + +from nova import test + + +class NovaManageTestCase(test.TestCase): + """Test case for nova-manage""" + def setUp(self): + super(NovaManageTestCase, self).setUp() + + def teardown(self): + fnull.close() + + def test_create_and_delete_instance_types(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "create", "test", "256", "1",\ + "120", "99"], stdout=fnull) + self.assertEqual(0, retcode) + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "delete", "test"], stdout=fnull) + self.assertEqual(0, retcode) + + def test_list_instance_types(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type", \ + "list"], stdout=fnull) + self.assertEqual(0, retcode) + + def test_list_specific_instance_type(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type", "list", + "m1.medium"], stdout=fnull) + self.assertEqual(0, retcode) + + def test_should_raise_on_bad_create_args(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "create", "test", "256", "0",\ + "120", "99"], stdout=fnull) + self.assertEqual(1, retcode) + + def test_should_fail_on_duplicate_flavorid(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "create", "test", "256", "1",\ + "120", "1"], stdout=fnull) + self.assertEqual(1, retcode) + + def test_instance_type_delete_should_fail_without_valid_name(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "delete", "saefasff"], stdout=fnull) + self.assertEqual(1, retcode) -- cgit From 555e5b5a0d3ae30f5d8b77d6b2dc47a953b4a81b Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 5 Feb 2011 18:54:56 -0800 Subject: updated api.create to use instance_type table --- nova/compute/api.py | 7 +++---- nova/db/sqlalchemy/api.py | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 78a34dff2..006db880e 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -89,10 +89,9 @@ class API(base.Base): """Create the number of instances requested if quota and other arguments check out ok.""" - # FIXME(kpepple) this needs to be changed from using the old constant - #type_data = db.instance_type_get_by_name(context.get_admin_context(), - # instance_type) - type_data = instance_types.INSTANCE_TYPES[instance_type] + # FIXME(kpepple) this needs to be factored for api.py:2065 refactor + type_data = db.instance_type_get_by_name(context,\ + instance_type)[instance_type] num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: pid = context.project_id diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 0e2675a9f..eff03aa02 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2062,6 +2062,7 @@ def instance_type_get_by_name(context, name): inst_type = session.query(models.InstanceTypes).\ filter_by(name=name).\ first() + # FIXME(kpepple) this needs to be refactored to just return dict return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], vcpus=inst_type['vcpus'], local_gb=inst_type['local_gb'], @@ -2078,6 +2079,7 @@ def instance_type_get_by_flavor_id(context, id): inst_type = session.query(models.InstanceTypes).\ filter_by(flavorid=int(id)).\ first() + # FIXME(kpepple) this needs to be refactored to just return dict return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], vcpus=inst_type['vcpus'], local_gb=inst_type['local_gb'], -- cgit From ea5271ed69d72dcab8189c3bfc66220c7ff60862 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sun, 6 Feb 2011 10:56:05 -0800 Subject: refactor to remove ugly code in flavors --- nova/api/openstack/flavors.py | 9 ++++----- nova/tests/db/fakes.py | 2 ++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index da38dd34d..3124c26b2 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -47,9 +47,10 @@ class Controller(wsgi.Controller): def show(self, req, id): """Return data about the given flavor id.""" - # FIXME(kpepple) do we need admin context here ? + # FIXME(kpepple) do we really need admin context here ? ctxt = context.get_admin_context() val = db.instance_type_get_by_flavor_id(ctxt, id) + # FIXME(kpepple) refactor db call to return dict v = val.values()[0] item = dict(ram=v['memory_mb'], disk=v['local_gb'], id=v['flavorid'], name=val.keys()[0]) @@ -58,10 +59,8 @@ class Controller(wsgi.Controller): def _all_ids(self): """Return the list of all flavorids.""" - # FIXME(kpepple) do we need admin context here ? + # FIXME(kpepple) do we really need admin context here ? ctxt = context.get_admin_context() - flavor_ids = [] inst_types = db.instance_type_get_all(ctxt) - for i in inst_types.keys(): - flavor_ids.append(inst_types[i]['flavorid']) + flavor_ids = [inst_types[i]['flavorid'] for i in inst_types.keys()] return flavor_ids diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 3b47fc867..859ed6edc 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -46,6 +46,8 @@ def stub_out_db_instance_api(stubs): # FIXME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[values['instance_type']] + # type_data = db.instance_type_get_by_name(context,\ + # instance_type)[instance_type] base_options = { 'name': values['name'], -- cgit From 7dcdbcc546248c3384bd15975a721413e1d1f507 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sun, 6 Feb 2011 13:28:07 -0800 Subject: simplified instance_types db calls to return entire row - we may need these extra columns for some features and there seems to be little downside in including them. still need to fix testing calls. --- bin/nova-manage | 10 +++++--- nova/api/ec2/admin.py | 5 ++-- nova/api/openstack/flavors.py | 10 ++++---- nova/compute/api.py | 2 +- nova/db/sqlalchemy/api.py | 41 +++++++++----------------------- nova/tests/api/openstack/test_flavors.py | 22 ----------------- nova/tests/db/fakes.py | 2 +- 7 files changed, 28 insertions(+), 64 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 8e2c7962f..73d69fc64 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -649,12 +649,16 @@ class InstanceTypesCommands(object): instance_types = db.instance_type_get_all(ctxt) if len(instance_types) < 1: sys.exit(1) + for k, v in instance_types.iteritems(): + print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ + (k, v["memory_mb"], + v["vcpus"], v["local_gb"]) else: instance_types = db.instance_type_get_by_name(ctxt, name) - for k, v in instance_types.iteritems(): print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (k, v["memory_mb"], - v["vcpus"], v["local_gb"]) + (instance_types["name"], instance_types["memory_mb"], + instance_types["vcpus"], instance_types["local_gb"]) + return diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 5a9e22bf7..4c0628e21 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -84,8 +84,9 @@ class AdminController(object): """Returns all active instance types data (vcpus, memory, etc.)""" # return {'instanceTypeSet': [instance_dict(n, v) for n, v in # instance_types.INSTANCE_TYPES.iteritems()]} - return {'instanceTypeSet': - [for i in db.instance_type_get_all(): instance_dict(i)]} + # return {'instanceTypeSet': + # [for i in db.instance_type_get_all(): instance_dict(i)]} + return {'instanceTypeSet': [db.instance_type_get_all(_context)]} # FIXME(kpepple) this is untested code path. def describe_instance_type(self, _context, name, **_kwargs): diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 3124c26b2..9b674afbd 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -49,12 +49,12 @@ class Controller(wsgi.Controller): """Return data about the given flavor id.""" # FIXME(kpepple) do we really need admin context here ? ctxt = context.get_admin_context() - val = db.instance_type_get_by_flavor_id(ctxt, id) + values = db.instance_type_get_by_flavor_id(ctxt, id) # FIXME(kpepple) refactor db call to return dict - v = val.values()[0] - item = dict(ram=v['memory_mb'], disk=v['local_gb'], - id=v['flavorid'], name=val.keys()[0]) - return dict(flavor=item) + # v = val.values()[0] + # item = dict(ram=v['memory_mb'], disk=v['local_gb'], + # id=v['flavorid'], name=val.keys()[0]) + return dict(flavor=values) raise faults.Fault(exc.HTTPNotFound()) def _all_ids(self): diff --git a/nova/compute/api.py b/nova/compute/api.py index 006db880e..fc18765f6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -91,7 +91,7 @@ class API(base.Base): # FIXME(kpepple) this needs to be factored for api.py:2065 refactor type_data = db.instance_type_get_by_name(context,\ - instance_type)[instance_type] + instance_type) num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: pid = context.project_id diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index eff03aa02..f4e021ac8 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2032,10 +2032,11 @@ def instance_type_create(context, values): def instance_type_get_all(context): - """Returns a dict of all instance_types: - { 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3)} + """ + Returns a dict describing all instance_types with name as key: + {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3)} """ session = get_session() inst_types = session.query(models.InstanceTypes).\ @@ -2044,51 +2045,31 @@ def instance_type_get_all(context): all() inst_dict = {} for i in inst_types: - inst_dict[i['name']] = dict(memory_mb=i['memory_mb'], - vcpus=i['vcpus'], - local_gb=i['local_gb'], - flavorid=i['flavorid'], - deleted=i['deleted']) - + inst_dict[i['name']] = dict(i) return inst_dict def instance_type_get_by_name(context, name): - """ - Returns a dict of specific instance_type: - {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1)} - """ + """Returns a dict describing specific instance_type""" session = get_session() inst_type = session.query(models.InstanceTypes).\ filter_by(name=name).\ first() - # FIXME(kpepple) this needs to be refactored to just return dict - return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], - vcpus=inst_type['vcpus'], - local_gb=inst_type['local_gb'], - flavorid=inst_type['flavorid'], - deleted=inst_type['deleted'])} + return dict(inst_type) def instance_type_get_by_flavor_id(context, id): - """ - Returns a dict of specific flavor_id: - {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1)} - """ + """Returns a dict describing specific flavor_id""" session = get_session() inst_type = session.query(models.InstanceTypes).\ filter_by(flavorid=int(id)).\ first() - # FIXME(kpepple) this needs to be refactored to just return dict - return {inst_type['name']: dict(memory_mb=inst_type['memory_mb'], - vcpus=inst_type['vcpus'], - local_gb=inst_type['local_gb'], - flavorid=inst_type['flavorid'], - deleted=inst_type['deleted'])} + return dict(inst_type) @require_admin_context def instance_type_destroy(context, name): + """ Marks specific instance_type as deleted""" session = get_session() instance_type_ref = session.query(models.InstanceTypes).\ filter_by(name=name) diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 9287e8adf..532b34e1b 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -44,28 +44,6 @@ class FlavorsTest(unittest.TestCase): req = webob.Request.blank('/v1.0/flavors') res = req.get_response(fakes.wsgi_app()) - def test_create_list_delete_favor(self): - # create a new flavor - starting_flavors = db.instance_type_get_all(self.context) - new_instance_type = dict(name="os1.big", memory_mb=512, - vcpus=1, local_gb=120, flavorid=25) - new_flavor = db.instance_type_create(self.context, new_instance_type) - self.assertEqual(new_flavor["name"], new_instance_type["name"]) - # retrieve the newly created flavor - retrieved_new_flavor = db.instance_type_get_by_name( - self.context, - new_instance_type["name"]) - self.assertEqual(retrieved_new_flavor.values()[0]["memory_mb"], - new_instance_type["memory_mb"]) - flavors = db.instance_type_get_all(self.context) - self.assertNotEqual(starting_flavors, flavors) - # delete the newly created flavor - delete_query = db.instance_type_destroy(self.context, - new_instance_type["name"]) - deleted_flavor = db.instance_type_get_by_name(self.context, - new_instance_type["name"]) - self.assertEqual(deleted_flavor.values()[0]["deleted"], 1) - if __name__ == '__main__': unittest.main() diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 859ed6edc..3aa7fcd22 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -47,7 +47,7 @@ def stub_out_db_instance_api(stubs): # FIXME(kpepple) for dynamic flavors type_data = instance_types.INSTANCE_TYPES[values['instance_type']] # type_data = db.instance_type_get_by_name(context,\ - # instance_type)[instance_type] + # instance_type) base_options = { 'name': values['name'], -- cgit From ea5ba79802321bb25f03dfb24fd7fb01866d9921 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sun, 6 Feb 2011 13:48:03 -0800 Subject: aliased flavor to instance_types in nova-manage. will probably need to make flavor a full fledged class as users will want to list flavors by flavor name --- bin/nova-manage | 5 +++-- nova/tests/test_nova_manage.py | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 73d69fc64..c3fa9cf3f 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -609,7 +609,7 @@ class VolumeCommands(object): "mountpoint": volume['mountpoint']}}) -class InstanceTypesCommands(object): +class InstanceTypeCommands(object): """Class for managing instance types / flavors.""" def create(self, name, memory, vcpus, local_gb, flavorid): @@ -674,7 +674,8 @@ CATEGORIES = [ ('log', LogCommands), ('db', DbCommands), ('volume', VolumeCommands), - ('instance_types', InstanceTypesCommands)] + ('instance_type', InstanceTypeCommands), + ('flavor', InstanceTypeCommands)] def lazy_match(name, key_value_tuples): diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 09ed8163b..487f172ff 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -39,11 +39,12 @@ class NovaManageTestCase(test.TestCase): "delete", "test"], stdout=fnull) self.assertEqual(0, retcode) - def test_list_instance_types(self): + def test_list_instance_types_or_flavors(self): fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type", \ - "list"], stdout=fnull) - self.assertEqual(0, retcode) + for c in ["instance_type", "flavor"]: + retcode = subprocess.call(["bin/nova-manage", c, \ + "list"], stdout=fnull) + self.assertEqual(0, retcode) def test_list_specific_instance_type(self): fnull = open(os.devnull, 'w') -- cgit From 96ea3dd3db826440a7b52d32fa1663c17aa8394e Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Mon, 7 Feb 2011 05:52:49 +0000 Subject: Launchpad automatic translations update. --- locale/cs.po | 2130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2130 insertions(+) create mode 100644 locale/cs.po diff --git a/locale/cs.po b/locale/cs.po new file mode 100644 index 000000000..b9403687a --- /dev/null +++ b/locale/cs.po @@ -0,0 +1,2130 @@ +# Czech translation for nova +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the nova package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: nova\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-10 11:25-0800\n" +"PO-Revision-Date: 2011-02-07 04:36+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Czech \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-02-07 05:52+0000\n" +"X-Generator: Launchpad (build 12177)\n" + +#: nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "" + +#: nova/crypto.py:49 +msgid "Filename of private key" +msgstr "" + +#: nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "" + +#: nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "" + +#: nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "" + +#: nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "" + +#: nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "" + +#: nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "" + +#: nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "" + +#: nova/exception.py:36 +#, python-format +msgid "" +"%s\n" +"Command: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" +msgstr "" + +#: nova/exception.py:86 +msgid "Uncaught exception" +msgstr "" + +#: nova/fakerabbit.py:48 +#, python-format +msgid "(%s) publish (key: %s) %s" +msgstr "" + +#: nova/fakerabbit.py:53 +#, python-format +msgid "Publishing to route %s" +msgstr "" + +#: nova/fakerabbit.py:83 +#, python-format +msgid "Declaring queue %s" +msgstr "" + +#: nova/fakerabbit.py:89 +#, python-format +msgid "Declaring exchange %s" +msgstr "" + +#: nova/fakerabbit.py:95 +#, python-format +msgid "Binding %s to %s with key %s" +msgstr "" + +#: nova/fakerabbit.py:120 +#, python-format +msgid "Getting from %s: %s" +msgstr "" + +#: nova/rpc.py:92 +#, python-format +msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." +msgstr "" + +#: nova/rpc.py:99 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "" + +#: nova/rpc.py:118 +msgid "Reconnected to queue" +msgstr "" + +#: nova/rpc.py:125 +msgid "Failed to fetch message from queue" +msgstr "" + +#: nova/rpc.py:155 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "" + +#: nova/rpc.py:170 +#, python-format +msgid "received %s" +msgstr "" + +#: nova/rpc.py:183 +#, python-format +msgid "no method for message: %s" +msgstr "" + +#: nova/rpc.py:184 +#, python-format +msgid "No method for message: %s" +msgstr "" + +#: nova/rpc.py:245 +#, python-format +msgid "Returning exception %s to caller" +msgstr "" + +#: nova/rpc.py:286 +#, python-format +msgid "unpacked context: %s" +msgstr "" + +#: nova/rpc.py:305 +msgid "Making asynchronous call..." +msgstr "" + +#: nova/rpc.py:308 +#, python-format +msgid "MSG_ID is %s" +msgstr "" + +#: nova/rpc.py:356 +#, python-format +msgid "response %s" +msgstr "" + +#: nova/rpc.py:365 +#, python-format +msgid "topic is %s" +msgstr "" + +#: nova/rpc.py:366 +#, python-format +msgid "message %s" +msgstr "" + +#: nova/service.py:157 +#, python-format +msgid "Starting %s node" +msgstr "" + +#: nova/service.py:169 +msgid "Service killed that has no database entry" +msgstr "" + +#: nova/service.py:190 +msgid "The service database object disappeared, Recreating it." +msgstr "" + +#: nova/service.py:202 +msgid "Recovered model server connection!" +msgstr "" + +#: nova/service.py:208 +msgid "model server went away" +msgstr "" + +#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 +#, python-format +msgid "Data store %s is unreachable. Trying again in %d seconds." +msgstr "" + +#: nova/service.py:232 nova/twistd.py:232 +#, python-format +msgid "Serving %s" +msgstr "" + +#: nova/service.py:234 nova/twistd.py:264 +msgid "Full set of FLAGS:" +msgstr "" + +#: nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "" + +#: nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "" + +#: nova/utils.py:53 +#, python-format +msgid "Inner Exception: %s" +msgstr "" + +#: nova/utils.py:54 +#, python-format +msgid "Class %s cannot be found" +msgstr "" + +#: nova/utils.py:113 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: nova/utils.py:125 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "" + +#: nova/utils.py:138 +#, python-format +msgid "Result was %s" +msgstr "" + +#: nova/utils.py:171 +#, python-format +msgid "debug in callback: %s" +msgstr "" + +#: nova/utils.py:176 +#, python-format +msgid "Running %s" +msgstr "" + +#: nova/utils.py:207 +#, python-format +msgid "Couldn't get IP, using 127.0.0.1 %s" +msgstr "" + +#: nova/utils.py:289 +#, python-format +msgid "Invalid backend: %s" +msgstr "" + +#: nova/utils.py:300 +#, python-format +msgid "backend %s" +msgstr "" + +#: nova/api/ec2/__init__.py:133 +msgid "Too many failed authentications." +msgstr "" + +#: nova/api/ec2/__init__.py:142 +#, python-format +msgid "" +"Access key %s has had %d failed authentications and will be locked out for " +"%d minutes." +msgstr "" + +#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:190 +#, python-format +msgid "Authenticated Request For %s:%s)" +msgstr "" + +#: nova/api/ec2/__init__.py:227 +#, python-format +msgid "action: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:229 +#, python-format +msgid "arg: %s\t\tval: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:301 +#, python-format +msgid "Unauthorized request for controller=%s and action=%s" +msgstr "" + +#: nova/api/ec2/__init__.py:339 +#, python-format +msgid "NotFound raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:342 +#, python-format +msgid "ApiError raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:349 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:354 +msgid "An unknown error has occurred. Please try your request again." +msgstr "" + +#: nova/api/ec2/admin.py:84 +#, python-format +msgid "Creating new user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:92 +#, python-format +msgid "Deleting user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:114 +#, python-format +msgid "Adding role %s to user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 +#, python-format +msgid "Adding sitewide role %s to user %s" +msgstr "" + +#: nova/api/ec2/admin.py:122 +#, python-format +msgid "Removing role %s from user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 +#, python-format +msgid "Removing sitewide role %s from user %s" +msgstr "" + +#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 +msgid "operation must be add or remove" +msgstr "" + +#: nova/api/ec2/admin.py:142 +#, python-format +msgid "Getting x509 for user: %s on project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:159 +#, python-format +msgid "Create project %s managed by %s" +msgstr "" + +#: nova/api/ec2/admin.py:170 +#, python-format +msgid "Delete project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 +#, python-format +msgid "Adding user %s to project %s" +msgstr "" + +#: nova/api/ec2/admin.py:188 +#, python-format +msgid "Removing user %s from project %s" +msgstr "" + +#: nova/api/ec2/apirequest.py:95 +#, python-format +msgid "Unsupported API request: controller = %s,action = %s" +msgstr "" + +#: nova/api/ec2/cloud.py:117 +#, python-format +msgid "Generating root CA: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:277 +#, python-format +msgid "Create key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:285 +#, python-format +msgid "Delete key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:357 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "" + +#: nova/api/ec2/cloud.py:361 +msgid "Invalid port range" +msgstr "" + +#: nova/api/ec2/cloud.py:392 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 +msgid "No rule for the specified parameters." +msgstr "" + +#: nova/api/ec2/cloud.py:421 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:432 +#, python-format +msgid "This rule already exists in group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:460 +#, python-format +msgid "Create Security Group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:463 +#, python-format +msgid "group %s already exists" +msgstr "" + +#: nova/api/ec2/cloud.py:475 +#, python-format +msgid "Delete security group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 +#, python-format +msgid "Get console output for instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:543 +#, python-format +msgid "Create volume of %s GB" +msgstr "" + +#: nova/api/ec2/cloud.py:567 +#, python-format +msgid "Attach volume %s to instacne %s at %s" +msgstr "" + +#: nova/api/ec2/cloud.py:579 +#, python-format +msgid "Detach volume %s" +msgstr "" + +#: nova/api/ec2/cloud.py:686 +msgid "Allocate address" +msgstr "" + +#: nova/api/ec2/cloud.py:691 +#, python-format +msgid "Release address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:696 +#, python-format +msgid "Associate address %s to instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:703 +#, python-format +msgid "Disassociate address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:730 +msgid "Going to start terminating instances" +msgstr "" + +#: nova/api/ec2/cloud.py:738 +#, python-format +msgid "Reboot instance %r" +msgstr "" + +#: nova/api/ec2/cloud.py:775 +#, python-format +msgid "De-registering image %s" +msgstr "" + +#: nova/api/ec2/cloud.py:783 +#, python-format +msgid "Registered image %s with id %s" +msgstr "" + +#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 +#, python-format +msgid "attribute not supported: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:794 +#, python-format +msgid "invalid id: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:807 +msgid "user or group not specified" +msgstr "" + +#: nova/api/ec2/cloud.py:809 +msgid "only group \"all\" is supported" +msgstr "" + +#: nova/api/ec2/cloud.py:811 +msgid "operation_type must be add or remove" +msgstr "" + +#: nova/api/ec2/cloud.py:812 +#, python-format +msgid "Updating image %s publicity" +msgstr "" + +#: nova/api/ec2/metadatarequesthandler.py:75 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:70 +#, python-format +msgid "Caught error: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:86 +msgid "Including admin operations in API." +msgstr "" + +#: nova/api/openstack/servers.py:184 +#, python-format +msgid "Compute.api::lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:199 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "" + +#: nova/api/openstack/servers.py:213 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:224 +#, python-format +msgid "Compute.api::pause %s" +msgstr "" + +#: nova/api/openstack/servers.py:235 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "" + +#: nova/api/openstack/servers.py:246 +#, python-format +msgid "compute.api::suspend %s" +msgstr "" + +#: nova/api/openstack/servers.py:257 +#, python-format +msgid "compute.api::resume %s" +msgstr "" + +#: nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "" + +#: nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "" + +#: nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "" + +#: nova/auth/ldapdriver.py:181 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:218 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:478 +#, python-format +msgid "User %s is already a member of the group %s" +msgstr "" + +#: nova/auth/ldapdriver.py:507 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "" + +#: nova/auth/ldapdriver.py:528 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "" + +#: nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "" + +#: nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "" + +#: nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "" + +#: nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "" + +#: nova/auth/manager.py:275 +#, python-format +msgid "failed authorization: no project named %s (user=%s)" +msgstr "" + +#: nova/auth/manager.py:277 +#, python-format +msgid "No project called %s could be found" +msgstr "" + +#: nova/auth/manager.py:281 +#, python-format +msgid "Failed authorization: user %s not admin and not member of project %s" +msgstr "" + +#: nova/auth/manager.py:283 +#, python-format +msgid "User %s is not a member of project %s" +msgstr "" + +#: nova/auth/manager.py:292 nova/auth/manager.py:303 +#, python-format +msgid "Invalid signature for user %s" +msgstr "" + +#: nova/auth/manager.py:293 nova/auth/manager.py:304 +msgid "Signature does not match" +msgstr "" + +#: nova/auth/manager.py:374 +msgid "Must specify project" +msgstr "" + +#: nova/auth/manager.py:408 +#, python-format +msgid "The %s role can not be found" +msgstr "" + +#: nova/auth/manager.py:410 +#, python-format +msgid "The %s role is global only" +msgstr "" + +#: nova/auth/manager.py:412 +#, python-format +msgid "Adding role %s to user %s in project %s" +msgstr "" + +#: nova/auth/manager.py:438 +#, python-format +msgid "Removing role %s from user %s on project %s" +msgstr "" + +#: nova/auth/manager.py:505 +#, python-format +msgid "Created project %s with manager %s" +msgstr "" + +#: nova/auth/manager.py:523 +#, python-format +msgid "modifying project %s" +msgstr "" + +#: nova/auth/manager.py:553 +#, python-format +msgid "Remove user %s from project %s" +msgstr "" + +#: nova/auth/manager.py:581 +#, python-format +msgid "Deleting project %s" +msgstr "" + +#: nova/auth/manager.py:637 +#, python-format +msgid "Created user %s (admin: %r)" +msgstr "" + +#: nova/auth/manager.py:645 +#, python-format +msgid "Deleting user %s" +msgstr "" + +#: nova/auth/manager.py:655 +#, python-format +msgid "Access Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:657 +#, python-format +msgid "Secret Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:659 +#, python-format +msgid "Admin status set to %r for user %s" +msgstr "" + +#: nova/auth/manager.py:708 +#, python-format +msgid "No vpn data for project %s" +msgstr "" + +#: nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "" + +#: nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "" + +#: nova/compute/api.py:67 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "" + +#: nova/compute/api.py:73 +#, python-format +msgid "Instance %d has no host" +msgstr "" + +#: nova/compute/api.py:92 +#, python-format +msgid "Quota exceeeded for %s, tried to run %s instances" +msgstr "" + +#: nova/compute/api.py:94 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "" + +#: nova/compute/api.py:109 +msgid "Creating a raw instance" +msgstr "" + +#: nova/compute/api.py:156 +#, python-format +msgid "Going to run %s instances..." +msgstr "" + +#: nova/compute/api.py:180 +#, python-format +msgid "Casting to scheduler for %s/%s's instance %s" +msgstr "" + +#: nova/compute/api.py:279 +#, python-format +msgid "Going to try and terminate %s" +msgstr "" + +#: nova/compute/api.py:283 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "" + +#: nova/compute/api.py:288 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "" + +#: nova/compute/api.py:450 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "" + +#: nova/compute/api.py:465 +msgid "Volume isn't attached to anything!" +msgstr "" + +#: nova/compute/disk.py:71 +#, python-format +msgid "Input partition size not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:75 +#, python-format +msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "" + +#: nova/compute/disk.py:136 +#, python-format +msgid "Failed to load partition: %s" +msgstr "" + +#: nova/compute/disk.py:158 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "" + +#: nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "" + +#: nova/compute/manager.py:69 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "" + +#: nova/compute/manager.py:71 +#, python-format +msgid "check_instance_lock: arguments: |%s| |%s| |%s|" +msgstr "" + +#: nova/compute/manager.py:75 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "" + +#: nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "" + +#: nova/compute/manager.py:82 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "" + +#: nova/compute/manager.py:86 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "" + +#: nova/compute/manager.py:157 +msgid "Instance has already been created" +msgstr "" + +#: nova/compute/manager.py:158 +#, python-format +msgid "instance %s: starting..." +msgstr "" + +#: nova/compute/manager.py:197 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "" + +#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 +#, python-format +msgid "Terminating instance %s" +msgstr "" + +#: nova/compute/manager.py:217 +#, python-format +msgid "Disassociating address %s" +msgstr "" + +#: nova/compute/manager.py:230 +#, python-format +msgid "Deallocating address %s" +msgstr "" + +#: nova/compute/manager.py:243 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "" + +#: nova/compute/manager.py:257 +#, python-format +msgid "Rebooting instance %s" +msgstr "" + +#: nova/compute/manager.py:260 +#, python-format +msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:286 +#, python-format +msgid "instance %s: snapshotting" +msgstr "" + +#: nova/compute/manager.py:289 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:301 +#, python-format +msgid "instance %s: rescuing" +msgstr "" + +#: nova/compute/manager.py:316 +#, python-format +msgid "instance %s: unrescuing" +msgstr "" + +#: nova/compute/manager.py:335 +#, python-format +msgid "instance %s: pausing" +msgstr "" + +#: nova/compute/manager.py:352 +#, python-format +msgid "instance %s: unpausing" +msgstr "" + +#: nova/compute/manager.py:369 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "" + +#: nova/compute/manager.py:382 +#, python-format +msgid "instance %s: suspending" +msgstr "" + +#: nova/compute/manager.py:401 +#, python-format +msgid "instance %s: resuming" +msgstr "" + +#: nova/compute/manager.py:420 +#, python-format +msgid "instance %s: locking" +msgstr "" + +#: nova/compute/manager.py:432 +#, python-format +msgid "instance %s: unlocking" +msgstr "" + +#: nova/compute/manager.py:442 +#, python-format +msgid "instance %s: getting locked state" +msgstr "" + +#: nova/compute/manager.py:462 +#, python-format +msgid "instance %s: attaching volume %s to %s" +msgstr "" + +#: nova/compute/manager.py:478 +#, python-format +msgid "instance %s: attach failed %s, removing" +msgstr "" + +#: nova/compute/manager.py:493 +#, python-format +msgid "Detach volume %s from mountpoint %s on instance %s" +msgstr "" + +#: nova/compute/manager.py:497 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "" + +#: nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "" + +#: nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "" + +#: nova/compute/monitor.py:355 +#, python-format +msgid "Cannot get blockstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:377 +#, python-format +msgid "Cannot get ifstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:412 +msgid "unexpected exception getting connection" +msgstr "" + +#: nova/compute/monitor.py:427 +#, python-format +msgid "Found instance: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:43 +msgid "Use of empty request context is deprecated" +msgstr "" + +#: nova/db/sqlalchemy/api.py:132 +#, python-format +msgid "No service for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:229 +#, python-format +msgid "No service for %s, %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:574 +#, python-format +msgid "No floating ip for address %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:668 +#, python-format +msgid "No instance for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 +#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 +#, python-format +msgid "Instance %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:891 +#, python-format +msgid "no keypair for user %s, name %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 +#, python-format +msgid "No network for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1036 +#, python-format +msgid "No network for bridge %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1050 +#, python-format +msgid "No network for instance %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1180 +#, python-format +msgid "Token %s does not exist" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1205 +#, python-format +msgid "No quota for project_id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1356 +#, python-format +msgid "No volume for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1401 +#, python-format +msgid "Volume %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1413 +#, python-format +msgid "No export device found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1426 +#, python-format +msgid "No target id found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1471 +#, python-format +msgid "No security group with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1488 +#, python-format +msgid "No security group named %s for project: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1576 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1650 +#, python-format +msgid "No user for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1666 +#, python-format +msgid "No user for access key %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1728 +#, python-format +msgid "No project with id %s" +msgstr "" + +#: nova/image/glance.py:78 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images" +msgstr "" + +#: nova/image/glance.py:97 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images/detail" +msgstr "" + +#: nova/image/s3.py:82 +#, python-format +msgid "Image %s could not be found" +msgstr "" + +#: nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "" + +#: nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "" + +#: nova/network/linux_net.py:176 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "" + +#: nova/network/linux_net.py:186 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "" + +#: nova/network/linux_net.py:254 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "" + +#: nova/network/linux_net.py:256 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "" + +#: nova/network/linux_net.py:334 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "" + +#: nova/network/manager.py:135 +msgid "setting network host" +msgstr "" + +#: nova/network/manager.py:190 +#, python-format +msgid "Leasing IP %s" +msgstr "" + +#: nova/network/manager.py:194 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "" + +#: nova/network/manager.py:197 +#, python-format +msgid "IP %s leased to bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:205 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "" + +#: nova/network/manager.py:214 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "" + +#: nova/network/manager.py:217 +#, python-format +msgid "IP %s released from bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:220 +#, python-format +msgid "IP %s released that was not leased" +msgstr "" + +#: nova/network/manager.py:442 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "" + +#: nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "" + +#: nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "" + +#: nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "" + +#: nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:271 +#, python-format +msgid "Getting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:274 +#, python-format +msgid "Unauthorized attempt to get object %s from bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:292 +#, python-format +msgid "Putting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:295 +#, python-format +msgid "Unauthorized attempt to upload object %s to bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:314 +#, python-format +msgid "Deleting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:393 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "" + +#: nova/objectstore/handler.py:401 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:406 +#, python-format +msgid "Starting image upload: %s" +msgstr "" + +#: nova/objectstore/handler.py:420 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "" + +#: nova/objectstore/handler.py:428 +#, python-format +msgid "Toggling publicity flag of image %s %r" +msgstr "" + +#: nova/objectstore/handler.py:433 +#, python-format +msgid "Updating user fields on image %s" +msgstr "" + +#: nova/objectstore/handler.py:447 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "" + +#: nova/objectstore/handler.py:452 +#, python-format +msgid "Deleted image: %s" +msgstr "" + +#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 +#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 +msgid "No hosts found" +msgstr "" + +#: nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "" + +#: nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %s %s for %s" +msgstr "" + +#: nova/scheduler/simple.py:63 +msgid "All hosts have too many cores" +msgstr "" + +#: nova/scheduler/simple.py:95 +msgid "All hosts have too many gigabytes" +msgstr "" + +#: nova/scheduler/simple.py:115 +msgid "All hosts have too many networks" +msgstr "" + +#: nova/tests/test_cloud.py:198 +msgid "Can't test instances without a real virtual env." +msgstr "" + +#: nova/tests/test_cloud.py:210 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "" + +#: nova/tests/test_compute.py:104 +#, python-format +msgid "Running instances: %s" +msgstr "" + +#: nova/tests/test_compute.py:110 +#, python-format +msgid "After terminating instances: %s" +msgstr "" + +#: nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %s, %s" +msgstr "" + +#: nova/tests/test_rpc.py:94 +#, python-format +msgid "Nested return %s" +msgstr "" + +#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 +#, python-format +msgid "Received %s" +msgstr "" + +#: nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "" + +#: nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "" + +#: nova/virt/fake.py:210 +#, python-format +msgid "Instance %s Not Found" +msgstr "" + +#: nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "" + +#: nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "" + +#: nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "" + +#: nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "" + +#: nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "" + +#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 +#, python-format +msgid "Created VM %s..." +msgstr "" + +#: nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %s by attaching disk file %s" +msgstr "" + +#: nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "" + +#: nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "" + +#: nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "" + +#: nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "" + +#: nova/virt/hyperv.py:275 +#, python-format +msgid "Created switch port %s on switch %s" +msgstr "" + +#: nova/virt/hyperv.py:285 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:287 +#, python-format +msgid "Created nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:320 +#, python-format +msgid "WMI job failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:322 +#, python-format +msgid "WMI job succeeded: %s, Elapsed=%s " +msgstr "" + +#: nova/virt/hyperv.py:358 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:383 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:389 +#, python-format +msgid "Del: disk %s vm %s" +msgstr "" + +#: nova/virt/hyperv.py:405 +#, python-format +msgid "" +"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " +"cpu_time=%s" +msgstr "" + +#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 +#, python-format +msgid "duplicate name found: %s" +msgstr "" + +#: nova/virt/hyperv.py:444 +#, python-format +msgid "Successfully changed vm state of %s to %s" +msgstr "" + +#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 +#, python-format +msgid "Failed to change vm state of %s to %s" +msgstr "" + +#: nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %s -- placed in %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:144 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:157 +msgid "Connection to libvirt broke" +msgstr "" + +#: nova/virt/libvirt_conn.py:229 +#, python-format +msgid "instance %s: deleting instance files %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:271 +#, python-format +msgid "No disk at %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:278 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "" + +#: nova/virt/libvirt_conn.py:294 +#, python-format +msgid "instance %s: rebooted" +msgstr "" + +#: nova/virt/libvirt_conn.py:297 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:340 +#, python-format +msgid "instance %s: rescued" +msgstr "" + +#: nova/virt/libvirt_conn.py:343 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:370 +#, python-format +msgid "instance %s: is running" +msgstr "" + +#: nova/virt/libvirt_conn.py:381 +#, python-format +msgid "instance %s: booted" +msgstr "" + +#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 +#, python-format +msgid "instance %s: failed to boot" +msgstr "" + +#: nova/virt/libvirt_conn.py:395 +#, python-format +msgid "virsh said: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:399 +msgid "cool, it's a device" +msgstr "" + +#: nova/virt/libvirt_conn.py:407 +#, python-format +msgid "data: %r, fpath: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:415 +#, python-format +msgid "Contents of file %s: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:449 +#, python-format +msgid "instance %s: Creating image" +msgstr "" + +#: nova/virt/libvirt_conn.py:505 +#, python-format +msgid "instance %s: injecting key into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:508 +#, python-format +msgid "instance %s: injecting net into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:516 +#, python-format +msgid "instance %s: ignoring error injecting data into image %s (%s)" +msgstr "" + +#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "" + +#: nova/virt/libvirt_conn.py:589 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "" + +#: nova/virt/xenapi_conn.py:113 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" + +#: nova/virt/xenapi_conn.py:263 +#, python-format +msgid "Task [%s] %s status: success %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:271 +#, python-format +msgid "Task [%s] %s status: %s %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 +#, python-format +msgid "Got exception: %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:72 +#, python-format +msgid "%s: _db_content => %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 +#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 +msgid "Raising NotImplemented" +msgstr "" + +#: nova/virt/xenapi/fake.py:249 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:283 +#, python-format +msgid "Calling %s %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:288 +#, python-format +msgid "Calling getter %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:340 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:127 +#, python-format +msgid "Created VM %s as %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:147 +#, python-format +msgid "Creating VBD for VM %s, VDI %s ... " +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:149 +#, python-format +msgid "Created VBD %s for VM %s, VDI %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:165 +#, python-format +msgid "VBD not found in instance %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:175 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:202 +#, python-format +msgid "Creating VIF for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:205 +#, python-format +msgid "Created VIF %s for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:216 +#, python-format +msgid "Snapshotting VM %s with label '%s'..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:229 +#, python-format +msgid "Created snapshot %s from VM %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:243 +#, python-format +msgid "Asking xapi to upload %s as '%s'" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:261 +#, python-format +msgid "Asking xapi to fetch %s as %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:279 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:290 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:318 +#, python-format +msgid "VDI %s is still available" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:331 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:333 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:390 +#, python-format +msgid "VHD %s has parent %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:407 +#, python-format +msgid "Re-scanning SR %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:431 +#, python-format +msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:448 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:452 +#, python-format +msgid "Unexpected number of VDIs (%s) found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:62 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:99 +#, python-format +msgid "Starting VM %s..." +msgstr "" + +#: nova/virt/xenapi/vmops.py:101 +#, python-format +msgid "Spawning VM %s created %s." +msgstr "" + +#: nova/virt/xenapi/vmops.py:112 +#, python-format +msgid "Instance %s: booted" +msgstr "" + +#: nova/virt/xenapi/vmops.py:137 +#, python-format +msgid "Instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:166 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:174 +#, python-format +msgid "Unable to Snapshot %s: %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:184 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:252 +#, python-format +msgid "suspend: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "resume: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:271 +#, python-format +msgid "Instance not found %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %s as %s." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %s when getting PBDs for %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %s when unplugging PBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %s when forgetting SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %s, %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %s, %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:81 +#, python-format +msgid "Unable to use SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:95 +#, python-format +msgid "Mountpoint %s attached to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:106 +#, python-format +msgid "Detach_volume: %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:113 +#, python-format +msgid "Unable to locate volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:121 +#, python-format +msgid "Unable to detach volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:128 +#, python-format +msgid "Mountpoint %s detached from instance %s" +msgstr "" + +#: nova/volume/api.py:44 +#, python-format +msgid "Quota exceeeded for %s, tried to create %sG volume" +msgstr "" + +#: nova/volume/api.py:46 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %s" +msgstr "" + +#: nova/volume/api.py:70 nova/volume/api.py:95 +msgid "Volume status must be available" +msgstr "" + +#: nova/volume/api.py:97 +msgid "Volume is already attached" +msgstr "" + +#: nova/volume/api.py:103 +msgid "Volume is already detached" +msgstr "" + +#: nova/volume/driver.py:76 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "" + +#: nova/volume/driver.py:85 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "" + +#: nova/volume/driver.py:210 +#, python-format +msgid "FAKE AOE: %s" +msgstr "" + +#: nova/volume/driver.py:315 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "" + +#: nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "" + +#: nova/volume/manager.py:93 +#, python-format +msgid "volume %s: creating" +msgstr "" + +#: nova/volume/manager.py:102 +#, python-format +msgid "volume %s: creating lv of size %sG" +msgstr "" + +#: nova/volume/manager.py:106 +#, python-format +msgid "volume %s: creating export" +msgstr "" + +#: nova/volume/manager.py:113 +#, python-format +msgid "volume %s: created successfully" +msgstr "" + +#: nova/volume/manager.py:121 +msgid "Volume is still attached" +msgstr "" + +#: nova/volume/manager.py:123 +msgid "Volume is not local to this node" +msgstr "" + +#: nova/volume/manager.py:124 +#, python-format +msgid "volume %s: removing export" +msgstr "" + +#: nova/volume/manager.py:126 +#, python-format +msgid "volume %s: deleting" +msgstr "" + +#: nova/volume/manager.py:129 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "" -- cgit From e12069f79cbf35215eeba5257b2394e9ebde5855 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Mon, 7 Feb 2011 14:29:11 +0100 Subject: replaced all calls to ifconfig with calls to ip --- nova/network/linux_net.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index cdd1f666a..6b8108b14 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -177,7 +177,7 @@ def ensure_vlan(vlan_num): LOG.debug(_("Starting VLAN inteface %s"), interface) _execute("sudo vconfig set_name_type VLAN_PLUS_VID_NO_PAD") _execute("sudo vconfig add %s %s" % (FLAGS.vlan_interface, vlan_num)) - _execute("sudo ifconfig %s up" % interface) + _execute("sudo ip link set %s up" % interface) return interface @@ -192,17 +192,17 @@ def ensure_bridge(bridge, interface, net_attrs=None): if interface: _execute("sudo brctl addif %s %s" % (bridge, interface)) if net_attrs: - _execute("sudo ifconfig %s %s broadcast %s netmask %s up" % \ - (bridge, - net_attrs['gateway'], - net_attrs['broadcast'], - net_attrs['netmask'])) + _execute("sudo ip addr add %s/%s dev %s broadcast %s" % \ + (net_attrs['gateway'], + net_attrs['netmask'], + bridge, + net_attrs['broadcast'])) if(FLAGS.use_ipv6): _execute("sudo ip -f inet6 addr change %s dev %s" % (net_attrs['cidr_v6'], bridge)) - _execute("sudo ifconfig %s up" % bridge) + _execute("sudo ip link set %s up" % bridge) else: - _execute("sudo ifconfig %s up" % bridge) + _execute("sudo ip link set %s up" % bridge) if FLAGS.use_nova_chains: (out, err) = _execute("sudo iptables -N nova_forward", check_exit_code=False) @@ -329,7 +329,8 @@ def _execute(cmd, *args, **kwargs): def _device_exists(device): """Check if ethernet device exists""" - (_out, err) = _execute("ifconfig %s" % device, check_exit_code=False) + (_out, err) = _execute("ip link show dev %s" % device, + check_exit_code=False) return not err -- cgit From 2458d674807d951a6b58c28cd334cd8d097822a9 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 7 Feb 2011 10:20:49 -0600 Subject: A few changes --- nova/compute/manager.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 140db0d3e..485efc047 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -380,6 +380,20 @@ class ComputeManager(manager.Manager): """Update instance state when async task completes.""" self._update_state(context, instance_id) + @exception.wrap_exception + @checks_instance_lock + def confirm_resize(self, context, instance_id): + """Destroys the old instance on the source machine""" + pass + + @exception.wrap_exception + @echecks_instance_lock + def revert_resize(self, context, instance_id): + """Destroys the new instance on the destination machine, + reverts the model changes, and powers on the old + instance on the source machine""" + pass + @exception.wrap_exception @checks_instance_lock @@ -395,8 +409,9 @@ class ComputeManager(manager.Manager): 'status': 'pre-migrating' } LOG.audit(_('instance %s: migrating to '), instance_id, context=context) service = self.db.service_get_by_host_and_topic(context, - instance_ref['host'], topic) - topic = self.db.queue_get_for(context, topic, service['id']) + instance_ref['host'], FLAGS.compute_topic) + topic = self.db.queue_get_for(context, FLAGS.compute_topic, + service['id']) rpc.cast(context, topic, { 'method': 'resize_instance', 'migration_id': migration_ref['id'], } @@ -417,8 +432,9 @@ class ComputeManager(manager.Manager): # This is where we would update the VM record after resizing service = self.db.service_get_by_host_and_topic(context, - migration_ref['dest_host'], topic) - topic = self.db.queue_get_for(context, topic, service['id']) + migration_ref['dest_host'], FLAGS.compute_topic) + topic = self.db.queue_get_for(context, FLAGS.compute_topic, + service['id']) rpc.cast(context, topic, { 'method': 'finish_resize', 'migration_id': migration_ref['id'], } -- cgit From 93d6050078214945fd2c842a15fb177f24811fa1 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Mon, 7 Feb 2011 17:06:30 +0000 Subject: Fix for bug #714709 --- nova/virt/xenapi/vm_utils.py | 5 +++++ nova/virt/xenapi/vmops.py | 19 +++++++++++++++++-- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 14 ++++++++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4bbd522c1..4f6de7588 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -438,7 +438,12 @@ class VMHelper(HelperBase): return vdis else: return None + @classmethod + def lookup_kernel_ramdisk(cls,session,vm): + vm_rec = session.get_xenapi().VM.get_record(vm) + return (vm_rec['PV_kernel'],vm_rec['PV_ramdisk']) + @classmethod def compile_info(cls, record): """Fill record with VM status information""" diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index e84ce20c4..fe95d881b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -286,8 +286,23 @@ class VMOps(object): def _destroy_vm(self, instance, vm): """Destroys a VM record """ try: - task = self._session.call_xenapi('Async.VM.destroy', vm) - self._session.wait_for_task(instance.id, task) + kernel = None + ramdisk = None + if instance.kernel_id or instance.ramdisk_id: + (kernel, ramdisk) = VMHelper.lookup_kernel_ramdisk( + self._session, vm) + task1 = self._session.call_xenapi('Async.VM.destroy', vm) + LOG.debug(_("Removing kernel/ramdisk files")) + fn = "remove_kernel_ramdisk" + args = {} + if kernel: + args['kernel-file'] = kernel + if ramdisk: + args['ramdisk-file'] = ramdisk + task2 = self._session.async_call_plugin('glance', fn, args) + self._session.wait_for_task(instance.id, task1) + self._session.wait_for_task(instance.id, task2) + LOG.debug(_("kernel/ramdisk files removed")) except self.XenAPI.Failure, exc: LOG.exception(exc) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index aadacce57..4fb96eeab 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -43,6 +43,16 @@ CHUNK_SIZE = 8192 KERNEL_DIR = '/boot/guest' FILE_SR_PATH = '/var/run/sr-mount' +def remove_kernel_ramdisk(session,args): + """Removes kernel and/or ramdisk from dom0's file system""" + kernel_file=exists(args,'kernel-file') + ramdisk_file=exists(args,'ramdisk-file') + if kernel_file: + os.remove(kernel_file) + if ramdisk_file: + os.remove(ramdisk_file) + return "ok" + def copy_kernel_vdi(session,args): vdi = exists(args, 'vdi-ref') size = exists(args,'image-size') @@ -117,7 +127,6 @@ def put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port): while chunk: conn.send(chunk) chunk = bundle.read(CHUNK_SIZE) - res = conn.getresponse() #FIXME(sirp): should this be 201 Created? @@ -157,4 +166,5 @@ def find_sr(session): if __name__ == '__main__': XenAPIPlugin.dispatch({'put_vdis': put_vdis, - 'copy_kernel_vdi': copy_kernel_vdi}) + 'copy_kernel_vdi': copy_kernel_vdi, + 'remove_kernel_ramdisk': remove_kernel_ramdisk}) -- cgit From 41e615b843c284631a0d878db2c93ef97f2eb4b8 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 7 Feb 2011 14:46:54 -0400 Subject: minor --- nova/api/openstack/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 025fa12a4..8901a8987 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -83,7 +83,7 @@ class APIRouter(wsgi.Router): mapper.resource("zone", "zones", controller=zones.Controller(), collection={'detail': 'GET'}, - member=zone_members) + collection_name='zones') mapper.resource("server", "servers", controller=servers.Controller(), collection={'detail': 'GET'}, -- cgit From b6022c1f7d7dc9294f6b1b613c7e99bd9437a72e Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Mon, 7 Feb 2011 13:43:23 -0600 Subject: added network_get_all_by_instance(), call to reset_network in vmops --- nova/db/sqlalchemy/api.py | 19 +++++++++++++------ nova/virt/xenapi/vmops.py | 11 +++++++---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 31865d553..26b685e43 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1055,12 +1055,6 @@ def network_get(context, network_id, session=None): return result -@require_context -def network_get_all(context): - session = get_session() - return session.query(models.Network).all() - - # NOTE(vish): pylint complains because of the long method name, but # it fits with the names of the rest of the methods # pylint: disable-msg=C0103 @@ -1104,6 +1098,19 @@ def network_get_by_instance(_context, instance_id): return rv +@require_admin_context +def network_get_all_by_instance(_context, instance_id): + session = get_session() + rv = session.query(models.Network).\ + filter_by(deleted=False).\ + join(models.Network.fixed_ips).\ + filter_by(instance_id=instance_id).\ + filter_by(deleted=False) + if not rv: + raise exception.NotFound(_('No network for instance %s') % instance_id) + return rv + + @require_admin_context def network_set_host(context, network_id, host_id): session = get_session() diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6edeae5c0..4056e99bc 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -96,9 +96,11 @@ class VMOps(object): # write network info admin_context = context.get_admin_context() - network = db.network_get_by_instance(admin_context, - instance['id']) - for network in db.network_get_all(admin_context): + #network = db.network_get_by_instance(admin_context, + # instance['id']) + + for network in db.network_get_all_by_instance(admin_context, + instance['id']): mac_id = instance.mac_address.replace(':', '') location = 'vm-data/networking/%s' % mac_id mapping = {'label': network['label'], @@ -119,6 +121,7 @@ class VMOps(object): network_ref, instance.mac_address) # call reset networking + self.reset_network(vm_ref) LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) @@ -389,7 +392,7 @@ class VMOps(object): # TODO: implement this! return 'http://fakeajaxconsole/fake_url' - def reset_networking(self, instance): + def reset_network(self, instance): vm = self._get_vm_opaque_ref(instance) args = {'id': str(uuid.uuid4())} resp = self._make_agent_call('resetnetwork', vm, '', args) -- cgit From 5a5e96ae90187e1ebfe93262df47ec2c4be23ef1 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 7 Feb 2011 13:00:39 -0800 Subject: require user context for most flavor/instance_type read calls --- nova/db/sqlalchemy/api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f4e021ac8..6065af9ca 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2031,6 +2031,7 @@ def instance_type_create(context, values): return instance_type_ref +@require_context def instance_type_get_all(context): """ Returns a dict describing all instance_types with name as key: @@ -2049,6 +2050,7 @@ def instance_type_get_all(context): return inst_dict +@require_context def instance_type_get_by_name(context, name): """Returns a dict describing specific instance_type""" session = get_session() @@ -2058,6 +2060,7 @@ def instance_type_get_by_name(context, name): return dict(inst_type) +@require_context def instance_type_get_by_flavor_id(context, id): """Returns a dict describing specific flavor_id""" session = get_session() -- cgit From 346087804dd923bcaa0faf433dc1f83a2f193815 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 7 Feb 2011 13:32:25 -0800 Subject: fixed instance_types methods to use database backend --- nova/compute/instance_types.py | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 449ec1d2e..d38d76ded 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -21,6 +21,8 @@ The built-in instance properties. """ +from nova import context +from nova import db from nova import flags from nova import exception @@ -34,20 +36,39 @@ INSTANCE_TYPES = { 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} +def get_all_types(): + """retrieves all instance_types""" + return db.instance_type_get_all() + + +def get_all_flavors(): + """retrieves all flavors. alias for instance_types.get_all_types()""" + return get_all_types() + + def get_by_type(instance_type): - """Build instance data structure and save it to the data store.""" + """retrieve instance_type details""" # FIXME(kpepple) for dynamic flavors if instance_type is None: return FLAGS.default_instance_type - if instance_type not in INSTANCE_TYPES: + try: + ctxt = context.get_admin_context() + inst_type = db.instance_type_get_by_name(ctxt, instance_type) + except Exception, e: + print e raise exception.ApiError(_("Unknown instance type: %s"), instance_type) - return instance_type + return inst_type['name'] def get_by_flavor_id(flavor_id): - # FIXME(kpepple) for dynamic flavors - for instance_type, details in INSTANCE_TYPES.iteritems(): - if details['flavorid'] == flavor_id: - return instance_type - return FLAGS.default_instance_type + """retrieve instance_type's name by flavor_id""" + if flavor_id is None: + return FLAGS.default_instance_type + try: + ctxt = context.get_admin_context() + flavor = db.instance_type_get_by_flavor_id(ctxt, flavor_id) + except Exception, e: + raise exception.ApiError(_("Unknown flavor: %s"), + flavor_id) + return flavor['name'] -- cgit From 2613d6449fa9f07c5ee1627a3c44895071fb1a59 Mon Sep 17 00:00:00 2001 From: Anne Gentle Date: Mon, 7 Feb 2011 16:32:04 -0600 Subject: Made updates to multinode install doc --- doc/source/adminguide/multi.node.install.rst | 38 ++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/doc/source/adminguide/multi.node.install.rst b/doc/source/adminguide/multi.node.install.rst index f2f25b060..cf518b26a 100644 --- a/doc/source/adminguide/multi.node.install.rst +++ b/doc/source/adminguide/multi.node.install.rst @@ -37,20 +37,22 @@ From a server you intend to use as a cloud controller node, use this command to :: - wget --no-check-certificate https://github.com/dubsquared/OpenStack-NOVA-Installer-Script/raw/master/Nova_CC_Installer_v0.1 + wget --no-check-certificate https://github.com/dubsquared/OpenStack-NOVA-Installer-Script/raw/master/ nova-CC-install-v1.1.sh Ensure you can execute the script by modifying the permissions on the script file. :: - sudo chmod 755 Nova_CC_Installer_v0.1 + sudo chmod 755 nova-CC-install-v1.1.sh :: - sudo ./Nova_CC_Installer_v0.1 + sudo ./nova-CC-install-v1.1.sh -Next, from a server you intend to use as a compute node (doesn't contain the database), install the nova services. Copy the nova.conf from the cloud controller node to the compute node. +Next, from a server you intend to use as a compute node (doesn't contain the database), install the nova services. You can use the nova-NODE-installer.sh script from the above github-hosted project for the compute node installation. + +Copy the nova.conf from the cloud controller node to the compute node. Restart related services:: @@ -247,7 +249,7 @@ Here is an example of what this looks like with real data:: Note: The nova-manage service assumes that the first IP address is your network (like 192.168.0.0), that the 2nd IP is your gateway (192.168.0.1), and that the broadcast is the very last IP in the range you defined (192.168.0.255). If this is not the case you will need to manually edit the sql db 'networks' table.o. -On running this command, entries are made in the 'networks' and 'fixed_ips' table. However, one of the networks listed in the 'networks' table needs to be marked as bridge in order for the code to know that a bridge exists. The Network is marked as bridged automatically based on the type of network manager selected. This is ONLY necessary if you chose FlatManager as your network type. More information can be found at the end of this document discussing setting up the bridge device. +On running the "nova-manage network create" command, entries are made in the 'networks' and 'fixed_ips' table. However, one of the networks listed in the 'networks' table needs to be marked as bridge in order for the code to know that a bridge exists. The Network is marked as bridged automatically based on the type of network manager selected. You only need to mark the network as a bridge if you chose FlatManager as your network type. More information can be found at the end of this document discussing setting up the bridge device. Step 2 - Create Nova certifications @@ -288,9 +290,35 @@ Another common issue is you cannot ping or SSH your instances after issusing the killall dnsmasq service nova-network restart +To avoid issues with KVM and permissions with Nova, run the following commands to ensure we have VM's that are running optimally:: + + chgrp kvm /dev/kvm + chmod g+rwx /dev/kvm + +If you want to use the 10.04 Ubuntu Enterprise Cloud images that are readily available at http://uec-images.ubuntu.com/releases/10.04/release/, you may run into delays with booting. Any server that does not have nova-api running on it needs this iptables entry so that UEC images can get metadata info. On compute nodes, configure the iptables with this next step:: + + # iptables -t nat -A PREROUTING -d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination $NOVA_API_IP:8773 + Testing the Installation ```````````````````````` +You can confirm that your compute node is talking to your cloud controller. From the cloud controller, run this database query:: + + mysql -u$MYSQL_USER -p$MYSQL_PASS nova -e 'select * from services;' + +In return, you should see something similar to this:: + +---------------------+---------------------+------------+---------+----+----------+----------------+-----------+--------------+----------+-------------------+ + | created_at | updated_at | deleted_at | deleted | id | host | binary | topic | report_count | disabled | availability_zone | + +---------------------+---------------------+------------+---------+----+----------+----------------+-----------+--------------+----------+-------------------+ + | 2011-01-28 22:52:46 | 2011-02-03 06:55:48 | NULL | 0 | 1 | osdemo02 | nova-network | network | 46064 | 0 | nova | + | 2011-01-28 22:52:48 | 2011-02-03 06:55:57 | NULL | 0 | 2 | osdemo02 | nova-compute | compute | 46056 | 0 | nova | + | 2011-01-28 22:52:52 | 2011-02-03 06:55:50 | NULL | 0 | 3 | osdemo02 | nova-scheduler | scheduler | 46065 | 0 | nova | + | 2011-01-29 23:49:29 | 2011-02-03 06:54:26 | NULL | 0 | 4 | osdemo01 | nova-compute | compute | 37050 | 0 | nova | + | 2011-01-30 23:42:24 | 2011-02-03 06:55:44 | NULL | 0 | 9 | osdemo04 | nova-compute | compute | 28484 | 0 | nova | + | 2011-01-30 21:27:28 | 2011-02-03 06:54:23 | NULL | 0 | 8 | osdemo05 | nova-compute | compute | 29284 | 0 | nova | + +---------------------+---------------------+------------+---------+----+----------+----------------+-----------+--------------+----------+-------------------+ +You can see that 'osdemo0{1,2,4,5} are all running 'nova-compute.' When you start spinning up instances, they will allocate on any node that is running nova-compute from this list. + You can then use `euca2ools` to test some items:: euca-describe-images -- cgit From a6b913dfd1d940cbf435cb9d9c5c6e2716cf5c2a Mon Sep 17 00:00:00 2001 From: Anne Gentle Date: Mon, 7 Feb 2011 16:35:03 -0600 Subject: Another quick fix to multinode install doc --- doc/source/adminguide/multi.node.install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/adminguide/multi.node.install.rst b/doc/source/adminguide/multi.node.install.rst index cf518b26a..c53455e3e 100644 --- a/doc/source/adminguide/multi.node.install.rst +++ b/doc/source/adminguide/multi.node.install.rst @@ -37,7 +37,7 @@ From a server you intend to use as a cloud controller node, use this command to :: - wget --no-check-certificate https://github.com/dubsquared/OpenStack-NOVA-Installer-Script/raw/master/ nova-CC-install-v1.1.sh + wget --no-check-certificate https://github.com/dubsquared/OpenStack-NOVA-Installer-Script/raw/master/nova-CC-install-v1.1.sh Ensure you can execute the script by modifying the permissions on the script file. -- cgit From e59c62efe5492e59fcc26b7b74f6ac2daa0caabe Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 7 Feb 2011 16:57:02 -0600 Subject: Added data_transfer xapi plugin --- nova/virt/xenapi_conn.py | 2 +- .../xenapi/etc/xapi.d/plugins/data_transfer | 44 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 628291764..acfde6caf 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -193,7 +193,7 @@ class XenAPIConnection(object): self._vmops.power_on(instance) def transfer_disk(self, instance, dest, callback): - self._vmops.transfer_disk( + self._vmops.transfer_disk() def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer new file mode 100644 index 000000000..d310a65d9 --- /dev/null +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +# 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. + +""" +XenAPI Plugin for transfering data between host nodes +""" + +import os.path +import subprocess + +import XenAPIPlugin + +SSH_HOSTS = '/root/.ssh/known_hosts' +DEVNULL = '/dev/null' +KEYSCAN = '/usr/bin/ssh-keyscan' + +def _key_scan_and_add(host): + """SSH scans a remote host and writes the SSH key out to known_hosts""" + open(SSH_HOSTS, 'a').close() + null = open(DEVNULL, 'w') + known_hosts = open(SSH_HOSTS, 'a') + key = subprocess.Popen(['/usr/bin/ssh-keyscan', '-t', 'rsa', host], + stdout=subprocess.PIPE, stderr=null).communicate()[0].strip() + grep = subprocess.call(['/bin/grep', '-o', '%s' % key, SSH_HOSTS], + stdout=null, stderr=null) + if grep == 1: + known_hosts.write(key) + null.close() + known_hosts.close() -- cgit From 15321719332a5b782ba5ac66d85db0eccc98ccba Mon Sep 17 00:00:00 2001 From: Ryan Lane Date: Mon, 7 Feb 2011 23:01:57 +0000 Subject: Checking whether the instance id is a list or not before assignment. This is to fix a bug relating to nova/boto. The AWK-SDK libraries pass in a string, not a list. the euca tools pass in a list. --- nova/api/ec2/cloud.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 00d044e95..c80e1168a 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -512,8 +512,11 @@ class CloudController(object): def get_console_output(self, context, instance_id, **kwargs): LOG.audit(_("Get console output for instance %s"), instance_id, context=context) - # instance_id is passed in as a list of instances - ec2_id = instance_id[0] + # instance_id may be passed in as a list of instances + if type(instance_id) == list: + ec2_id = instance_id[0] + else: + ec2_id = instance_id instance_id = ec2_id_to_id(ec2_id) output = self.compute_api.get_console_output( context, instance_id=instance_id) -- cgit From bb2a379e2827ceccc5b46b0a9936e6dcedc6499e Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 7 Feb 2011 15:04:26 -0800 Subject: added INSTANCE_TYPES to test for compatibility with current tests --- nova/compute/instance_types.py | 12 ++++++------ nova/test.py | 8 ++++++++ nova/tests/db/fakes.py | 6 ++---- nova/tests/test_quota.py | 9 +++------ nova/tests/test_xenapi.py | 3 +-- nova/virt/libvirt_conn.py | 7 +++---- nova/virt/xenapi/vm_utils.py | 4 ++-- 7 files changed, 25 insertions(+), 24 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index d38d76ded..0e20982e3 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -28,12 +28,12 @@ from nova import exception FLAGS = flags.FLAGS # FIXME(kpepple) for dynamic flavors -INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} +# INSTANCE_TYPES = { +# 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), +# 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), +# 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), +# 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), +# 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} def get_all_types(): diff --git a/nova/test.py b/nova/test.py index 881baccd5..cd049f007 100644 --- a/nova/test.py +++ b/nova/test.py @@ -44,6 +44,14 @@ flags.DEFINE_bool('fake_tests', True, 'should we use everything for testing') +INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + + def skip_if_fake(func): """Decorator that skips a test if running in fake mode""" def _skipper(*args, **kw): diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 3aa7fcd22..d9a5032ee 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -20,6 +20,7 @@ import time from nova import db +from nova import test from nova import utils from nova.compute import instance_types @@ -44,10 +45,7 @@ def stub_out_db_instance_api(stubs): def fake_instance_create(values): """ Stubs out the db.instance_create method """ - # FIXME(kpepple) for dynamic flavors - type_data = instance_types.INSTANCE_TYPES[values['instance_type']] - # type_data = db.instance_type_get_by_name(context,\ - # instance_type) + type_data = test.INSTANCE_TYPES[values['instance_type']] base_options = { 'name': values['name'], diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index c70803e05..4206ca416 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -75,20 +75,17 @@ class QuotaTestCase(test.TestCase): def test_quota_overrides(self): """Make sure overriding a projects quotas works""" - # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, - instance_types.INSTANCE_TYPES['m1.small']) + test.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 2) db.quota_create(self.context, {'project_id': self.project.id, 'instances': 10}) - # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, - instance_types.INSTANCE_TYPES['m1.small']) + test.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 4) db.quota_update(self.context, self.project.id, {'cores': 100}) - # FIXME(kpepple) for dynamic flavors num_instances = quota.allowed_instances(self.context, 100, - instance_types.INSTANCE_TYPES['m1.small']) + test.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 10) db.quota_destroy(self.context, self.project.id) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 1d42f8da3..d8611ea10 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -225,8 +225,7 @@ class XenAPIVMTestCase(test.TestCase): vm = vms[0] # Check that m1.large above turned into the right thing. - # FIXME(kpepple) for dynamic flavors - instance_type = instance_types.INSTANCE_TYPES['m1.large'] + instance_type = test.INSTANCE_TYPES['m1.large'] mem_kib = long(instance_type['memory_mb']) << 10 mem_bytes = str(mem_kib << 10) vcpus = instance_type['vcpus'] diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index e66115464..94fe93c40 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -55,6 +55,7 @@ from nova import db from nova import exception from nova import flags from nova import log as logging +from nova import test from nova import utils #from nova.api import context from nova.auth import manager @@ -611,8 +612,7 @@ class LibvirtConnection(object): user=user, project=project, size=size) - # FIXME(kpepple) for dynamic flavors - type_data = instance_types.INSTANCE_TYPES[inst['instance_type']] + type_data = test.INSTANCE_TYPES[inst['instance_type']] if type_data['local_gb']: self._cache_image(fn=self._create_local, @@ -672,9 +672,8 @@ class LibvirtConnection(object): network = db.network_get_by_instance(context.get_admin_context(), instance['id']) # FIXME(vish): stick this in db - # FIXME(kpepple) for dynamic flavors instance_type = instance['instance_type'] - instance_type = instance_types.INSTANCE_TYPES[instance_type] + instance_type = test.INSTANCE_TYPES[instance_type] ip_address = db.instance_get_fixed_address(context.get_admin_context(), instance['id']) # Assume that the gateway also acts as the dhcp server. diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 3f0112609..38a6f73ce 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -31,6 +31,7 @@ import glance.client from nova import exception from nova import flags from nova import log as logging +from nova import test from nova import utils from nova.auth.manager import AuthManager from nova.compute import instance_types @@ -82,8 +83,7 @@ class VMHelper(HelperBase): the pv_kernel flag indicates whether the guest is HVM or PV """ - # FIXME(kpepple) for dynamic flavors - instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] + instance_type = test.INSTANCE_TYPES[instance.instance_type] mem = str(long(instance_type['memory_mb']) * 1024 * 1024) vcpus = str(instance_type['vcpus']) rec = { -- cgit From a40f6041556ec09a1cb79c2b8abcec7fa70e72bf Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 7 Feb 2011 17:12:15 -0600 Subject: Some stuff --- nova/virt/xenapi_conn.py | 2 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index acfde6caf..726106b37 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -193,7 +193,7 @@ class XenAPIConnection(object): self._vmops.power_on(instance) def transfer_disk(self, instance, dest, callback): - self._vmops.transfer_disk() + self._vmops.transfer_disk(dest) def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer index d310a65d9..cde7bb823 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer @@ -28,10 +28,13 @@ import XenAPIPlugin SSH_HOSTS = '/root/.ssh/known_hosts' DEVNULL = '/dev/null' KEYSCAN = '/usr/bin/ssh-keyscan' +RSYNC = '/usr/bin/rsync' def _key_scan_and_add(host): """SSH scans a remote host and writes the SSH key out to known_hosts""" + # Touch the file if it doesn't yet exist open(SSH_HOSTS, 'a').close() + null = open(DEVNULL, 'w') known_hosts = open(SSH_HOSTS, 'a') key = subprocess.Popen(['/usr/bin/ssh-keyscan', '-t', 'rsa', host], @@ -42,3 +45,12 @@ def _key_scan_and_add(host): known_hosts.write(key) null.close() known_hosts.close() + +def transfer_vhd(host, vhd_path): + """Rsyncs a VHD to an adjacent host""" + _key_scan_and_add(host) + if subprocess.call([RSYNC, vhd_path, "%s:/root/" % host]) != 0: + raise Exception("Unexpected VHD transfer failure") + +if __name__ == '__main__': + XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) -- cgit From 203c94c89caabc1d4ece4c462819a90c05cde163 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 7 Feb 2011 17:39:53 -0600 Subject: blargh --- nova/compute/api.py | 2 +- nova/virt/xenapi/vmops.py | 8 ++++++++ nova/virt/xenapi_conn.py | 2 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer | 6 +++--- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index f0d5ff2cb..6b2628378 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -379,7 +379,7 @@ class API(base.Base): kwargs = {'method': method, 'args': params} return rpc.call(context, queue, kwargs) - def _cast_scheduler_message(self, context, args) + def _cast_scheduler_message(self, context, args): """Generic handler for RPC calls to the scheduler""" rpc.cast(context, FLAGS.scheduler_topic, args) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 468881355..4b835c707 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -220,6 +220,14 @@ class VMOps(object): logging.debug(_("Finished snapshot and upload for VM %s"), instance) + def transfer_disk(self, instance, dest): + """ Copies a VHD from one host machine to another + + :param instance: the instance that owns the VHD in question + :param dest: the destination host machine + """ + + def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ raise NotImplementedError() diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 726106b37..2e587117a 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -193,7 +193,7 @@ class XenAPIConnection(object): self._vmops.power_on(instance) def transfer_disk(self, instance, dest, callback): - self._vmops.transfer_disk(dest) + self._vmops.transfer_disk(instance, dest) def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer index cde7bb823..2af4a758b 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer @@ -46,11 +46,11 @@ def _key_scan_and_add(host): null.close() known_hosts.close() -def transfer_vhd(host, vhd_path): +def transfer_file(host, file_path): """Rsyncs a VHD to an adjacent host""" _key_scan_and_add(host) - if subprocess.call([RSYNC, vhd_path, "%s:/root/" % host]) != 0: + if subprocess.call([RSYNC, file_path, "%s:/root/" % host]) != 0: raise Exception("Unexpected VHD transfer failure") if __name__ == '__main__': - XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) + XenAPIPlugin.dispatch({'transfer_file': transfer_file}) -- cgit From a51d187128def4f44ad06cd0880a3e3b4518e073 Mon Sep 17 00:00:00 2001 From: Ryan Lane Date: Tue, 8 Feb 2011 01:00:00 +0000 Subject: Catching all socket errors in _get_my_ip, since any socket error is likely enough to cause a failure in detection. --- nova/flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/flags.py b/nova/flags.py index 43bc174d2..1d8eba94f 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -208,7 +208,7 @@ def _get_my_ip(): (addr, port) = csock.getsockname() csock.close() return addr - except socket.gaierror as ex: + except socket.error as ex: return "127.0.0.1" -- cgit From 25f96558743a0dd10dfa82d1e5f463c0ed5ccfaa Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Tue, 8 Feb 2011 05:28:14 +0000 Subject: Launchpad automatic translations update. --- locale/cs.po | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index b9403687a..861efa37e 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -8,22 +8,22 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-01-10 11:25-0800\n" -"PO-Revision-Date: 2011-02-07 04:36+0000\n" -"Last-Translator: FULL NAME \n" +"PO-Revision-Date: 2011-02-07 12:45+0000\n" +"Last-Translator: David Pravec \n" "Language-Team: Czech \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-07 05:52+0000\n" +"X-Launchpad-Export-Date: 2011-02-08 05:28+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 msgid "Filename of root CA" -msgstr "" +msgstr "Jméno souboru kořenové CA" #: nova/crypto.py:49 msgid "Filename of private key" -msgstr "" +msgstr "Jméno souboru s privátním klíčem" #: nova/crypto.py:51 msgid "Filename of root Certificate Revokation List" @@ -31,15 +31,15 @@ msgstr "" #: nova/crypto.py:53 msgid "Where we keep our keys" -msgstr "" +msgstr "Adresář, do kterého ukládáme naše klíče" #: nova/crypto.py:55 msgid "Where we keep our root CA" -msgstr "" +msgstr "Adresář, do kterého ukládáme naši kořenovou CA" #: nova/crypto.py:57 msgid "Should we use a CA for each project?" -msgstr "" +msgstr "Použijeme CA pro každý projekt?" #: nova/crypto.py:61 #, python-format @@ -63,7 +63,7 @@ msgstr "" #: nova/exception.py:33 msgid "Unexpected error while running command." -msgstr "" +msgstr "Při spouštění příkazu došlo k nečekané chybě" #: nova/exception.py:36 #, python-format @@ -74,10 +74,15 @@ msgid "" "Stdout: %r\n" "Stderr: %r" msgstr "" +"%s\n" +"Příkaz: %s\n" +"Vrácená hodnota: %s\n" +"Stdout: %r\n" +"Stderr: %r" #: nova/exception.py:86 msgid "Uncaught exception" -msgstr "" +msgstr "Neošetřená výjimka" #: nova/fakerabbit.py:48 #, python-format @@ -112,20 +117,22 @@ msgstr "" #: nova/rpc.py:92 #, python-format msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." -msgstr "" +msgstr "AMQP server na %s:%d není dosažitelný. Zkusím znovu za %d sekund." #: nova/rpc.py:99 #, python-format msgid "Unable to connect to AMQP server after %d tries. Shutting down." msgstr "" +"Nepodařilo se připojit k AMQP serveru ani po %d pokusech. Tento proces bude " +"ukončen." #: nova/rpc.py:118 msgid "Reconnected to queue" -msgstr "" +msgstr "Znovu připojeno k AMQP frontě" #: nova/rpc.py:125 msgid "Failed to fetch message from queue" -msgstr "" +msgstr "Selhalo získání zprávy z AMQP fronty" #: nova/rpc.py:155 #, python-format @@ -135,41 +142,41 @@ msgstr "" #: nova/rpc.py:170 #, python-format msgid "received %s" -msgstr "" +msgstr "získáno: %s" #: nova/rpc.py:183 #, python-format msgid "no method for message: %s" -msgstr "" +msgstr "Není metoda pro zpracování zprávy: %s" #: nova/rpc.py:184 #, python-format msgid "No method for message: %s" -msgstr "" +msgstr "Není metoda pro zpracování zprávy: %s" #: nova/rpc.py:245 #, python-format msgid "Returning exception %s to caller" -msgstr "" +msgstr "Volajícímu je vrácena výjimka: %s" #: nova/rpc.py:286 #, python-format msgid "unpacked context: %s" -msgstr "" +msgstr "rozbalený obsah: %s" #: nova/rpc.py:305 msgid "Making asynchronous call..." -msgstr "" +msgstr "Volání asynchronní funkce..." #: nova/rpc.py:308 #, python-format msgid "MSG_ID is %s" -msgstr "" +msgstr "MSG_ID je %s" #: nova/rpc.py:356 #, python-format msgid "response %s" -msgstr "" +msgstr "odpověď %s" #: nova/rpc.py:365 #, python-format @@ -179,7 +186,7 @@ msgstr "" #: nova/rpc.py:366 #, python-format msgid "message %s" -msgstr "" +msgstr "zpráva %s" #: nova/service.py:157 #, python-format -- cgit From 976420e608140e449db5748e57cb18fab74b6d43 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 7 Feb 2011 22:05:45 -0800 Subject: use route -n instead of route to avoid chopped names --- nova/network/linux_net.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 69389b333..b66ca1973 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -215,10 +215,10 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): # NOTE(vish): This will break if there is already an ip on the # interface, so we move any ips to the bridge gateway = None - out, err = _execute("sudo route") + out, err = _execute("sudo route -n") for line in out.split("\n"): fields = line.split() - if fields and fields[0] == "default" and fields[-1] == interface: + if fields and fields[0] == "0.0.0.0" and fields[-1] == interface: gateway = fields[1] out, err = _execute("sudo ip addr show dev %s scope global" % interface) @@ -229,7 +229,7 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): _execute("sudo ip addr del %s dev %s" % (params, fields[-1])) _execute("sudo ip addr add %s dev %s" % (params, bridge)) if gateway: - _execute("sudo route add default gw %s" % gateway) + _execute("sudo route add 0.0.0.0 gw %s" % gateway) out, err = _execute("sudo brctl addif %s %s" % (bridge, interface), check_exit_code=False) -- cgit From 2ecd9de1a0b26cd7168a008f44caad47ecb49c01 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Tue, 8 Feb 2011 10:14:51 +0000 Subject: Fixed pep8 errors Unit tests passed --- nova/virt/xenapi/vm_utils.py | 9 ++-- plugins/xenserver/xenapi/etc/xapi.d/plugins/agent | 7 +-- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 63 ++++++++++++---------- .../xenapi/etc/xapi.d/plugins/objectstore | 46 ++++++++-------- 4 files changed, 70 insertions(+), 55 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4f6de7588..2d2200805 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -438,12 +438,15 @@ class VMHelper(HelperBase): return vdis else: return None + @classmethod - def lookup_kernel_ramdisk(cls,session,vm): + def lookup_kernel_ramdisk(cls, session, vm): vm_rec = session.get_xenapi().VM.get_record(vm) - return (vm_rec['PV_kernel'],vm_rec['PV_ramdisk']) + if 'PV_kernel' in vm_rec and 'PV_ramdisk' in vm_rec: + return (vm_rec['PV_kernel'], vm_rec['PV_ramdisk']) + else: + return (None, None) - @classmethod def compile_info(cls, record): """Fill record with VM status information""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent index 12c3a19c8..031a49708 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent @@ -73,8 +73,8 @@ def key_init(self, arg_dict): @jsonify def password(self, arg_dict): """Writes a request to xenstore that tells the agent to set - the root password for the given VM. The password should be - encrypted using the shared secret key that was returned by a + the root password for the given VM. The password should be + encrypted using the shared secret key that was returned by a previous call to key_init. The encrypted password value should be passed as the value for the 'enc_pass' key in arg_dict. """ @@ -108,7 +108,8 @@ def _wait_for_agent(self, request_id, arg_dict): # First, delete the request record arg_dict["path"] = "data/host/%s" % request_id xenstore.delete_record(self, arg_dict) - raise TimeoutError("TIMEOUT: No response from agent within %s seconds." % + raise TimeoutError( + "TIMEOUT: No response from agent within %s seconds." % AGENT_TIMEOUT) ret = xenstore.read_record(self, arg_dict) # Note: the response for None with be a string that includes diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 4fb96eeab..8cb439259 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -43,42 +43,47 @@ CHUNK_SIZE = 8192 KERNEL_DIR = '/boot/guest' FILE_SR_PATH = '/var/run/sr-mount' -def remove_kernel_ramdisk(session,args): + +def remove_kernel_ramdisk(session, args): """Removes kernel and/or ramdisk from dom0's file system""" - kernel_file=exists(args,'kernel-file') - ramdisk_file=exists(args,'ramdisk-file') + kernel_file = exists(args, 'kernel-file') + ramdisk_file = exists(args, 'ramdisk-file') if kernel_file: os.remove(kernel_file) if ramdisk_file: os.remove(ramdisk_file) return "ok" -def copy_kernel_vdi(session,args): + +def copy_kernel_vdi(session, args): vdi = exists(args, 'vdi-ref') - size = exists(args,'image-size') + size = exists(args, 'image-size') #Use the uuid as a filename - vdi_uuid=session.xenapi.VDI.get_uuid(vdi) - copy_args={'vdi_uuid':vdi_uuid,'vdi_size':int(size)} - filename=with_vdi_in_dom0(session, vdi, False, + vdi_uuid = session.xenapi.VDI.get_uuid(vdi) + copy_args = {'vdi_uuid': vdi_uuid, 'vdi_size': int(size)} + filename = with_vdi_in_dom0(session, vdi, False, lambda dev: - _copy_kernel_vdi('/dev/%s' % dev,copy_args)) + _copy_kernel_vdi('/dev/%s' % dev, copy_args)) return filename -def _copy_kernel_vdi(dest,copy_args): - vdi_uuid=copy_args['vdi_uuid'] - vdi_size=copy_args['vdi_size'] - logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s",dest,vdi_uuid) - filename=KERNEL_DIR + '/' + vdi_uuid + +def _copy_kernel_vdi(dest, copy_args): + vdi_uuid = copy_args['vdi_uuid'] + vdi_size = copy_args['vdi_size'] + logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s", + dest, vdi_uuid) + filename = KERNEL_DIR + '/' + vdi_uuid #read data from /dev/ and write into a file on /boot/guest - of=open(filename,'wb') - f=open(dest,'rb') + of = open(filename, 'wb') + f = open(dest, 'rb') #copy only vdi_size bytes - data=f.read(vdi_size) + data = f.read(vdi_size) of.write(data) f.close() - of.close() - logging.debug("Done. Filename: %s",filename) - return filename + of.close() + logging.debug("Done. Filename: %s", filename) + return filename + def put_vdis(session, args): params = pickle.loads(exists(args, 'params')) @@ -86,22 +91,23 @@ def put_vdis(session, args): image_id = params["image_id"] glance_host = params["glance_host"] glance_port = params["glance_port"] - + sr_path = get_sr_path(session) #FIXME(sirp): writing to a temp file until Glance supports chunked-PUTs - tmp_file = "%s.tar.gz" % os.path.join('/tmp', str(image_id)) + tmp_file = "%s.tar.gz" % os.path.join('/tmp', str(image_id)) tar_cmd = ['tar', '-zcf', tmp_file, '--directory=%s' % sr_path] - paths = [ "%s.vhd" % vdi_uuid for vdi_uuid in vdi_uuids ] + paths = ["%s.vhd" % vdi_uuid for vdi_uuid in vdi_uuids] tar_cmd.extend(paths) logging.debug("Bundling image with cmd: %s", tar_cmd) subprocess.call(tar_cmd) - logging.debug("Writing to test file %s", tmp_file) + logging.debug("Writing to test file %s", tmp_file) put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port) - return "" # FIXME(sirp): return anything useful here? + # FIXME(sirp): return anything useful here? + return "" def put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port): - size = os.path.getsize(tmp_file) + size = os.path.getsize(tmp_file) basename = os.path.basename(tmp_file) bundle = open(tmp_file, 'r') @@ -122,7 +128,7 @@ def put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port): for header, value in headers.iteritems(): conn.putheader(header, value) conn.endheaders() - + chunk = bundle.read(CHUNK_SIZE) while chunk: conn.send(chunk) @@ -135,6 +141,7 @@ def put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port): finally: bundle.close() + def get_sr_path(session): sr_ref = find_sr(session) @@ -165,6 +172,6 @@ def find_sr(session): if __name__ == '__main__': - XenAPIPlugin.dispatch({'put_vdis': put_vdis, + XenAPIPlugin.dispatch({'put_vdis': put_vdis, 'copy_kernel_vdi': copy_kernel_vdi, 'remove_kernel_ramdisk': remove_kernel_ramdisk}) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore b/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore index 8ee2f748d..d0313b4ed 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore @@ -43,34 +43,37 @@ SECTOR_SIZE = 512 MBR_SIZE_SECTORS = 63 MBR_SIZE_BYTES = MBR_SIZE_SECTORS * SECTOR_SIZE -def is_vdi_pv(session,args): + +def is_vdi_pv(session, args): logging.debug("Checking wheter VDI has PV kernel") vdi = exists(args, 'vdi-ref') - pv=with_vdi_in_dom0(session, vdi, False, + pv = with_vdi_in_dom0(session, vdi, False, lambda dev: _is_vdi_pv('/dev/%s' % dev)) if pv: return 'true' else: return 'false' + def _is_vdi_pv(dest): - logging.debug("Running pygrub against %s",dest) - output=os.popen('pygrub -qn %s' % dest) - pv=False + logging.debug("Running pygrub against %s", dest) + output = os.popen('pygrub -qn %s' % dest) + pv = False for line in output.readlines(): #try to find kernel string - m=re.search('(?<=kernel:)/.*(?:>)',line) + m = re.search('(?<=kernel:)/.*(?:>)', line) if m: - if m.group(0).find('xen')!=-1: - pv=True - logging.debug("PV:%d",pv) - return pv - + if m.group(0).find('xen') != -1: + pv = True + logging.debug("PV:%d", pv) + return pv + + def get_vdi(session, args): src_url = exists(args, 'src_url') username = exists(args, 'username') password = exists(args, 'password') - raw_image=validate_bool(args, 'raw', 'false') + raw_image = validate_bool(args, 'raw', 'false') add_partition = validate_bool(args, 'add_partition', 'false') (proto, netloc, url_path, _, _, _) = urlparse.urlparse(src_url) sr = find_sr(session) @@ -88,16 +91,17 @@ def get_vdi(session, args): vdi = create_vdi(session, sr, src_url, vdi_size, False) with_vdi_in_dom0(session, vdi, False, lambda dev: get_vdi_(proto, netloc, url_path, - username, password, add_partition,raw_image, + username, password, + add_partition, raw_image, virtual_size, '/dev/%s' % dev)) return session.xenapi.VDI.get_uuid(vdi) -def get_vdi_(proto, netloc, url_path, username, password, add_partition,raw_image, - virtual_size, dest): +def get_vdi_(proto, netloc, url_path, username, password, + add_partition, raw_image, virtual_size, dest): - #Salvatore: vdi should not be partitioned for raw images - if (add_partition and not raw_image): + #vdi should not be partitioned for raw images + if add_partition and not raw_image: write_partition(virtual_size, dest) offset = (add_partition and not raw_image and MBR_SIZE_BYTES) or 0 @@ -144,7 +148,7 @@ def get_kernel(session, args): password = exists(args, 'password') (proto, netloc, url_path, _, _, _) = urlparse.urlparse(src_url) - + dest = os.path.join(KERNEL_DIR, url_path[1:]) # Paranoid check against people using ../ to do rude things. @@ -154,8 +158,8 @@ def get_kernel(session, args): dirname = os.path.dirname(dest) try: os.makedirs(dirname) - except os.error, e: - if e.errno != errno.EEXIST: + except os.error, e: + if e.errno != errno.EEXIST: raise if not os.path.isdir(dirname): raise Exception('Cannot make directory %s', dirname) @@ -248,5 +252,5 @@ def download_all(response, length, dest_file, offset): if __name__ == '__main__': XenAPIPlugin.dispatch({'get_vdi': get_vdi, - 'get_kernel': get_kernel, + 'get_kernel': get_kernel, 'is_vdi_pv': is_vdi_pv}) -- cgit From 653ff11692fa5cd5ec5f9ea75cddc03df1b3dcd5 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Tue, 8 Feb 2011 16:39:46 +0000 Subject: avoiding HOST_UNAVAILABLE exception: if there is not enough free memory does not spawn the VM at all. instance state is set to "SHUTDOWN" --- nova/virt/xenapi/vm_utils.py | 11 +++++++++++ nova/virt/xenapi/vmops.py | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4bbd522c1..3a0f0a149 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -138,6 +138,17 @@ class VMHelper(HelperBase): LOG.debug(_('Created VM %(instance_name)s as %(vm_ref)s.') % locals()) return vm_ref + @classmethod + def ensure_free_mem(cls, session, instance): + instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] + mem = str(long(instance_type['memory_mb']) * 1024 * 1024) + #get free memory from host + host = session.get_xenapi_host() + host_free_mem = session.get_xenapi().host.compute_free_memory(host) + if (host_free_mem < mem ): + return False + return True + @classmethod def create_vbd(cls, session, vm_ref, vdi_ref, userdevice, bootable): """Create a VBD record. Returns a Deferred that gives the new diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index e84ce20c4..2d4e53083 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -66,7 +66,14 @@ class VMOps(object): if vm is not None: raise exception.Duplicate(_('Attempted to create' ' non-unique name %s') % instance.name) - + #ensure enough free memory, otherwise don't bother + if not VMHelper.ensure_free_mem(self._session,instance): + LOG.exception(_('instance %s: not enough free memory'), + instance['name']) + db.instance_set_state(context.get_admin_context(), + instance['id'], + power_state.SHUTDOWN) + return bridge = db.network_get_by_instance(context.get_admin_context(), instance['id'])['bridge'] network_ref = \ -- cgit From 3f2cd17011e17991ebf1a77605686ce3dc48d92e Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 8 Feb 2011 10:47:23 -0600 Subject: Changes and bug fixes --- nova/compute/manager.py | 8 ++-- nova/db/sqlalchemy/api.py | 6 +-- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 46 ++++++++++++++++++++++ nova/db/sqlalchemy/migration.py | 2 +- nova/virt/xenapi/vmops.py | 2 +- 5 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 485efc047..4189c49a4 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -387,7 +387,7 @@ class ComputeManager(manager.Manager): pass @exception.wrap_exception - @echecks_instance_lock + @checks_instance_lock def revert_resize(self, context, instance_id): """Destroys the new instance on the destination machine, reverts the model changes, and powers on the old @@ -406,7 +406,7 @@ class ComputeManager(manager.Manager): { 'instance_id': instance_id, 'source_host': instance_ref['host'], 'dest_host': socket.gethostbyname(socket.gethostname()), - 'status': 'pre-migrating' } + 'status': 'pre-migrating' }) LOG.audit(_('instance %s: migrating to '), instance_id, context=context) service = self.db.service_get_by_host_and_topic(context, instance_ref['host'], FLAGS.compute_topic) @@ -414,7 +414,7 @@ class ComputeManager(manager.Manager): service['id']) rpc.cast(context, topic, { 'method': 'resize_instance', - 'migration_id': migration_ref['id'], } + 'migration_id': migration_ref['id'], }) @exception.wrap_exception @checks_instance_lock @@ -437,7 +437,7 @@ class ComputeManager(manager.Manager): service['id']) rpc.cast(context, topic, { 'method': 'finish_resize', - 'migration_id': migration_ref['id'], } + 'migration_id': migration_ref['id'], }) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index e94f9f4d2..ece1cd373 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1942,8 +1942,7 @@ def migration_update(context, migration_id, values): def migration_get(context, migration_id): session = get_session() result = session.query(models.Migration.\ - filter_by(migration_id=migration_id)). - first() + filter_by(migration_id=migration_id)).first() if not result: raise exception.NotFound(_("No migration found with id %s") % migration_id) @@ -1954,8 +1953,7 @@ def migration_get(context, migration_id): def migration_get_by_instance(context, instance_id): session = get_session() result = session.query(models.Migration.\ - filter_by(instance_id=instance_id)). - first() + filter_by(instance_id=instance_id)).first() if not result: raise exception.NotFound(_("No migration found with instance id %s") % migration_id) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py new file mode 100644 index 000000000..bbe5cbcb0 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -0,0 +1,46 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 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.from sqlalchemy import * + +from migrate import * + +from nova import log as logging + + +meta = MetaData() + +# +# New Tables +# + +migrations = Table('migrations', meta, + Column('source_host', String(255)) + Column('dest_host', String(255)) + Column('instance_id', Integer, ForeignKey('instances.id'), nullable=True) + Column('status', String(255)) + ) + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (migrations): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py index 2a13c5466..644e3e45e 100644 --- a/nova/db/sqlalchemy/migration.py +++ b/nova/db/sqlalchemy/migration.py @@ -50,7 +50,7 @@ def db_version(): 'key_pairs', 'networks', 'projects', 'quotas', 'security_group_instance_association', 'security_group_rules', 'security_groups', - 'services', + 'services', 'migrations', 'users', 'user_project_association', 'user_project_role_association', 'user_role_association', diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 4b835c707..6a7621502 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -226,7 +226,7 @@ class VMOps(object): :param instance: the instance that owns the VHD in question :param dest: the destination host machine """ - + vm_ref = VMHelper.lookup(self._session, instance.name) def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ -- cgit From 49e07d0581317daf1bb605d56575c62743a210be Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 8 Feb 2011 11:07:03 -0600 Subject: Commas help --- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index bbe5cbcb0..dc384fbc3 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -27,10 +27,11 @@ meta = MetaData() # migrations = Table('migrations', meta, - Column('source_host', String(255)) - Column('dest_host', String(255)) - Column('instance_id', Integer, ForeignKey('instances.id'), nullable=True) - Column('status', String(255)) + Column('source_host', String(255)), + Column('dest_host', String(255)), + Column('instance_id', Integer, ForeignKey('instances.id'), + nullable=True), + Column('status', String(255)) ) def upgrade(migrate_engine): -- cgit From 6f30cff7374e91c2920759e13971c0149d96d821 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 8 Feb 2011 10:54:29 -0800 Subject: add docstring and revert set_ip changes as they are unnecessary --- nova/network/linux_net.py | 21 ++++++++++++++++----- nova/network/manager.py | 10 ++++------ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 37993d34a..df54606db 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -168,10 +168,10 @@ def remove_floating_forward(floating_ip, fixed_ip): % (fixed_ip, floating_ip)) -def ensure_vlan_bridge(vlan_num, bridge, net_attrs, set_ip=False): +def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): """Create a vlan and bridge unless they already exist""" interface = ensure_vlan(vlan_num) - ensure_bridge(bridge, interface, net_attrs, set_ip) + ensure_bridge(bridge, interface, net_attrs) def ensure_vlan(vlan_num): @@ -185,8 +185,19 @@ def ensure_vlan(vlan_num): return interface -def ensure_bridge(bridge, interface, net_attrs, set_ip=False): - """Create a bridge unless it already exists""" +def ensure_bridge(bridge, interface, net_attrs=None): + """Create a bridge unless it already exists. + + :param interface: the interface to create the bridge on. + :param net_attrs: dictionary with attributes used to create the bridge. + + If net_attrs is set, it will add the net_attrs['gateway'] to the bridge + using net_attrs['broadcast'] and net_attrs['cidr']. It will also add + the ip_v6 address specified in net_attrs['cidr_v6'] if use_ipv6 is set. + + The code will attempt to move any ips that already exist on the interface + onto the bridge and reset the default gateway if necessary. + """ if not _device_exists(bridge): LOG.debug(_("Starting Bridge interface for %s"), interface) _execute("sudo brctl addbr %s" % bridge) @@ -194,7 +205,7 @@ def ensure_bridge(bridge, interface, net_attrs, set_ip=False): # _execute("sudo brctl setageing %s 10" % bridge) _execute("sudo brctl stp %s off" % bridge) _execute("sudo ip link set %s up" % bridge) - if set_ip: + if net_attrs: # NOTE(vish): The ip for dnsmasq has to be the first address on the # bridge for it to respond to reqests properly suffix = net_attrs['cidr'].rpartition('/')[2] diff --git a/nova/network/manager.py b/nova/network/manager.py index 735327230..fbcbea131 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -402,8 +402,7 @@ class FlatDHCPManager(FlatManager): """Sets up matching network for compute hosts.""" network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_bridge(network_ref['bridge'], - FLAGS.flat_interface, - network_ref) + FLAGS.flat_interface) def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Setup dhcp for this network.""" @@ -428,7 +427,7 @@ class FlatDHCPManager(FlatManager): network_ref = db.network_get(context, network_id) self.driver.ensure_bridge(network_ref['bridge'], FLAGS.flat_interface, - network_ref, True) + network_ref) if not FLAGS.fake_network: self.driver.update_dhcp(context, network_id) if(FLAGS.use_ipv6): @@ -499,8 +498,7 @@ class VlanManager(NetworkManager): """Sets up matching network for compute hosts.""" network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_vlan_bridge(network_ref['vlan'], - network_ref['bridge'], - network_ref) + network_ref['bridge']) def create_networks(self, context, cidr, num_networks, network_size, cidr_v6, vlan_start, vpn_start): @@ -567,7 +565,7 @@ class VlanManager(NetworkManager): address = network_ref['vpn_public_address'] self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge'], - network_ref, True) + network_ref) # NOTE(vish): only ensure this forward if the address hasn't been set # manually. -- cgit From 089286802db0dca22cd67e46f26fab3ab0a3a73b Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 8 Feb 2011 13:12:21 -0600 Subject: Typos and primary keys --- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index dc384fbc3..4d01cd874 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -27,6 +27,7 @@ meta = MetaData() # migrations = Table('migrations', meta, + Column('id', Integer(), primary_key=True, nullable=False), Column('source_host', String(255)), Column('dest_host', String(255)), Column('instance_id', Integer, ForeignKey('instances.id'), @@ -38,7 +39,7 @@ def upgrade(migrate_engine): # Upgrade operations go here. Don't create your own engine; # bind migrate_engine to your metadata meta.bind = migrate_engine - for table in (migrations): + for table in (migrations, ): try: table.create() except Exception: -- cgit From ffc788fb41bf5a4bcb85cfa80b3437ed94d46291 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Tue, 8 Feb 2011 11:24:05 -0800 Subject: additional error checking for nova-manage instance_type --- bin/nova-manage | 57 +++++++++++++++++++++++++++++------------------ nova/db/sqlalchemy/api.py | 26 +++++++++++++++------ 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index c3fa9cf3f..78591585d 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -617,49 +617,62 @@ class InstanceTypeCommands(object): arguments: name memory_mb vcpus local_gb""" # FIXME(kpepple) check for absurb arguments (?) for option in [memory, flavorid, local_gb, vcpus]: - if option <= 0: + if (option <= 0) or (option.__class__ == int): print "Instance type parameters must be positive \ - numbers: %s" % option + integers: %s" % option sys.exit(1) - db.instance_type_create(context.get_admin_context(), + try: + db.instance_type_create(context.get_admin_context(), dict(name=name, memory_mb=memory, vcpus=vcpus, local_gb=local_gb, flavorid=flavorid)) - print "%s created" % name - return + print "%s created" % name + except exception.DBError, e: + print "%s is already a defined instance types" % name + sys.exit(1) + except: + print "Unknown error" + sys.exit(1) def delete(self, name): """Marks instance types / flavors as deleted arguments: name""" if name == None: print "Instance type name must be supplied" - exit(1) + sys.exit(1) else: - records = db.instance_type_destroy(context.get_admin_context(),\ - name) - if records != 1: + try: + records = db.instance_type_destroy( + context.get_admin_context(), + name) + except exception.NotFound, e: sys.exit(1) - return def list(self, name=None): """Lists all instance types / flavors arguments: [name]""" ctxt = context.get_admin_context() if name == None: - instance_types = db.instance_type_get_all(ctxt) - if len(instance_types) < 1: + try: + instance_types = db.instance_type_get_all(ctxt) + if len(instance_types) < 1: + sys.exit(1) + for k, v in instance_types.iteritems(): + print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ + (k, v["memory_mb"], + v["vcpus"], v["local_gb"]) + except exception.NotFound, e: + print e sys.exit(1) - for k, v in instance_types.iteritems(): - print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (k, v["memory_mb"], - v["vcpus"], v["local_gb"]) else: - instance_types = db.instance_type_get_by_name(ctxt, name) - print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (instance_types["name"], instance_types["memory_mb"], - instance_types["vcpus"], instance_types["local_gb"]) - - return + try: + instance_types = db.instance_type_get_by_name(ctxt, name) + print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ + (instance_types["name"], instance_types["memory_mb"], + instance_types["vcpus"], instance_types["local_gb"]) + except exception.NotFound, e: + print e + sys.exit(1) CATEGORIES = [ diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 6065af9ca..f084423e1 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2044,10 +2044,13 @@ def instance_type_get_all(context): filter_by(deleted=0).\ order_by("name").\ all() - inst_dict = {} - for i in inst_types: - inst_dict[i['name']] = dict(i) - return inst_dict + if inst_types: + inst_dict = {} + for i in inst_types: + inst_dict[i['name']] = dict(i) + return inst_dict + else: + raise exception.NotFound @require_context @@ -2057,7 +2060,10 @@ def instance_type_get_by_name(context, name): inst_type = session.query(models.InstanceTypes).\ filter_by(name=name).\ first() - return dict(inst_type) + if not inst_type: + raise exception.NotFound(_("No instance type with name %s") % name) + else: + return dict(inst_type) @require_context @@ -2067,7 +2073,10 @@ def instance_type_get_by_flavor_id(context, id): inst_type = session.query(models.InstanceTypes).\ filter_by(flavorid=int(id)).\ first() - return dict(inst_type) + if not inst_type: + raise exception.NotFound(_("No flavor with name %s") % id) + else: + return dict(inst_type) @require_admin_context @@ -2077,4 +2086,7 @@ def instance_type_destroy(context, name): instance_type_ref = session.query(models.InstanceTypes).\ filter_by(name=name) rows = instance_type_ref.update(dict(deleted=1)) - return rows + if not rows: + raise exception.NotFound(_("Couldn't delete instance type %s") % name) + else: + return rows -- cgit From 2f5d8a25c99875838a08ed06728bcd9d68cdd7f1 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Tue, 8 Feb 2011 11:31:20 -0800 Subject: added testing for nova-manage instance_type --- nova/tests/test_nova_manage.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 487f172ff..5645bf97e 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -66,6 +66,17 @@ class NovaManageTestCase(test.TestCase): "120", "1"], stdout=fnull) self.assertEqual(1, retcode) + def test_should_fail_on_duplicate_name(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "create", "fsfsfsdfsdf", "256", "1",\ + "120", "189"], stdout=fnull) + self.assertEqual(0, retcode) + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "create", "fsfsfsdfsdf", "256", "1",\ + "120", "190"], stdout=fnull) + self.assertEqual(1, retcode) + def test_instance_type_delete_should_fail_without_valid_name(self): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type",\ -- cgit From cf562efb7441a761fcebf0653e4a886655826a10 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Tue, 8 Feb 2011 15:03:35 -0800 Subject: added create and delete methods to instance_types in preparation to call them from nova-manage --- nova/compute/instance_types.py | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 0e20982e3..af097672e 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -27,13 +27,28 @@ from nova import flags from nova import exception FLAGS = flags.FLAGS -# FIXME(kpepple) for dynamic flavors -# INSTANCE_TYPES = { -# 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), -# 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), -# 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), -# 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), -# 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + + +def create(name, memory, vcpus, local_gb, flavorid): + """Creates instance types / flavors + arguments: name memory_mb vcpus local_gb""" + for option in [memory, flavorid, local_gb, vcpus]: + if (option <= 0) or (option.__class__ == int): + raise InvalidParameters + db.instance_type_create(context.get_admin_context(), + dict(name=name, memory_mb=memory, + vcpus=vcpus, local_gb=local_gb, + flavorid=flavorid)) + + +def delete(name): + """Marks instance types / flavors as deleted + arguments: name""" + if name == None: + raise InvalidParameters + else: + records = db.instance_type_destroy(context.get_admin_context(), + name) def get_all_types(): @@ -48,17 +63,15 @@ def get_all_flavors(): def get_by_type(instance_type): """retrieve instance_type details""" - # FIXME(kpepple) for dynamic flavors if instance_type is None: return FLAGS.default_instance_type try: ctxt = context.get_admin_context() inst_type = db.instance_type_get_by_name(ctxt, instance_type) - except Exception, e: - print e + return inst_type['name'] + except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s"), instance_type) - return inst_type['name'] def get_by_flavor_id(flavor_id): @@ -68,7 +81,7 @@ def get_by_flavor_id(flavor_id): try: ctxt = context.get_admin_context() flavor = db.instance_type_get_by_flavor_id(ctxt, flavor_id) - except Exception, e: + return flavor['name'] + except exception.DBError: raise exception.ApiError(_("Unknown flavor: %s"), flavor_id) - return flavor['name'] -- cgit From dd2544345e1686ee1ed020fd8f14607d10d8b3d1 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Tue, 8 Feb 2011 19:24:14 -0800 Subject: added testing for instance_types.py and refactored nova-manage to use instance_types.py instead of going directly to db. --- bin/nova-manage | 63 ++++++++++++++++++------------------ nova/compute/instance_types.py | 31 ++++++++++++++---- nova/db/sqlalchemy/api.py | 9 ++++-- nova/tests/test_instance_types.py | 67 +++++++++++++++++++++++++++++++++++++++ nova/tests/test_nova_manage.py | 4 +-- 5 files changed, 129 insertions(+), 45 deletions(-) create mode 100644 nova/tests/test_instance_types.py diff --git a/bin/nova-manage b/bin/nova-manage index 78591585d..3ee188593 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -84,6 +84,7 @@ from nova import utils from nova.api.ec2.cloud import ec2_id_to_id from nova.auth import manager from nova.cloudpipe import pipelib +from nova.compute import instance_types from nova.db import migration @@ -612,67 +613,63 @@ class VolumeCommands(object): class InstanceTypeCommands(object): """Class for managing instance types / flavors.""" + def _print_instance_types(self, n, val): + print "%s: %s memory(MB), %s vcpus, %s storage(GB), %s flavorid"\ + % (n, val["memory_mb"], val["vcpus"], + val["local_gb"], val["flavorid"]) + def create(self, name, memory, vcpus, local_gb, flavorid): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" - # FIXME(kpepple) check for absurb arguments (?) - for option in [memory, flavorid, local_gb, vcpus]: - if (option <= 0) or (option.__class__ == int): - print "Instance type parameters must be positive \ - integers: %s" % option - sys.exit(1) try: - db.instance_type_create(context.get_admin_context(), - dict(name=name, memory_mb=memory, - vcpus=vcpus, local_gb=local_gb, - flavorid=flavorid)) - print "%s created" % name + instance_types.create(name, memory, vcpus, local_gb, flavorid) + except exception.InvalidInputException, e: + print "Must supply valid parameters to create instance type" + sys.exit(1) except exception.DBError, e: - print "%s is already a defined instance types" % name + print "DB Error: %s" % e sys.exit(1) except: print "Unknown error" sys.exit(1) + else: + print "%s created" % name def delete(self, name): """Marks instance types / flavors as deleted arguments: name""" - if name == None: - print "Instance type name must be supplied" + try: + records = instance_types.destroy(name) + except exception.InvalidParameters: + print "Valid instance type name is required" + sys.exit(1) + except exception.NotFound, e: + print "Instance type name %s not found. \ + No instance type deleted." % name sys.exit(1) else: - try: - records = db.instance_type_destroy( - context.get_admin_context(), - name) - except exception.NotFound, e: - sys.exit(1) + print "%s deleted" % name def list(self, name=None): - """Lists all instance types / flavors + """Lists all or specific instance types / flavors arguments: [name]""" - ctxt = context.get_admin_context() if name == None: try: - instance_types = db.instance_type_get_all(ctxt) - if len(instance_types) < 1: - sys.exit(1) - for k, v in instance_types.iteritems(): - print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (k, v["memory_mb"], - v["vcpus"], v["local_gb"]) + inst_types = instance_types.get_all_types() except exception.NotFound, e: print e sys.exit(1) + else: + for k, v in inst_types.iteritems(): + self._print_instance_types(k, v) else: try: - instance_types = db.instance_type_get_by_name(ctxt, name) - print "%s : %s memory(MB), %s vcpus, %s storage(GB)" % \ - (instance_types["name"], instance_types["memory_mb"], - instance_types["vcpus"], instance_types["local_gb"]) + inst_types = instance_types.get_instance_type(name) except exception.NotFound, e: print e sys.exit(1) + else: + self._print_instance_types(name, inst_types) CATEGORIES = [ diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index af097672e..b97a0da25 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -32,33 +32,50 @@ FLAGS = flags.FLAGS def create(name, memory, vcpus, local_gb, flavorid): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" - for option in [memory, flavorid, local_gb, vcpus]: - if (option <= 0) or (option.__class__ == int): - raise InvalidParameters + for option in [memory, flavorid, vcpus]: + if option <= 0: + raise exception.InvalidInputException("Parameters incorrect") db.instance_type_create(context.get_admin_context(), dict(name=name, memory_mb=memory, vcpus=vcpus, local_gb=local_gb, flavorid=flavorid)) -def delete(name): +def destroy(name): """Marks instance types / flavors as deleted arguments: name""" if name == None: - raise InvalidParameters + raise exception.InvalidInputException else: records = db.instance_type_destroy(context.get_admin_context(), name) + if records == 0: + raise exception.NotFound("Cannot find instance type named %s" % name) + else: + return records def get_all_types(): """retrieves all instance_types""" - return db.instance_type_get_all() + return db.instance_type_get_all(context.get_admin_context()) def get_all_flavors(): """retrieves all flavors. alias for instance_types.get_all_types()""" - return get_all_types() + return get_all_types(context.get_admin_context()) + + +def get_instance_type(name): + """Retrieves single instance type by name""" + if name is None: + return FLAGS.default_instance_type + try: + ctxt = context.get_admin_context() + inst_type = db.instance_type_get_by_name(ctxt, name) + return inst_type + except exception.DBError: + raise exception.ApiError(_("Unknown instance type: %s"), + instance_type) def get_by_type(instance_type): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f084423e1..e5afb8eac 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2025,9 +2025,12 @@ def console_get(context, console_id, instance_id=None): @require_admin_context def instance_type_create(context, values): - instance_type_ref = models.InstanceTypes() - instance_type_ref.update(values) - instance_type_ref.save() + try: + instance_type_ref = models.InstanceTypes() + instance_type_ref.update(values) + instance_type_ref.save() + except: + raise exception.DBError return instance_type_ref diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py new file mode 100644 index 000000000..819431763 --- /dev/null +++ b/nova/tests/test_instance_types.py @@ -0,0 +1,67 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +Unit Tests for instance types code +""" +import datetime + +from nova import context +from nova import db +from nova import exception +from nova import flags +from nova import log as logging +from nova import test +from nova import utils +from nova.compute import instance_types +from nova.db.sqlalchemy.session import get_session +from nova.db.sqlalchemy import models + +FLAGS = flags.FLAGS +LOG = logging.getLogger('nova.tests.compute') + + +class InstanceTypeTestCase(test.TestCase): + """Test cases for instance type code""" + def setUp(self): + super(InstanceTypeTestCase, self).setUp() + session = get_session() + max_flavorid = session.query(models.InstanceTypes).\ + order_by("flavorid desc").first() + self.flavorid = max_flavorid["flavorid"] + 1 + self.name = str(datetime.datetime.utcnow()) + + def tearDown(self): + pass + + def test_instance_type_create_then_delete(self): + """Ensure instance types can be created""" + starting_inst_list = instance_types.get_all_types() + instance_types.create(self.name, 256, 1, 120, self.flavorid) + new = instance_types.get_all_types() + self.assertNotEqual(len(starting_inst_list), + len(new), + 'instance was not created') + rows = instance_types.destroy(self.name) + self.assertEqual(rows, 1) + self.assertEqual(1, + instance_types.get_instance_type(self.name)["deleted"]) + self.assertEqual(starting_inst_list, instance_types.get_all_types()) + + def test_get_all_instance_types(self): + """Ensures that all instance types can be retrieved""" + session = get_session() + total_instance_types = session.query(models.InstanceTypes).\ + count() + inst_types = instance_types.get_all_types() + self.assertEqual(total_instance_types, len(inst_types)) diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 5645bf97e..a2fa919a0 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -31,8 +31,8 @@ class NovaManageTestCase(test.TestCase): def test_create_and_delete_instance_types(self): fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", "test", "256", "1",\ + retcode = subprocess.call(["bin/nova-manage", "instance_type", + "create", "test", "256", "1", "120", "99"], stdout=fnull) self.assertEqual(0, retcode) retcode = subprocess.call(["bin/nova-manage", "instance_type",\ -- cgit From 129935dfa787c79f32b1e317e360bd05a3126319 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Wed, 9 Feb 2011 05:41:14 +0000 Subject: Launchpad automatic translations update. --- locale/de.po | 2136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2136 insertions(+) create mode 100644 locale/de.po diff --git a/locale/de.po b/locale/de.po new file mode 100644 index 000000000..e96292597 --- /dev/null +++ b/locale/de.po @@ -0,0 +1,2136 @@ +# German translation for nova +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the nova package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: nova\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-10 11:25-0800\n" +"PO-Revision-Date: 2011-02-08 13:06+0000\n" +"Last-Translator: Christian Berendt \n" +"Language-Team: German \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-02-09 05:41+0000\n" +"X-Generator: Launchpad (build 12177)\n" + +#: nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "Dateiname der Root CA" + +#: nova/crypto.py:49 +msgid "Filename of private key" +msgstr "Dateiname des Private Key" + +#: nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "Dateiname der Certificate Revocation List" + +#: nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "" + +#: nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "" + +#: nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "Soll eine eigenständige CA für jedes Projekt verwendet werden?" + +#: nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "" + +#: nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "" + +#: nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "" + +#: nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "Unerwarteter Fehler bei Ausführung des Kommandos." + +#: nova/exception.py:36 +#, python-format +msgid "" +"%s\n" +"Command: %s\n" +"Exit code: %s\n" +"Stdout: %r\n" +"Stderr: %r" +msgstr "" +"%s\n" +"Kommando: %s\n" +"Exit Code: %s\n" +"Stdout: %r\n" +"Stderr: %r" + +#: nova/exception.py:86 +msgid "Uncaught exception" +msgstr "Nicht abgefangene Ausnahme" + +#: nova/fakerabbit.py:48 +#, python-format +msgid "(%s) publish (key: %s) %s" +msgstr "(%s) öffentlich (Schlüssel: %s) %s" + +#: nova/fakerabbit.py:53 +#, python-format +msgid "Publishing to route %s" +msgstr "" + +#: nova/fakerabbit.py:83 +#, python-format +msgid "Declaring queue %s" +msgstr "" + +#: nova/fakerabbit.py:89 +#, python-format +msgid "Declaring exchange %s" +msgstr "" + +#: nova/fakerabbit.py:95 +#, python-format +msgid "Binding %s to %s with key %s" +msgstr "" + +#: nova/fakerabbit.py:120 +#, python-format +msgid "Getting from %s: %s" +msgstr "Beziehe von %s: %s" + +#: nova/rpc.py:92 +#, python-format +msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." +msgstr "" +"Der AMQP server %s:%d ist nicht erreichbar. Erneuter Versuch in %d Sekunden." + +#: nova/rpc.py:99 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "" + +#: nova/rpc.py:118 +msgid "Reconnected to queue" +msgstr "" + +#: nova/rpc.py:125 +msgid "Failed to fetch message from queue" +msgstr "" + +#: nova/rpc.py:155 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "" + +#: nova/rpc.py:170 +#, python-format +msgid "received %s" +msgstr "" + +#: nova/rpc.py:183 +#, python-format +msgid "no method for message: %s" +msgstr "" + +#: nova/rpc.py:184 +#, python-format +msgid "No method for message: %s" +msgstr "" + +#: nova/rpc.py:245 +#, python-format +msgid "Returning exception %s to caller" +msgstr "" + +#: nova/rpc.py:286 +#, python-format +msgid "unpacked context: %s" +msgstr "" + +#: nova/rpc.py:305 +msgid "Making asynchronous call..." +msgstr "" + +#: nova/rpc.py:308 +#, python-format +msgid "MSG_ID is %s" +msgstr "" + +#: nova/rpc.py:356 +#, python-format +msgid "response %s" +msgstr "" + +#: nova/rpc.py:365 +#, python-format +msgid "topic is %s" +msgstr "" + +#: nova/rpc.py:366 +#, python-format +msgid "message %s" +msgstr "" + +#: nova/service.py:157 +#, python-format +msgid "Starting %s node" +msgstr "" + +#: nova/service.py:169 +msgid "Service killed that has no database entry" +msgstr "" + +#: nova/service.py:190 +msgid "The service database object disappeared, Recreating it." +msgstr "" + +#: nova/service.py:202 +msgid "Recovered model server connection!" +msgstr "" + +#: nova/service.py:208 +msgid "model server went away" +msgstr "" + +#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 +#, python-format +msgid "Data store %s is unreachable. Trying again in %d seconds." +msgstr "" + +#: nova/service.py:232 nova/twistd.py:232 +#, python-format +msgid "Serving %s" +msgstr "" + +#: nova/service.py:234 nova/twistd.py:264 +msgid "Full set of FLAGS:" +msgstr "" + +#: nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "" + +#: nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "" + +#: nova/utils.py:53 +#, python-format +msgid "Inner Exception: %s" +msgstr "" + +#: nova/utils.py:54 +#, python-format +msgid "Class %s cannot be found" +msgstr "" + +#: nova/utils.py:113 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: nova/utils.py:125 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "" + +#: nova/utils.py:138 +#, python-format +msgid "Result was %s" +msgstr "" + +#: nova/utils.py:171 +#, python-format +msgid "debug in callback: %s" +msgstr "" + +#: nova/utils.py:176 +#, python-format +msgid "Running %s" +msgstr "" + +#: nova/utils.py:207 +#, python-format +msgid "Couldn't get IP, using 127.0.0.1 %s" +msgstr "" + +#: nova/utils.py:289 +#, python-format +msgid "Invalid backend: %s" +msgstr "" + +#: nova/utils.py:300 +#, python-format +msgid "backend %s" +msgstr "" + +#: nova/api/ec2/__init__.py:133 +msgid "Too many failed authentications." +msgstr "" + +#: nova/api/ec2/__init__.py:142 +#, python-format +msgid "" +"Access key %s has had %d failed authentications and will be locked out for " +"%d minutes." +msgstr "" + +#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:190 +#, python-format +msgid "Authenticated Request For %s:%s)" +msgstr "" + +#: nova/api/ec2/__init__.py:227 +#, python-format +msgid "action: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:229 +#, python-format +msgid "arg: %s\t\tval: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:301 +#, python-format +msgid "Unauthorized request for controller=%s and action=%s" +msgstr "" + +#: nova/api/ec2/__init__.py:339 +#, python-format +msgid "NotFound raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:342 +#, python-format +msgid "ApiError raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:349 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "" + +#: nova/api/ec2/__init__.py:354 +msgid "An unknown error has occurred. Please try your request again." +msgstr "" + +#: nova/api/ec2/admin.py:84 +#, python-format +msgid "Creating new user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:92 +#, python-format +msgid "Deleting user: %s" +msgstr "" + +#: nova/api/ec2/admin.py:114 +#, python-format +msgid "Adding role %s to user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 +#, python-format +msgid "Adding sitewide role %s to user %s" +msgstr "" + +#: nova/api/ec2/admin.py:122 +#, python-format +msgid "Removing role %s from user %s for project %s" +msgstr "" + +#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 +#, python-format +msgid "Removing sitewide role %s from user %s" +msgstr "" + +#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 +msgid "operation must be add or remove" +msgstr "" + +#: nova/api/ec2/admin.py:142 +#, python-format +msgid "Getting x509 for user: %s on project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:159 +#, python-format +msgid "Create project %s managed by %s" +msgstr "" + +#: nova/api/ec2/admin.py:170 +#, python-format +msgid "Delete project: %s" +msgstr "" + +#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 +#, python-format +msgid "Adding user %s to project %s" +msgstr "" + +#: nova/api/ec2/admin.py:188 +#, python-format +msgid "Removing user %s from project %s" +msgstr "" + +#: nova/api/ec2/apirequest.py:95 +#, python-format +msgid "Unsupported API request: controller = %s,action = %s" +msgstr "" + +#: nova/api/ec2/cloud.py:117 +#, python-format +msgid "Generating root CA: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:277 +#, python-format +msgid "Create key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:285 +#, python-format +msgid "Delete key pair %s" +msgstr "" + +#: nova/api/ec2/cloud.py:357 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "" + +#: nova/api/ec2/cloud.py:361 +msgid "Invalid port range" +msgstr "" + +#: nova/api/ec2/cloud.py:392 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 +msgid "No rule for the specified parameters." +msgstr "" + +#: nova/api/ec2/cloud.py:421 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "" + +#: nova/api/ec2/cloud.py:432 +#, python-format +msgid "This rule already exists in group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:460 +#, python-format +msgid "Create Security Group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:463 +#, python-format +msgid "group %s already exists" +msgstr "" + +#: nova/api/ec2/cloud.py:475 +#, python-format +msgid "Delete security group %s" +msgstr "" + +#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 +#, python-format +msgid "Get console output for instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:543 +#, python-format +msgid "Create volume of %s GB" +msgstr "" + +#: nova/api/ec2/cloud.py:567 +#, python-format +msgid "Attach volume %s to instacne %s at %s" +msgstr "" + +#: nova/api/ec2/cloud.py:579 +#, python-format +msgid "Detach volume %s" +msgstr "" + +#: nova/api/ec2/cloud.py:686 +msgid "Allocate address" +msgstr "" + +#: nova/api/ec2/cloud.py:691 +#, python-format +msgid "Release address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:696 +#, python-format +msgid "Associate address %s to instance %s" +msgstr "" + +#: nova/api/ec2/cloud.py:703 +#, python-format +msgid "Disassociate address %s" +msgstr "" + +#: nova/api/ec2/cloud.py:730 +msgid "Going to start terminating instances" +msgstr "" + +#: nova/api/ec2/cloud.py:738 +#, python-format +msgid "Reboot instance %r" +msgstr "" + +#: nova/api/ec2/cloud.py:775 +#, python-format +msgid "De-registering image %s" +msgstr "" + +#: nova/api/ec2/cloud.py:783 +#, python-format +msgid "Registered image %s with id %s" +msgstr "" + +#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 +#, python-format +msgid "attribute not supported: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:794 +#, python-format +msgid "invalid id: %s" +msgstr "" + +#: nova/api/ec2/cloud.py:807 +msgid "user or group not specified" +msgstr "" + +#: nova/api/ec2/cloud.py:809 +msgid "only group \"all\" is supported" +msgstr "" + +#: nova/api/ec2/cloud.py:811 +msgid "operation_type must be add or remove" +msgstr "" + +#: nova/api/ec2/cloud.py:812 +#, python-format +msgid "Updating image %s publicity" +msgstr "" + +#: nova/api/ec2/metadatarequesthandler.py:75 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:70 +#, python-format +msgid "Caught error: %s" +msgstr "" + +#: nova/api/openstack/__init__.py:86 +msgid "Including admin operations in API." +msgstr "" + +#: nova/api/openstack/servers.py:184 +#, python-format +msgid "Compute.api::lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:199 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "" + +#: nova/api/openstack/servers.py:213 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "" + +#: nova/api/openstack/servers.py:224 +#, python-format +msgid "Compute.api::pause %s" +msgstr "" + +#: nova/api/openstack/servers.py:235 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "" + +#: nova/api/openstack/servers.py:246 +#, python-format +msgid "compute.api::suspend %s" +msgstr "" + +#: nova/api/openstack/servers.py:257 +#, python-format +msgid "compute.api::resume %s" +msgstr "" + +#: nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "" + +#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "" + +#: nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "" + +#: nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "" + +#: nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "" + +#: nova/auth/ldapdriver.py:181 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:218 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "" + +#: nova/auth/ldapdriver.py:478 +#, python-format +msgid "User %s is already a member of the group %s" +msgstr "" + +#: nova/auth/ldapdriver.py:507 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "" + +#: nova/auth/ldapdriver.py:528 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "" + +#: nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "" + +#: nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "" + +#: nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "" + +#: nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "" + +#: nova/auth/manager.py:275 +#, python-format +msgid "failed authorization: no project named %s (user=%s)" +msgstr "" + +#: nova/auth/manager.py:277 +#, python-format +msgid "No project called %s could be found" +msgstr "" + +#: nova/auth/manager.py:281 +#, python-format +msgid "Failed authorization: user %s not admin and not member of project %s" +msgstr "" + +#: nova/auth/manager.py:283 +#, python-format +msgid "User %s is not a member of project %s" +msgstr "" + +#: nova/auth/manager.py:292 nova/auth/manager.py:303 +#, python-format +msgid "Invalid signature for user %s" +msgstr "" + +#: nova/auth/manager.py:293 nova/auth/manager.py:304 +msgid "Signature does not match" +msgstr "" + +#: nova/auth/manager.py:374 +msgid "Must specify project" +msgstr "" + +#: nova/auth/manager.py:408 +#, python-format +msgid "The %s role can not be found" +msgstr "" + +#: nova/auth/manager.py:410 +#, python-format +msgid "The %s role is global only" +msgstr "" + +#: nova/auth/manager.py:412 +#, python-format +msgid "Adding role %s to user %s in project %s" +msgstr "" + +#: nova/auth/manager.py:438 +#, python-format +msgid "Removing role %s from user %s on project %s" +msgstr "" + +#: nova/auth/manager.py:505 +#, python-format +msgid "Created project %s with manager %s" +msgstr "" + +#: nova/auth/manager.py:523 +#, python-format +msgid "modifying project %s" +msgstr "" + +#: nova/auth/manager.py:553 +#, python-format +msgid "Remove user %s from project %s" +msgstr "" + +#: nova/auth/manager.py:581 +#, python-format +msgid "Deleting project %s" +msgstr "" + +#: nova/auth/manager.py:637 +#, python-format +msgid "Created user %s (admin: %r)" +msgstr "" + +#: nova/auth/manager.py:645 +#, python-format +msgid "Deleting user %s" +msgstr "" + +#: nova/auth/manager.py:655 +#, python-format +msgid "Access Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:657 +#, python-format +msgid "Secret Key change for user %s" +msgstr "" + +#: nova/auth/manager.py:659 +#, python-format +msgid "Admin status set to %r for user %s" +msgstr "" + +#: nova/auth/manager.py:708 +#, python-format +msgid "No vpn data for project %s" +msgstr "" + +#: nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "" + +#: nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "" + +#: nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "" + +#: nova/compute/api.py:67 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "" + +#: nova/compute/api.py:73 +#, python-format +msgid "Instance %d has no host" +msgstr "" + +#: nova/compute/api.py:92 +#, python-format +msgid "Quota exceeeded for %s, tried to run %s instances" +msgstr "" + +#: nova/compute/api.py:94 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "" + +#: nova/compute/api.py:109 +msgid "Creating a raw instance" +msgstr "" + +#: nova/compute/api.py:156 +#, python-format +msgid "Going to run %s instances..." +msgstr "" + +#: nova/compute/api.py:180 +#, python-format +msgid "Casting to scheduler for %s/%s's instance %s" +msgstr "" + +#: nova/compute/api.py:279 +#, python-format +msgid "Going to try and terminate %s" +msgstr "" + +#: nova/compute/api.py:283 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "" + +#: nova/compute/api.py:288 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "" + +#: nova/compute/api.py:450 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "" + +#: nova/compute/api.py:465 +msgid "Volume isn't attached to anything!" +msgstr "" + +#: nova/compute/disk.py:71 +#, python-format +msgid "Input partition size not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:75 +#, python-format +msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" +msgstr "" + +#: nova/compute/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "" + +#: nova/compute/disk.py:136 +#, python-format +msgid "Failed to load partition: %s" +msgstr "" + +#: nova/compute/disk.py:158 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "" + +#: nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "" + +#: nova/compute/manager.py:69 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "" + +#: nova/compute/manager.py:71 +#, python-format +msgid "check_instance_lock: arguments: |%s| |%s| |%s|" +msgstr "" + +#: nova/compute/manager.py:75 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "" + +#: nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "" + +#: nova/compute/manager.py:82 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "" + +#: nova/compute/manager.py:86 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "" + +#: nova/compute/manager.py:157 +msgid "Instance has already been created" +msgstr "" + +#: nova/compute/manager.py:158 +#, python-format +msgid "instance %s: starting..." +msgstr "" + +#: nova/compute/manager.py:197 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "" + +#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 +#, python-format +msgid "Terminating instance %s" +msgstr "" + +#: nova/compute/manager.py:217 +#, python-format +msgid "Disassociating address %s" +msgstr "" + +#: nova/compute/manager.py:230 +#, python-format +msgid "Deallocating address %s" +msgstr "" + +#: nova/compute/manager.py:243 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "" + +#: nova/compute/manager.py:257 +#, python-format +msgid "Rebooting instance %s" +msgstr "" + +#: nova/compute/manager.py:260 +#, python-format +msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:286 +#, python-format +msgid "instance %s: snapshotting" +msgstr "" + +#: nova/compute/manager.py:289 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %s (state: %s excepted: %s)" +msgstr "" + +#: nova/compute/manager.py:301 +#, python-format +msgid "instance %s: rescuing" +msgstr "" + +#: nova/compute/manager.py:316 +#, python-format +msgid "instance %s: unrescuing" +msgstr "" + +#: nova/compute/manager.py:335 +#, python-format +msgid "instance %s: pausing" +msgstr "" + +#: nova/compute/manager.py:352 +#, python-format +msgid "instance %s: unpausing" +msgstr "" + +#: nova/compute/manager.py:369 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "" + +#: nova/compute/manager.py:382 +#, python-format +msgid "instance %s: suspending" +msgstr "" + +#: nova/compute/manager.py:401 +#, python-format +msgid "instance %s: resuming" +msgstr "" + +#: nova/compute/manager.py:420 +#, python-format +msgid "instance %s: locking" +msgstr "" + +#: nova/compute/manager.py:432 +#, python-format +msgid "instance %s: unlocking" +msgstr "" + +#: nova/compute/manager.py:442 +#, python-format +msgid "instance %s: getting locked state" +msgstr "" + +#: nova/compute/manager.py:462 +#, python-format +msgid "instance %s: attaching volume %s to %s" +msgstr "" + +#: nova/compute/manager.py:478 +#, python-format +msgid "instance %s: attach failed %s, removing" +msgstr "" + +#: nova/compute/manager.py:493 +#, python-format +msgid "Detach volume %s from mountpoint %s on instance %s" +msgstr "" + +#: nova/compute/manager.py:497 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "" + +#: nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "" + +#: nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "" + +#: nova/compute/monitor.py:355 +#, python-format +msgid "Cannot get blockstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:377 +#, python-format +msgid "Cannot get ifstats for \"%s\" on \"%s\"" +msgstr "" + +#: nova/compute/monitor.py:412 +msgid "unexpected exception getting connection" +msgstr "" + +#: nova/compute/monitor.py:427 +#, python-format +msgid "Found instance: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:43 +msgid "Use of empty request context is deprecated" +msgstr "" + +#: nova/db/sqlalchemy/api.py:132 +#, python-format +msgid "No service for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:229 +#, python-format +msgid "No service for %s, %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:574 +#, python-format +msgid "No floating ip for address %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:668 +#, python-format +msgid "No instance for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 +#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 +#, python-format +msgid "Instance %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:891 +#, python-format +msgid "no keypair for user %s, name %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 +#, python-format +msgid "No network for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1036 +#, python-format +msgid "No network for bridge %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1050 +#, python-format +msgid "No network for instance %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1180 +#, python-format +msgid "Token %s does not exist" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1205 +#, python-format +msgid "No quota for project_id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1356 +#, python-format +msgid "No volume for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1401 +#, python-format +msgid "Volume %s not found" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1413 +#, python-format +msgid "No export device found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1426 +#, python-format +msgid "No target id found for volume %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1471 +#, python-format +msgid "No security group with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1488 +#, python-format +msgid "No security group named %s for project: %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1576 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1650 +#, python-format +msgid "No user for id %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1666 +#, python-format +msgid "No user for access key %s" +msgstr "" + +#: nova/db/sqlalchemy/api.py:1728 +#, python-format +msgid "No project with id %s" +msgstr "" + +#: nova/image/glance.py:78 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images" +msgstr "" + +#: nova/image/glance.py:97 +#, python-format +msgid "Parallax returned HTTP error %d from request for /images/detail" +msgstr "" + +#: nova/image/s3.py:82 +#, python-format +msgid "Image %s could not be found" +msgstr "" + +#: nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "" + +#: nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "" + +#: nova/network/linux_net.py:176 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "" + +#: nova/network/linux_net.py:186 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "" + +#: nova/network/linux_net.py:254 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "" + +#: nova/network/linux_net.py:256 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "" + +#: nova/network/linux_net.py:334 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "" + +#: nova/network/manager.py:135 +msgid "setting network host" +msgstr "" + +#: nova/network/manager.py:190 +#, python-format +msgid "Leasing IP %s" +msgstr "" + +#: nova/network/manager.py:194 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "" + +#: nova/network/manager.py:197 +#, python-format +msgid "IP %s leased to bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:205 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "" + +#: nova/network/manager.py:214 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "" + +#: nova/network/manager.py:217 +#, python-format +msgid "IP %s released from bad mac %s vs %s" +msgstr "" + +#: nova/network/manager.py:220 +#, python-format +msgid "IP %s released that was not leased" +msgstr "" + +#: nova/network/manager.py:442 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "" + +#: nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "" + +#: nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "" + +#: nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "" + +#: nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:271 +#, python-format +msgid "Getting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:274 +#, python-format +msgid "Unauthorized attempt to get object %s from bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:292 +#, python-format +msgid "Putting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:295 +#, python-format +msgid "Unauthorized attempt to upload object %s to bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:314 +#, python-format +msgid "Deleting object: %s / %s" +msgstr "" + +#: nova/objectstore/handler.py:393 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "" + +#: nova/objectstore/handler.py:401 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "" + +#: nova/objectstore/handler.py:406 +#, python-format +msgid "Starting image upload: %s" +msgstr "" + +#: nova/objectstore/handler.py:420 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "" + +#: nova/objectstore/handler.py:428 +#, python-format +msgid "Toggling publicity flag of image %s %r" +msgstr "" + +#: nova/objectstore/handler.py:433 +#, python-format +msgid "Updating user fields on image %s" +msgstr "" + +#: nova/objectstore/handler.py:447 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "" + +#: nova/objectstore/handler.py:452 +#, python-format +msgid "Deleted image: %s" +msgstr "" + +#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 +#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 +msgid "No hosts found" +msgstr "" + +#: nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "" + +#: nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %s %s for %s" +msgstr "" + +#: nova/scheduler/simple.py:63 +msgid "All hosts have too many cores" +msgstr "" + +#: nova/scheduler/simple.py:95 +msgid "All hosts have too many gigabytes" +msgstr "" + +#: nova/scheduler/simple.py:115 +msgid "All hosts have too many networks" +msgstr "" + +#: nova/tests/test_cloud.py:198 +msgid "Can't test instances without a real virtual env." +msgstr "" + +#: nova/tests/test_cloud.py:210 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "" + +#: nova/tests/test_compute.py:104 +#, python-format +msgid "Running instances: %s" +msgstr "" + +#: nova/tests/test_compute.py:110 +#, python-format +msgid "After terminating instances: %s" +msgstr "" + +#: nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %s, %s" +msgstr "" + +#: nova/tests/test_rpc.py:94 +#, python-format +msgid "Nested return %s" +msgstr "" + +#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 +#, python-format +msgid "Received %s" +msgstr "" + +#: nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "" + +#: nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "" + +#: nova/virt/fake.py:210 +#, python-format +msgid "Instance %s Not Found" +msgstr "" + +#: nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "" + +#: nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "" + +#: nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "" + +#: nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "" + +#: nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "" + +#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 +#, python-format +msgid "Created VM %s..." +msgstr "" + +#: nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "" + +#: nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %s by attaching disk file %s" +msgstr "" + +#: nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "" + +#: nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "" + +#: nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "" + +#: nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "" + +#: nova/virt/hyperv.py:275 +#, python-format +msgid "Created switch port %s on switch %s" +msgstr "" + +#: nova/virt/hyperv.py:285 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "" + +#: nova/virt/hyperv.py:287 +#, python-format +msgid "Created nic for %s " +msgstr "" + +#: nova/virt/hyperv.py:320 +#, python-format +msgid "WMI job failed: %s" +msgstr "" + +#: nova/virt/hyperv.py:322 +#, python-format +msgid "WMI job succeeded: %s, Elapsed=%s " +msgstr "" + +#: nova/virt/hyperv.py:358 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:383 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "" + +#: nova/virt/hyperv.py:389 +#, python-format +msgid "Del: disk %s vm %s" +msgstr "" + +#: nova/virt/hyperv.py:405 +#, python-format +msgid "" +"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " +"cpu_time=%s" +msgstr "" + +#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 +#, python-format +msgid "duplicate name found: %s" +msgstr "" + +#: nova/virt/hyperv.py:444 +#, python-format +msgid "Successfully changed vm state of %s to %s" +msgstr "" + +#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 +#, python-format +msgid "Failed to change vm state of %s to %s" +msgstr "" + +#: nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %s -- placed in %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:144 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:157 +msgid "Connection to libvirt broke" +msgstr "" + +#: nova/virt/libvirt_conn.py:229 +#, python-format +msgid "instance %s: deleting instance files %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:271 +#, python-format +msgid "No disk at %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:278 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "" + +#: nova/virt/libvirt_conn.py:294 +#, python-format +msgid "instance %s: rebooted" +msgstr "" + +#: nova/virt/libvirt_conn.py:297 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:340 +#, python-format +msgid "instance %s: rescued" +msgstr "" + +#: nova/virt/libvirt_conn.py:343 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:370 +#, python-format +msgid "instance %s: is running" +msgstr "" + +#: nova/virt/libvirt_conn.py:381 +#, python-format +msgid "instance %s: booted" +msgstr "" + +#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 +#, python-format +msgid "instance %s: failed to boot" +msgstr "" + +#: nova/virt/libvirt_conn.py:395 +#, python-format +msgid "virsh said: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:399 +msgid "cool, it's a device" +msgstr "" + +#: nova/virt/libvirt_conn.py:407 +#, python-format +msgid "data: %r, fpath: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:415 +#, python-format +msgid "Contents of file %s: %r" +msgstr "" + +#: nova/virt/libvirt_conn.py:449 +#, python-format +msgid "instance %s: Creating image" +msgstr "" + +#: nova/virt/libvirt_conn.py:505 +#, python-format +msgid "instance %s: injecting key into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:508 +#, python-format +msgid "instance %s: injecting net into image %s" +msgstr "" + +#: nova/virt/libvirt_conn.py:516 +#, python-format +msgid "instance %s: ignoring error injecting data into image %s (%s)" +msgstr "" + +#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "" + +#: nova/virt/libvirt_conn.py:589 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "" + +#: nova/virt/xenapi_conn.py:113 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" + +#: nova/virt/xenapi_conn.py:263 +#, python-format +msgid "Task [%s] %s status: success %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:271 +#, python-format +msgid "Task [%s] %s status: %s %s" +msgstr "" + +#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 +#, python-format +msgid "Got exception: %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:72 +#, python-format +msgid "%s: _db_content => %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 +#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 +msgid "Raising NotImplemented" +msgstr "" + +#: nova/virt/xenapi/fake.py:249 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:283 +#, python-format +msgid "Calling %s %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:288 +#, python-format +msgid "Calling getter %s" +msgstr "" + +#: nova/virt/xenapi/fake.py:340 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:127 +#, python-format +msgid "Created VM %s as %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:147 +#, python-format +msgid "Creating VBD for VM %s, VDI %s ... " +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:149 +#, python-format +msgid "Created VBD %s for VM %s, VDI %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:165 +#, python-format +msgid "VBD not found in instance %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:175 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:202 +#, python-format +msgid "Creating VIF for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:205 +#, python-format +msgid "Created VIF %s for VM %s, network %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:216 +#, python-format +msgid "Snapshotting VM %s with label '%s'..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:229 +#, python-format +msgid "Created snapshot %s from VM %s." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:243 +#, python-format +msgid "Asking xapi to upload %s as '%s'" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:261 +#, python-format +msgid "Asking xapi to fetch %s as %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:279 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:290 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:318 +#, python-format +msgid "VDI %s is still available" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:331 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:333 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:390 +#, python-format +msgid "VHD %s has parent %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:407 +#, python-format +msgid "Re-scanning SR %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:431 +#, python-format +msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:448 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vm_utils.py:452 +#, python-format +msgid "Unexpected number of VDIs (%s) found for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:62 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:99 +#, python-format +msgid "Starting VM %s..." +msgstr "" + +#: nova/virt/xenapi/vmops.py:101 +#, python-format +msgid "Spawning VM %s created %s." +msgstr "" + +#: nova/virt/xenapi/vmops.py:112 +#, python-format +msgid "Instance %s: booted" +msgstr "" + +#: nova/virt/xenapi/vmops.py:137 +#, python-format +msgid "Instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:166 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:174 +#, python-format +msgid "Unable to Snapshot %s: %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:184 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:252 +#, python-format +msgid "suspend: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "resume: instance not present %s" +msgstr "" + +#: nova/virt/xenapi/vmops.py:271 +#, python-format +msgid "Instance not found %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %s as %s." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %s when getting PBDs for %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %s when unplugging PBD %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %s when forgetting SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %s, %s" +msgstr "" + +#: nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %s, %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:81 +#, python-format +msgid "Unable to use SR %s for instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:95 +#, python-format +msgid "Mountpoint %s attached to instance %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:106 +#, python-format +msgid "Detach_volume: %s, %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:113 +#, python-format +msgid "Unable to locate volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:121 +#, python-format +msgid "Unable to detach volume %s" +msgstr "" + +#: nova/virt/xenapi/volumeops.py:128 +#, python-format +msgid "Mountpoint %s detached from instance %s" +msgstr "" + +#: nova/volume/api.py:44 +#, python-format +msgid "Quota exceeeded for %s, tried to create %sG volume" +msgstr "" + +#: nova/volume/api.py:46 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %s" +msgstr "" + +#: nova/volume/api.py:70 nova/volume/api.py:95 +msgid "Volume status must be available" +msgstr "" + +#: nova/volume/api.py:97 +msgid "Volume is already attached" +msgstr "" + +#: nova/volume/api.py:103 +msgid "Volume is already detached" +msgstr "" + +#: nova/volume/driver.py:76 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "" + +#: nova/volume/driver.py:85 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "" + +#: nova/volume/driver.py:210 +#, python-format +msgid "FAKE AOE: %s" +msgstr "" + +#: nova/volume/driver.py:315 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "" + +#: nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "" + +#: nova/volume/manager.py:93 +#, python-format +msgid "volume %s: creating" +msgstr "" + +#: nova/volume/manager.py:102 +#, python-format +msgid "volume %s: creating lv of size %sG" +msgstr "" + +#: nova/volume/manager.py:106 +#, python-format +msgid "volume %s: creating export" +msgstr "" + +#: nova/volume/manager.py:113 +#, python-format +msgid "volume %s: created successfully" +msgstr "" + +#: nova/volume/manager.py:121 +msgid "Volume is still attached" +msgstr "" + +#: nova/volume/manager.py:123 +msgid "Volume is not local to this node" +msgstr "" + +#: nova/volume/manager.py:124 +#, python-format +msgid "volume %s: removing export" +msgstr "" + +#: nova/volume/manager.py:126 +#, python-format +msgid "volume %s: deleting" +msgstr "" + +#: nova/volume/manager.py:129 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "" -- cgit From d5501234cfc434fbb4b959b63822598a1a68b88b Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Wed, 9 Feb 2011 11:34:23 +0300 Subject: Give a better error message if the instance type specified is invalid. --- nova/compute/instance_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 196d6a8df..510bfd99e 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -38,8 +38,8 @@ def get_by_type(instance_type): if instance_type is None: return FLAGS.default_instance_type if instance_type not in INSTANCE_TYPES: - raise exception.ApiError(_("Unknown instance type: %s"), - instance_type) + raise exception.ApiError(_("Unknown instance type: %s") % (instance_type), + "Invalid") return instance_type -- cgit From f6ec9568561dd430bd772f171f5bbddd0bd038c6 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 9 Feb 2011 10:08:15 +0000 Subject: Added test case for 'not enough memory' Successfully ran unit tests Fixed pep8 errors --- nova/compute/manager.py | 2 +- nova/compute/power_state.py | 4 +++- nova/tests/test_xenapi.py | 11 +++++++++-- nova/virt/xenapi/fake.py | 4 ++++ nova/virt/xenapi/vm_utils.py | 4 ++-- nova/virt/xenapi/vmops.py | 7 ++++--- 6 files changed, 23 insertions(+), 9 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index f4418af26..bb999931c 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -127,7 +127,7 @@ class ComputeManager(manager.Manager): info = self.driver.get_info(instance_ref['name']) state = info['state'] except exception.NotFound: - state = power_state.NOSTATE + state = power_state.FAILED self.db.instance_set_state(context, instance_id, state) def get_console_topic(self, context, **_kwargs): diff --git a/nova/compute/power_state.py b/nova/compute/power_state.py index 37039d2ec..adfc2dff0 100644 --- a/nova/compute/power_state.py +++ b/nova/compute/power_state.py @@ -27,6 +27,7 @@ SHUTDOWN = 0x04 SHUTOFF = 0x05 CRASHED = 0x06 SUSPENDED = 0x07 +FAILED = 0x08 def name(code): @@ -38,5 +39,6 @@ def name(code): SHUTDOWN: 'shutdown', SHUTOFF: 'shutdown', CRASHED: 'crashed', - SUSPENDED: 'suspended'} + SUSPENDED: 'suspended', + FAILED: 'failed to spawn'} return d[code] diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 9f5b266f3..d5660c5d1 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -243,7 +243,8 @@ class XenAPIVMTestCase(test.TestCase): # Check that the VM is running according to XenAPI. self.assertEquals(vm['power_state'], 'Running') - def _test_spawn(self, image_id, kernel_id, ramdisk_id): + def _test_spawn(self, image_id, kernel_id, ramdisk_id, + instance_type="m1.large"): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) values = {'name': 1, 'id': 1, @@ -252,7 +253,7 @@ class XenAPIVMTestCase(test.TestCase): 'image_id': image_id, 'kernel_id': kernel_id, 'ramdisk_id': ramdisk_id, - 'instance_type': 'm1.large', + 'instance_type': instance_type, 'mac_address': 'aa:bb:cc:dd:ee:ff', } conn = xenapi_conn.get_connection(False) @@ -260,6 +261,12 @@ class XenAPIVMTestCase(test.TestCase): conn.spawn(instance) self.check_vm_record(conn) + def test_spawn_not_enough_memory(self): + FLAGS.xenapi_image_service = 'glance' + self.assertRaises(Exception, + self._test_spawn, + 1, 2, 3, "m1.xlarge") + def test_spawn_raw_objectstore(self): FLAGS.xenapi_image_service = 'objectstore' self._test_spawn(1, None, None) diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index e8352771c..018d0dcd3 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -286,6 +286,10 @@ class SessionBase(object): rec['currently_attached'] = False rec['device'] = '' + def host_compute_free_memory(self, _1, ref): + #Always return 12GB available + return 12 * 1024 * 1024 * 1024 + def xenapi_request(self, methodname, params): if methodname.startswith('login'): self._login(methodname, params) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 3a0f0a149..c6ac969b9 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -141,11 +141,11 @@ class VMHelper(HelperBase): @classmethod def ensure_free_mem(cls, session, instance): instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] - mem = str(long(instance_type['memory_mb']) * 1024 * 1024) + mem = long(instance_type['memory_mb']) * 1024 * 1024 #get free memory from host host = session.get_xenapi_host() host_free_mem = session.get_xenapi().host.compute_free_memory(host) - if (host_free_mem < mem ): + if (host_free_mem < mem): return False return True diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 2d4e53083..e3c303d91 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -67,13 +67,13 @@ class VMOps(object): raise exception.Duplicate(_('Attempted to create' ' non-unique name %s') % instance.name) #ensure enough free memory, otherwise don't bother - if not VMHelper.ensure_free_mem(self._session,instance): + if not VMHelper.ensure_free_mem(self._session, instance): LOG.exception(_('instance %s: not enough free memory'), instance['name']) db.instance_set_state(context.get_admin_context(), instance['id'], power_state.SHUTDOWN) - return + return bridge = db.network_get_by_instance(context.get_admin_context(), instance['id'])['bridge'] network_ref = \ @@ -168,7 +168,8 @@ class VMOps(object): instance_name = instance_or_vm.name vm = VMHelper.lookup(self._session, instance_name) if vm is None: - raise Exception(_('Instance not present %s') % instance_name) + raise exception.NotFound( + _('Instance not present %s') % instance_name) return vm def snapshot(self, instance, image_id): -- cgit From fef2b4edaaf77109457dc2bf80e7f845a65a129e Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 9 Feb 2011 13:22:10 +0100 Subject: Don't convert datetime objects to a string using .isoformat(). Leave it to sqlalchmeny (or pysqlite or whatever it is that does the magic) to work it out. --- nova/db/sqlalchemy/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 85250d56e..80c844635 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -579,7 +579,7 @@ def fixed_ip_disassociate_all_by_timeout(_context, host, time): 'AND instance_id IS NOT NULL ' 'AND allocated = 0', {'host': host, - 'time': time.isoformat()}) + 'time': time}) return result.rowcount -- cgit From 96fbbcde9c84a72036a74ba07318f31a471eb402 Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Wed, 9 Feb 2011 17:24:36 +0300 Subject: Add my name to AUTHORS, remove parentheses from the substitution made in the previous commit --- Authors | 1 + nova/compute/instance_types.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Authors b/Authors index 27782738f..b359fec22 100644 --- a/Authors +++ b/Authors @@ -3,6 +3,7 @@ Anne Gentle Anthony Young Antony Messerli Armando Migliaccio +Bilal Akhtar Chiradeep Vittal Chmouel Boudjnah Chris Behrens diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 510bfd99e..a41f5fa79 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -38,7 +38,7 @@ def get_by_type(instance_type): if instance_type is None: return FLAGS.default_instance_type if instance_type not in INSTANCE_TYPES: - raise exception.ApiError(_("Unknown instance type: %s") % (instance_type), + raise exception.ApiError(_("Unknown instance type: %s") % instance_type, "Invalid") return instance_type -- cgit From 9a55136eb691de8d795ec47c5720556160899244 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 9 Feb 2011 15:39:37 +0000 Subject: Fixed pep8 error in vm_utils.py --- nova/virt/xenapi/vm_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 2d2200805..f5c19099a 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -445,7 +445,7 @@ class VMHelper(HelperBase): if 'PV_kernel' in vm_rec and 'PV_ramdisk' in vm_rec: return (vm_rec['PV_kernel'], vm_rec['PV_ramdisk']) else: - return (None, None) + return (None, None) @classmethod def compile_info(cls, record): -- cgit From 636bf106d5fc0424b9045e79ea18fb74406e070c Mon Sep 17 00:00:00 2001 From: SuperStack Date: Wed, 9 Feb 2011 09:49:59 -0800 Subject: Added my mail alias (Part of an experiment in using github, which got messy fast...) --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index d13219ab0..c6f6c9a8b 100644 --- a/.mailmap +++ b/.mailmap @@ -33,3 +33,4 @@ + -- cgit From 6e881239c9b8a1fb209868addf1a2b83042f2128 Mon Sep 17 00:00:00 2001 From: brian-lamar Date: Wed, 9 Feb 2011 13:30:40 -0500 Subject: 1) Moved tests for limiter to test_common.py (from __init__.py) and expanded test suite to include bad inputs and tests for custom limits (#2) 2) Wrapped int() calls in blocks to ensure logic regardless of input. 3) Moved 1000 hard limit hard-coding to a keyword param. 4) Added comments as I went. --- nova/api/openstack/common.py | 33 ++++--- nova/tests/api/openstack/__init__.py | 28 ------ nova/tests/api/openstack/test_common.py | 161 ++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 41 deletions(-) create mode 100644 nova/tests/api/openstack/test_common.py diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 6d2fa16e8..1dc3767e2 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -18,22 +18,29 @@ from nova import exception -def limited(items, req): - """Return a slice of items according to requested offset and limit. - - items - a sliceable - req - wobob.Request possibly containing offset and limit GET variables. - offset is where to start in the list, and limit is the maximum number - of items to return. +def limited(items, request, max_limit=1000): + """ + Return a slice of items according to requested offset and limit. - If limit is not specified, 0, or > 1000, defaults to 1000. + @param items: A sliceable entity + @param request: `webob.Request` possibly containing 'offset' and 'limit' + GET variables. 'offset' is where to start in the list, + and 'limit' is the maximum number of items to return. If + 'limit' is not specified, 0, or > max_limit, we default + to max_limit. + @kwarg max_limit: The maximum number of items to return from 'items' """ + try: + offset = int(request.GET.get('offset', 0)) + except ValueError: + offset = 0 + + try: + limit = int(request.GET.get('limit', max_limit)) + except ValueError: + limit = max_limit - offset = int(req.GET.get('offset', 0)) - limit = int(req.GET.get('limit', 0)) - if not limit: - limit = 1000 - limit = min(1000, limit) + limit = min(max_limit, limit or max_limit) range_end = offset + limit return items[offset:range_end] diff --git a/nova/tests/api/openstack/__init__.py b/nova/tests/api/openstack/__init__.py index 14eaaa62c..77b1dd37f 100644 --- a/nova/tests/api/openstack/__init__.py +++ b/nova/tests/api/openstack/__init__.py @@ -92,31 +92,3 @@ class RateLimitingMiddlewareTest(unittest.TestCase): self.assertEqual(middleware.limiter.__class__.__name__, "Limiter") middleware = RateLimitingMiddleware(simple_wsgi, service_host='foobar') self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy") - - -class LimiterTest(unittest.TestCase): - - def test_limiter(self): - items = range(2000) - req = Request.blank('/') - self.assertEqual(limited(items, req), items[:1000]) - req = Request.blank('/?offset=0') - self.assertEqual(limited(items, req), items[:1000]) - req = Request.blank('/?offset=3') - self.assertEqual(limited(items, req), items[3:1003]) - req = Request.blank('/?offset=2005') - self.assertEqual(limited(items, req), []) - req = Request.blank('/?limit=10') - self.assertEqual(limited(items, req), items[:10]) - req = Request.blank('/?limit=0') - self.assertEqual(limited(items, req), items[:1000]) - req = Request.blank('/?limit=3000') - self.assertEqual(limited(items, req), items[:1000]) - req = Request.blank('/?offset=1&limit=3') - self.assertEqual(limited(items, req), items[1:4]) - req = Request.blank('/?offset=3&limit=0') - self.assertEqual(limited(items, req), items[3:1003]) - req = Request.blank('/?offset=3&limit=1500') - self.assertEqual(limited(items, req), items[3:1003]) - req = Request.blank('/?offset=3000&limit=10') - self.assertEqual(limited(items, req), []) diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py new file mode 100644 index 000000000..9d9837cc9 --- /dev/null +++ b/nova/tests/api/openstack/test_common.py @@ -0,0 +1,161 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 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. + +""" +Test suites for 'common' code used throughout the OpenStack HTTP API. +""" + +import unittest + +from webob import Request + +from nova.api.openstack.common import limited + + +class LimiterTest(unittest.TestCase): + """ + Unit tests for the `nova.api.openstack.common.limited` method which takes + in a list of items and, depending on the 'offset' and 'limit' GET params, + returns a subset or complete set of the given items. + """ + + def setUp(self): + """ + Run before each test. + """ + self.tiny = range(1) + self.small = range(10) + self.medium = range(1000) + self.large = range(10000) + + def test_limiter_offset_zero(self): + """ + Test offset key works with 0. + """ + req = Request.blank('/?offset=0') + self.assertEqual(limited(self.tiny, req), self.tiny) + self.assertEqual(limited(self.small, req), self.small) + self.assertEqual(limited(self.medium, req), self.medium) + self.assertEqual(limited(self.large, req), self.large[:1000]) + + def test_limiter_offset_medium(self): + """ + Test offset key works with a medium sized number. + """ + req = Request.blank('/?offset=10') + self.assertEqual(limited(self.tiny, req), []) + self.assertEqual(limited(self.small, req), self.small[10:]) + self.assertEqual(limited(self.medium, req), self.medium[10:]) + self.assertEqual(limited(self.large, req), self.large[10:1010]) + + def test_limiter_offset_over_max(self): + """ + Test offset key works with a number over 1000 (max_limit). + """ + req = Request.blank('/?offset=1001') + self.assertEqual(limited(self.tiny, req), []) + self.assertEqual(limited(self.small, req), []) + self.assertEqual(limited(self.medium, req), []) + self.assertEqual(limited(self.large, req), self.large[1001:2001]) + + def test_limiter_offset_blank(self): + """ + Test offset key works with a blank offset. + """ + req = Request.blank('/?offset=') + self.assertEqual(limited(self.tiny, req), self.tiny) + self.assertEqual(limited(self.small, req), self.small) + self.assertEqual(limited(self.medium, req), self.medium) + self.assertEqual(limited(self.large, req), self.large[:1000]) + + def test_limiter_offset_bad(self): + """ + Test offset key works with a BAD offset. + """ + req = Request.blank(u'/?offset=\u0020aa') + self.assertEqual(limited(self.tiny, req), self.tiny) + self.assertEqual(limited(self.small, req), self.small) + self.assertEqual(limited(self.medium, req), self.medium) + self.assertEqual(limited(self.large, req), self.large[:1000]) + + def test_limiter_nothing(self): + """ + Test request with no offset or limit + """ + req = Request.blank('/') + self.assertEqual(limited(self.tiny, req), self.tiny) + self.assertEqual(limited(self.small, req), self.small) + self.assertEqual(limited(self.medium, req), self.medium) + self.assertEqual(limited(self.large, req), self.large[:1000]) + + def test_limiter_limit_zero(self): + """ + Test limit of zero. + """ + req = Request.blank('/?limit=0') + self.assertEqual(limited(self.tiny, req), self.tiny) + self.assertEqual(limited(self.small, req), self.small) + self.assertEqual(limited(self.medium, req), self.medium) + self.assertEqual(limited(self.large, req), self.large[:1000]) + + def test_limiter_limit_medium(self): + """ + Test limit of 10. + """ + req = Request.blank('/?limit=10') + self.assertEqual(limited(self.tiny, req), self.tiny) + self.assertEqual(limited(self.small, req), self.small) + self.assertEqual(limited(self.medium, req), self.medium[:10]) + self.assertEqual(limited(self.large, req), self.large[:10]) + + def test_limiter_limit_over_max(self): + """ + Test limit of 3000. + """ + req = Request.blank('/?limit=3000') + self.assertEqual(limited(self.tiny, req), self.tiny) + self.assertEqual(limited(self.small, req), self.small) + self.assertEqual(limited(self.medium, req), self.medium) + self.assertEqual(limited(self.large, req), self.large[:1000]) + + def test_limiter_limit_and_offset(self): + """ + Test request with both limit and offset. + """ + items = range(2000) + req = Request.blank('/?offset=1&limit=3') + self.assertEqual(limited(items, req), items[1:4]) + req = Request.blank('/?offset=3&limit=0') + self.assertEqual(limited(items, req), items[3:1003]) + req = Request.blank('/?offset=3&limit=1500') + self.assertEqual(limited(items, req), items[3:1003]) + req = Request.blank('/?offset=3000&limit=10') + self.assertEqual(limited(items, req), []) + + def test_limiter_custom_max_limit(self): + """ + Test a max_limit other than 1000. + """ + items = range(2000) + req = Request.blank('/?offset=1&limit=3') + self.assertEqual(limited(items, req, max_limit=2000), items[1:4]) + req = Request.blank('/?offset=3&limit=0') + self.assertEqual(limited(items, req, max_limit=2000), items[3:]) + req = Request.blank('/?offset=3&limit=2500') + self.assertEqual(limited(items, req, max_limit=2000), items[3:]) + req = Request.blank('/?offset=3000&limit=10') + self.assertEqual(limited(items, req, max_limit=2000), []) -- cgit From 590f5f1793c1f829101b4edbacbc79eac7acd2ef Mon Sep 17 00:00:00 2001 From: brian-lamar Date: Wed, 9 Feb 2011 13:36:16 -0500 Subject: Added myself to Authors --- Authors | 1 + 1 file changed, 1 insertion(+) diff --git a/Authors b/Authors index 27782738f..14cc95377 100644 --- a/Authors +++ b/Authors @@ -3,6 +3,7 @@ Anne Gentle Anthony Young Antony Messerli Armando Migliaccio +Brian Lamar Chiradeep Vittal Chmouel Boudjnah Chris Behrens -- cgit From d4b4fa93e7603d73eb99c8edb0e648ff047cda30 Mon Sep 17 00:00:00 2001 From: SuperStack Date: Wed, 9 Feb 2011 11:18:48 -0800 Subject: Fix PEP8 violations --- nova/utils.py | 1 + nova/volume/san.py | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/nova/utils.py b/nova/utils.py index 100e26f70..8d7ff1f64 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -187,6 +187,7 @@ def ssh_execute(ssh, cmd, process_input=None, return (stdout, stderr) + def abspath(s): return os.path.join(os.path.dirname(__file__), s) diff --git a/nova/volume/san.py b/nova/volume/san.py index 1f3417ae5..e7d559a2a 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -42,6 +42,7 @@ flags.DEFINE_string('san_password', '', flags.DEFINE_string('san_privatekey', '', 'Filename of private key to use for SSH authentication') + class SanISCSIDriver(ISCSIDriver): """ Base class for SAN-style storage volumes (storage providers we access over SSH)""" @@ -68,7 +69,8 @@ class SanISCSIDriver(ISCSIDriver): volume_name) iscsi_portal = location.split(",")[0] - LOG.debug("iscsi_name=%s, iscsi_portal=%s" % (iscsi_name, iscsi_portal)) + LOG.debug("iscsi_name=%s, iscsi_portal=%s" % + (iscsi_name, iscsi_portal)) return (iscsi_name, iscsi_portal) def _build_iscsi_target_name(self, volume): @@ -89,7 +91,9 @@ class SanISCSIDriver(ISCSIDriver): privatekeyfile = os.path.expanduser(FLAGS.san_privatekey) # It sucks that paramiko doesn't support DSA keys privatekey = paramiko.RSAKey.from_private_key_file(privatekeyfile) - ssh.connect(FLAGS.san_ip, username=FLAGS.san_login, pkey=privatekey) + ssh.connect(FLAGS.san_ip, + username=FLAGS.san_login, + pkey=privatekey) else: raise exception.Error("Specify san_password or san_privatekey") return ssh @@ -125,6 +129,7 @@ class SanISCSIDriver(ISCSIDriver): if not (FLAGS.san_ip): raise exception.Error("san_ip must be set") + def _collect_lines(data): """ Split lines from data into an array, trimming them """ matches = [] @@ -134,6 +139,7 @@ def _collect_lines(data): return matches + def _get_prefixed_values(data, prefix): """Collect lines which start with prefix; with trimming""" matches = [] @@ -146,6 +152,7 @@ def _get_prefixed_values(data, prefix): return matches + class SolarisISCSIDriver(SanISCSIDriver): """Executes commands relating to Solaris-hosted ISCSI volumes. Basic setup for a Solaris iSCSI server: @@ -165,8 +172,8 @@ class SolarisISCSIDriver(SanISCSIDriver): """ def _view_exists(self, luid): - (out, _err) = self._run_ssh("pfexec /usr/sbin/stmfadm list-view -l %s" % - (luid), + cmd = "pfexec /usr/sbin/stmfadm list-view -l %s" % (luid) + (out, _err) = self._run_ssh(cmd, check_exit_code=False) if "no views found" in out: return False @@ -229,9 +236,6 @@ class SolarisISCSIDriver(SanISCSIDriver): sizestr, zfs_poolname)) - return { # 'target_ip': FLAGS.san_ip - } - def _get_luid(self, volume): zfs_poolname = self._build_zfs_poolname(volume) @@ -329,4 +333,3 @@ class SolarisISCSIDriver(SanISCSIDriver): if self._is_lu_created(volume): self._run_ssh("pfexec /usr/sbin/sbdadm delete-lu %s" % (luid)) - -- cgit From b1fbb1a4a087722ce6dc7fd7a1b9fe01cb6ec766 Mon Sep 17 00:00:00 2001 From: SuperStack Date: Wed, 9 Feb 2011 11:25:18 -0800 Subject: Indent args to ssh_connect correctly --- nova/volume/san.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/volume/san.py b/nova/volume/san.py index e7d559a2a..26d6125e7 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -92,8 +92,8 @@ class SanISCSIDriver(ISCSIDriver): # It sucks that paramiko doesn't support DSA keys privatekey = paramiko.RSAKey.from_private_key_file(privatekeyfile) ssh.connect(FLAGS.san_ip, - username=FLAGS.san_login, - pkey=privatekey) + username=FLAGS.san_login, + pkey=privatekey) else: raise exception.Error("Specify san_password or san_privatekey") return ssh -- cgit From 99a02a7d68416c72675f7b6c554df9b682771e04 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 9 Feb 2011 11:36:45 -0800 Subject: added support to pull list of ALL instance types even those that are marked deleted --- nova/compute/instance_types.py | 14 ++++++++------ nova/db/api.py | 4 ++-- nova/db/sqlalchemy/api.py | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index b97a0da25..b13ccda43 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -55,13 +55,15 @@ def destroy(name): return records -def get_all_types(): - """retrieves all instance_types""" - return db.instance_type_get_all(context.get_admin_context()) +def get_all_types(inactive=0): + """Retrieves non-deleted instance_types. + Pass true as argument if you want deleted instance types returned also.""" + return db.instance_type_get_all(context.get_admin_context(), inactive) def get_all_flavors(): - """retrieves all flavors. alias for instance_types.get_all_types()""" + """retrieves non-deleted flavors. alias for instance_types.get_all_types(). + Pass true as argument if you want deleted instance types returned also.""" return get_all_types(context.get_admin_context()) @@ -79,7 +81,7 @@ def get_instance_type(name): def get_by_type(instance_type): - """retrieve instance_type details""" + """retrieve instance type name""" if instance_type is None: return FLAGS.default_instance_type try: @@ -92,7 +94,7 @@ def get_by_type(instance_type): def get_by_flavor_id(flavor_id): - """retrieve instance_type's name by flavor_id""" + """retrieve instance type's name by flavor_id""" if flavor_id is None: return FLAGS.default_instance_type try: diff --git a/nova/db/api.py b/nova/db/api.py index 2f1903ade..69f577764 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -995,9 +995,9 @@ def instance_type_create(context, values): return IMPL.instance_type_create(context, values) -def instance_type_get_all(context): +def instance_type_get_all(context, inactive=0): """Get all instance types""" - return IMPL.instance_type_get_all(context) + return IMPL.instance_type_get_all(context, inactive) def instance_type_get_by_name(context, name): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index e5afb8eac..1e13e4d2d 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2035,16 +2035,16 @@ def instance_type_create(context, values): @require_context -def instance_type_get_all(context): +def instance_type_get_all(context, inactive=0): """ - Returns a dict describing all instance_types with name as key: + Returns a dict describing all non-deleted instance_types with name as key: {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3)} """ session = get_session() inst_types = session.query(models.InstanceTypes).\ - filter_by(deleted=0).\ + filter_by(deleted=inactive).\ order_by("name").\ all() if inst_types: -- cgit From 52e1ad5321590b7b4671349373217bc8fce275fc Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 9 Feb 2011 15:55:29 -0500 Subject: - population of public and private addresses containers in openstack api - replacement of sqlalchemy model in instance stub with dict --- nova/api/openstack/servers.py | 18 +++++++++ nova/tests/api/openstack/test_servers.py | 66 ++++++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 17c5519a1..60f3d96e3 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -64,6 +64,24 @@ def _translate_detail_keys(inst): inst_dict['status'] = power_mapping[inst_dict['status']] inst_dict['addresses'] = dict(public=[], private=[]) + + # grab single private fixed ip + try: + private_ip = inst['fixed_ip']['address'] + if private_ip: + inst_dict['addresses']['private'].append(private_ip) + except KeyError: + LOG.debug(_("Failed to read private ip")) + pass + + # grab all public floating ips + try: + [inst_dict['addresses']['public'].append(floating['address']) \ + for floating in inst['fixed_ip']['floating_ips']] + except KeyError: + LOG.debug(_("Failed to read public ip(s)")) + pass + inst_dict['metadata'] = {} inst_dict['hostId'] = '' diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 724f14f19..816a0ab8c 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -17,6 +17,7 @@ import json import unittest +import datetime import stubout import webob @@ -39,6 +40,13 @@ def return_server(context, id): return stub_instance(id) +def return_server_with_addresses(private, public): + def _return_server(context, id): + return stub_instance(id, private_address=private, + public_addresses=public) + return _return_server + + def return_servers(context, user_id=1): return [stub_instance(i, user_id) for i in xrange(5)] @@ -55,9 +63,45 @@ def instance_address(context, instance_id): return None -def stub_instance(id, user_id=1): - return Instance(id=id, state=0, image_id=10, user_id=user_id, - display_name='server%s' % id) +def stub_instance(id, user_id=1, private_address=None, public_addresses=None): + if public_addresses == None: + public_addresses = list() + + instance = { + "id": id, + "admin_pass": "", + "user_id": user_id, + "project_id": "", + "image_id": 10, + "kernel_id": "", + "ramdisk_id": "", + "launch_index": 0, + "key_name": "", + "key_data": "", + "state": 0, + "state_description": "", + "memory_mb": 0, + "vcpus": 0, + "local_gb": 0, + "hostname": "", + "host": "", + "instance_type": "", + "user_data": "", + "reservation_id": "", + "mac_address": "", + "scheduled_at": datetime.datetime.now(), + "launched_at": datetime.datetime.now(), + "terminated_at": datetime.datetime.now(), + "availability_zone": "", + "display_name": "server%s" % id, + "display_description": "", + "locked": False} + + instance["fixed_ip"] = { + "address": private_address, + "floating_ips": [{"address":ip} for ip in public_addresses]} + + return instance def fake_compute_api(cls, req, id): @@ -105,6 +149,22 @@ class ServersTest(unittest.TestCase): self.assertEqual(res_dict['server']['id'], '1') self.assertEqual(res_dict['server']['name'], 'server1') + def test_get_server_by_id_with_addresses(self): + private = "192.168.0.3" + public = ["1.2.3.4"] + new_return_server = return_server_with_addresses(private, public) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + req = webob.Request.blank('/v1.0/servers/1') + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + self.assertEqual(res_dict['server']['id'], '1') + self.assertEqual(res_dict['server']['name'], 'server1') + addresses = res_dict['server']['addresses'] + self.assertEqual(len(addresses["public"]), len(public)) + self.assertEqual(addresses["public"][0], public[0]) + self.assertEqual(len(addresses["private"]), 1) + self.assertEqual(addresses["private"][0], private) + def test_get_server_list(self): req = webob.Request.blank('/v1.0/servers') res = req.get_response(fakes.wsgi_app()) -- cgit From ce5e3bdd30712aa6704926e6cdeb5ae73ae8200b Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 9 Feb 2011 15:26:37 -0600 Subject: A lot of stuff --- nova/compute/manager.py | 8 ++-- nova/db/sqlalchemy/models.py | 1 + nova/virt/xenapi/vmops.py | 53 +++++++++++++++++----- nova/virt/xenapi_conn.py | 8 +++- .../xenapi/etc/xapi.d/plugins/data_transfer | 37 +++++++-------- 5 files changed, 71 insertions(+), 36 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 4189c49a4..ac09f7c8c 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -423,12 +423,12 @@ class ComputeManager(manager.Manager): migration_ref = self.db.migration_get(context, migration_id) self.db.migration_update(context, migration_id, { 'status': 'migrating', }) - self.driver.transfer_disk(context, instance_id, + + self.driver.migrate_disk_and_power_off(context, instance, migration_ref['dest_host']) + self.db.migration_update(context, migration_id, { 'status': 'post-migrating', }) - - self.driver.power_off(context, migration_ref['instance_id']) # This is where we would update the VM record after resizing service = self.db.service_get_by_host_and_topic(context, @@ -449,7 +449,7 @@ class ComputeManager(manager.Manager): migration_ref['instance_id']) # this may get passed into the following spawn instead - self.driver.attach_disk(context, migration_ref['instance_id']) + self.driver.attach_disk(context, instance_ref) self.driver.spawn(context, instance_ref, preexisting=True) self.db.migration_update(context, migration_id, diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 499275504..ebf3a382b 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -369,6 +369,7 @@ class KeyPair(BASE, NovaBase): class Migration(BASE, NovaBase): """Represents a running host-to-host migration.""" __tablename__ = 'migrations' + id = Column(Integer, primary_key=True, nullable=False) source_host = Column(String(255)) dest_host = Column(String(255)) instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6a7621502..40b075b3d 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -196,6 +196,26 @@ class VMOps(object): Glance. """ + with self._get_snapshot(instance) as snapshot: + # call plugin to ship snapshot off to glance + VMHelper.upload_image( + self._session, instance.id, snapshot.vdi_uuids, image_id) + + logging.debug(_("Finished snapshot and upload for VM %s"), instance) + + def _get_snapshot(self, instance): + class Snapshot(object): + def __init__(self, virt, instance, vdis): + self.instance = instance + self.vdi_uuids = vdis + self.virt = virt + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.virt._destroy(self.instance, self.vm_ref, shutdown=False) + #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added @@ -204,30 +224,41 @@ class VMOps(object): label = "%s-snapshot" % instance.name try: - template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( + _, template_vdi_uuids = VMHelper.create_snapshot( self._session, instance.id, vm_ref, label) + return Snapshot(self, instance, template_vdi_uuids) except self.XenAPI.Failure, exc: logging.error(_("Unable to Snapshot %(vm_ref)s: %(exc)s") % locals()) return - try: - # call plugin to ship snapshot off to glance - VMHelper.upload_image( - self._session, instance.id, template_vdi_uuids, image_id) - finally: - self._destroy(instance, template_vm_ref, shutdown=False) - - logging.debug(_("Finished snapshot and upload for VM %s"), instance) - - def transfer_disk(self, instance, dest): + def migrate_disk_and_power_off(self, instance, dest): """ Copies a VHD from one host machine to another :param instance: the instance that owns the VHD in question :param dest: the destination host machine + :param disk_type: values are 'primary' or 'cow' """ vm_ref = VMHelper.lookup(self._session, instance.name) + # The primary VDI becomes the COW after the snapshot. We can figure + # this out from the VBD. The base copy is the parent_uuid returned + # from the snapshot creation + with self._get_snapshot(instance) as snapshot: + params = {'host':dest, 'vdi_uuid':snapshot.vdi_uuids[1]} + kwargs = {'params': pickle.dumps(params)} + self._session.async_call_plugin('data_transfer', 'transfer_vhd', + kwargs) + + # Now power down the instance and transfer the COW VHD + self._shutdown(instance, method='clean') + + _, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid']} + kwargs = {'params': pickle.dumps(params)} + self._session.async_call_plugin('data_transfer', 'transfer_vhd', + kwargs) + def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ raise NotImplementedError() diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 2e587117a..98b5e7851 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -192,8 +192,14 @@ class XenAPIConnection(object): """powers on a powered off VM instance""" self._vmops.power_on(instance) - def transfer_disk(self, instance, dest, callback): + 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""" self._vmops.transfer_disk(instance, dest) + + def move_disk(self, instance_ref): + """Moves the copied VDIs into the SR""" + pass def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer index 2af4a758b..bd46e1c0b 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer @@ -21,6 +21,7 @@ XenAPI Plugin for transfering data between host nodes """ import os.path +import pickle import subprocess import XenAPIPlugin @@ -30,27 +31,23 @@ DEVNULL = '/dev/null' KEYSCAN = '/usr/bin/ssh-keyscan' RSYNC = '/usr/bin/rsync' -def _key_scan_and_add(host): - """SSH scans a remote host and writes the SSH key out to known_hosts""" - # Touch the file if it doesn't yet exist - open(SSH_HOSTS, 'a').close() - - null = open(DEVNULL, 'w') - known_hosts = open(SSH_HOSTS, 'a') - key = subprocess.Popen(['/usr/bin/ssh-keyscan', '-t', 'rsa', host], - stdout=subprocess.PIPE, stderr=null).communicate()[0].strip() - grep = subprocess.call(['/bin/grep', '-o', '%s' % key, SSH_HOSTS], - stdout=null, stderr=null) - if grep == 1: - known_hosts.write(key) - null.close() - known_hosts.close() - -def transfer_file(host, file_path): + +def transfer_vhd(session, args): """Rsyncs a VHD to an adjacent host""" - _key_scan_and_add(host) - if subprocess.call([RSYNC, file_path, "%s:/root/" % host]) != 0: + params = pickle.dumps(args) + instance_id = params['instance_id'] + host = params['host'] + vdi_uuid = params['vdi_uuid'] + sr_path = get_sr_path(session) + vhd_path = "%s.vhd" % vdi_uuid + + source_path = "%s/%s" % (sr_path, vhd_path) + dest_path = '%s:/images/instance%d/' % (host, instance_id) + rsync_args = [['nohup', RSYNC, '-av', '--progress', + '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] + + if subprocess.call(rsync_args) != 0: raise Exception("Unexpected VHD transfer failure") if __name__ == '__main__': - XenAPIPlugin.dispatch({'transfer_file': transfer_file}) + XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) -- cgit From 482c7b57a3d0ac8bf6df98539bf8a1220470e0f7 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 9 Feb 2011 15:27:23 -0600 Subject: Renamed migration plugin --- .../xenapi/etc/xapi.d/plugins/data_transfer | 53 ---------------------- .../xenserver/xenapi/etc/xapi.d/plugins/migration | 53 ++++++++++++++++++++++ 2 files changed, 53 insertions(+), 53 deletions(-) delete mode 100644 plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer create mode 100644 plugins/xenserver/xenapi/etc/xapi.d/plugins/migration diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer deleted file mode 100644 index bd46e1c0b..000000000 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python - -# 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. - -""" -XenAPI Plugin for transfering data between host nodes -""" - -import os.path -import pickle -import subprocess - -import XenAPIPlugin - -SSH_HOSTS = '/root/.ssh/known_hosts' -DEVNULL = '/dev/null' -KEYSCAN = '/usr/bin/ssh-keyscan' -RSYNC = '/usr/bin/rsync' - - -def transfer_vhd(session, args): - """Rsyncs a VHD to an adjacent host""" - params = pickle.dumps(args) - instance_id = params['instance_id'] - host = params['host'] - vdi_uuid = params['vdi_uuid'] - sr_path = get_sr_path(session) - vhd_path = "%s.vhd" % vdi_uuid - - source_path = "%s/%s" % (sr_path, vhd_path) - dest_path = '%s:/images/instance%d/' % (host, instance_id) - rsync_args = [['nohup', RSYNC, '-av', '--progress', - '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] - - if subprocess.call(rsync_args) != 0: - raise Exception("Unexpected VHD transfer failure") - -if __name__ == '__main__': - XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration new file mode 100644 index 000000000..bd46e1c0b --- /dev/null +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# 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. + +""" +XenAPI Plugin for transfering data between host nodes +""" + +import os.path +import pickle +import subprocess + +import XenAPIPlugin + +SSH_HOSTS = '/root/.ssh/known_hosts' +DEVNULL = '/dev/null' +KEYSCAN = '/usr/bin/ssh-keyscan' +RSYNC = '/usr/bin/rsync' + + +def transfer_vhd(session, args): + """Rsyncs a VHD to an adjacent host""" + params = pickle.dumps(args) + instance_id = params['instance_id'] + host = params['host'] + vdi_uuid = params['vdi_uuid'] + sr_path = get_sr_path(session) + vhd_path = "%s.vhd" % vdi_uuid + + source_path = "%s/%s" % (sr_path, vhd_path) + dest_path = '%s:/images/instance%d/' % (host, instance_id) + rsync_args = [['nohup', RSYNC, '-av', '--progress', + '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] + + if subprocess.call(rsync_args) != 0: + raise Exception("Unexpected VHD transfer failure") + +if __name__ == '__main__': + XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) -- cgit From cf2db4f18dbff14fb8882a4747c607ff26b1de55 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 9 Feb 2011 13:58:43 -0800 Subject: fixed overlooked mandatory changes in Xen --- nova/virt/libvirt_conn.py | 5 +++-- nova/virt/xenapi/vm_utils.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 94fe93c40..14cc4cf39 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -612,7 +612,7 @@ class LibvirtConnection(object): user=user, project=project, size=size) - type_data = test.INSTANCE_TYPES[inst['instance_type']] + type_data = instance_types.get_instance_type([inst['instance_type']]) if type_data['local_gb']: self._cache_image(fn=self._create_local, @@ -673,7 +673,8 @@ class LibvirtConnection(object): instance['id']) # FIXME(vish): stick this in db instance_type = instance['instance_type'] - instance_type = test.INSTANCE_TYPES[instance_type] + # instance_type = test.INSTANCE_TYPES[instance_type] + instance_type = instance_types.get_instance_type(instance_type) ip_address = db.instance_get_fixed_address(context.get_admin_context(), instance['id']) # Assume that the gateway also acts as the dhcp server. diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 38a6f73ce..a9308eea1 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -31,7 +31,6 @@ import glance.client from nova import exception from nova import flags from nova import log as logging -from nova import test from nova import utils from nova.auth.manager import AuthManager from nova.compute import instance_types @@ -83,7 +82,8 @@ class VMHelper(HelperBase): the pv_kernel flag indicates whether the guest is HVM or PV """ - instance_type = test.INSTANCE_TYPES[instance.instance_type] + instance_type = instance_types.\ + get_instance_type(instance.instance_type) mem = str(long(instance_type['memory_mb']) * 1024 * 1024) vcpus = str(instance_type['vcpus']) rec = { -- cgit From ac33f61c5c382fc7c8e8ab872192858860672d70 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 9 Feb 2011 16:38:27 -0600 Subject: Plugin tidying and more migration implementation --- nova/virt/xenapi/vm_utils.py | 16 +++--- nova/virt/xenapi/vmops.py | 35 +++++++---- nova/virt/xenapi_conn.py | 9 ++- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 4 +- .../xenserver/xenapi/etc/xapi.d/plugins/migration | 67 +++++++++++++++++++++- 5 files changed, 108 insertions(+), 23 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4bbd522c1..e16662aad 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -478,6 +478,14 @@ class VMHelper(HelperBase): except cls.XenAPI.Failure as e: return {"Unable to retrieve diagnostics": e} + @classmethod + def scan_sr(cls, session, instance_id, sr_ref): + LOG.debug(_("Re-scanning SR %s"), sr_ref) + task = session.call_xenapi('Async.SR.scan', sr_ref) + session.wait_for_task(instance_id, task) + + + def get_rrd(host, uuid): """Return the VM RRD XML as a string""" @@ -520,12 +528,6 @@ def get_vhd_parent_uuid(session, vdi_ref): return None -def scan_sr(session, instance_id, sr_ref): - LOG.debug(_("Re-scanning SR %s"), sr_ref) - task = session.call_xenapi('Async.SR.scan', sr_ref) - session.wait_for_task(instance_id, task) - - def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, original_parent_uuid): """ Spin until the parent VHD is coalesced into its parent VHD @@ -550,7 +552,7 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, " %(max_attempts)d), giving up...") % locals()) raise exception.Error(msg) - scan_sr(session, instance_id, sr_ref) + VMHelper.scan_sr(session, instance_id, sr_ref) parent_uuid = get_vhd_parent_uuid(session, vdi_ref) if original_parent_uuid and (parent_uuid != original_parent_uuid): LOG.debug(_("Parent %(parent_uuid)s doesn't match original parent" diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 40b075b3d..ea4b7899b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -146,7 +146,7 @@ class VMOps(object): """ vm = None try: - if instance_or_vm.startswith("OpaqueRef:"): + if instance_or_vm.startswith("OpaqueRef:") # Got passed an opaque ref; return it return instance_or_vm else: @@ -241,23 +241,38 @@ class VMOps(object): """ vm_ref = VMHelper.lookup(self._session, instance.name) - # The primary VDI becomes the COW after the snapshot. We can figure - # this out from the VBD. The base copy is the parent_uuid returned + # The primary VDI becomes the COW after the snapshot, and we can + # identify it via the VBD. The base copy is the parent_uuid returned # from the snapshot creation + + #TODO(mdietz): explicitly forcing the base_copy and cow names is + #pretty fugly with self._get_snapshot(instance) as snapshot: - params = {'host':dest, 'vdi_uuid':snapshot.vdi_uuids[1]} - kwargs = {'params': pickle.dumps(params)} - self._session.async_call_plugin('data_transfer', 'transfer_vhd', - kwargs) + params = {'host':dest, 'vdi_uuid':snapshot.vdi_uuids[1], + 'dest_name':'base_copy.vhd'} + self._session.async_call_plugin('migration', 'transfer_vhd', + {'params': pickle.dumps(params)}) # Now power down the instance and transfer the COW VHD self._shutdown(instance, method='clean') _, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) - params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid']} - kwargs = {'params': pickle.dumps(params)} + params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], + 'dest_name': 'cow.vhd'} self._session.async_call_plugin('data_transfer', 'transfer_vhd', - kwargs) + {'params': pickle.dumps(params)}) + return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] + + def attach_disk(self, instance): + vm_ref = VMHelper.lookup(self._session, instance.name) + + params = { 'instance_id': instance.id } + self._session.async_call_plugin('migration', 'move_vhds_into_sr', + {'params': pickle.dumps(params)}) + + + _, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + VMHelper.scan_sr(self._session, instance.id, vm_vdi_rec['SR']) def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 98b5e7851..cc43050b4 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -164,6 +164,9 @@ class XenAPIConnection(object): """Resize a VM instance""" raise NotImplementedError() + def attach_disk(self, instance_ref): + + def reboot(self, instance): """Reboot VM instance""" self._vmops.reboot(instance) @@ -195,11 +198,11 @@ class XenAPIConnection(object): 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""" - self._vmops.transfer_disk(instance, dest) + self._vmops.migrate_disk_and_power_off(instance, dest) - def move_disk(self, instance_ref): + def attach_disk(self, instance): """Moves the copied VDIs into the SR""" - pass + self._vmops.attach_disk(instance) def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index aadacce57..817269769 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -138,8 +138,8 @@ def get_sr_path(session): return sr_path -#TODO(sirp): both objectstore and glance need this, should this be refactored -#into common lib +#TODO(sirp): objectstore, migration and glance need this, should this be +# refactored into common lib def find_sr(session): host = get_this_host(session) srs = session.xenapi.SR.get_all() diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index bd46e1c0b..e81b18a5e 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -20,16 +20,79 @@ XenAPI Plugin for transfering data between host nodes """ +import os import os.path import pickle +import shutil import subprocess +import uuid import XenAPIPlugin +from pluginlib_nova import * + SSH_HOSTS = '/root/.ssh/known_hosts' DEVNULL = '/dev/null' KEYSCAN = '/usr/bin/ssh-keyscan' RSYNC = '/usr/bin/rsync' +FILE_SR_PATH = '/var/run/sr-mount' +IMAGE_PATH = '/images/' +VHD_UTIL = '/usr/sbin/vhd-util' + +def get_sr_path(session): + sr_ref = find_sr(session) + + if sr_ref is None: + raise Exception('Cannot find SR to read VDI from') + + sr_rec = session.xenapi.SR.get_record(sr_ref) + sr_uuid = sr_rec["uuid"] + sr_path = os.path.join(FILE_SR_PATH, sr_uuid) + return sr_path + +def find_sr(session): + host = get_this_host(session) + srs = session.xenapi.SR.get_all() + for sr in srs: + sr_rec = session.xenapi.SR.get_record(sr) + if not ('i18n-key' in sr_rec['other_config'] and + sr_rec['other_config']['i18n-key'] == 'local-storage'): + continue + for pbd in sr_rec['PBDs']: + pbd_rec = session.xenapi.PBD.get_record(pbd) + if pbd_rec['host'] == host: + return sr + return None + +def move_vhds_into_sr(session, args): + """Moves the VHDs from their copied location to the SR""" + params = pickle.dumps(args) + instance_id = params['instance_id'] + + sr_path = get_sr_path(session) + + # Discover the copied VHDs locally, and then set up paths to copy + # them to under the SR + source_image_path = "%s/instance%d" % (IMAGE_PATH, instance_id) + source_base_copy_path = "%s/base_copy.vhd" % source_image_path + source_cow_path = "%s/cow.vhd" % source_image_path + + temp_vhd_path = "%s/instance%d/" % (sr_path, instance_id) + new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) + new_cow_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) + + os.mkdir(temp_vhd_path) + shutil.move(source_base_copy_path, new_base_copy_path) + shutil.move(source_cow_path, new_cow_path) + + os.rmdir(source_image_path) + + # Link the COW to the base copy + subprocess.call([VHD_UTIL, 'modify', '-n', new_cow_path, '-p', + new_base_copy_path]) + + shutil.move("%s/*.vhd" % temp_vhd_path, sr_path) + os.rmdir(temp_vhd_path) def transfer_vhd(session, args): @@ -38,11 +101,13 @@ def transfer_vhd(session, args): instance_id = params['instance_id'] host = params['host'] vdi_uuid = params['vdi_uuid'] + dest_name = params['dest_name'] sr_path = get_sr_path(session) vhd_path = "%s.vhd" % vdi_uuid source_path = "%s/%s" % (sr_path, vhd_path) - dest_path = '%s:/images/instance%d/' % (host, instance_id) + dest_path = '%s:%sinstance%d/%s' % (host, IMAGE_PATH, instance_id, + dest_name) rsync_args = [['nohup', RSYNC, '-av', '--progress', '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] -- cgit From 1a9225d945bdc9b94473c1dd4ad5b9e4b7624571 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 9 Feb 2011 15:48:31 -0800 Subject: forgot to register new instance_types table --- nova/db/sqlalchemy/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 44583861b..3f418392c 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -550,7 +550,7 @@ def register_models(): connection is lost and needs to be reestablished. """ from sqlalchemy import create_engine - models = (Service, Instance, InstanceActions, + models = (Service, Instance, InstanceActions, InstanceTypes, Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, -- cgit From 4a4a3f04b78ba2cbaa0d02ecf0f7cd3cf580901b Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 9 Feb 2011 21:54:52 -0500 Subject: adding myself to Authors file --- Authors | 1 + 1 file changed, 1 insertion(+) diff --git a/Authors b/Authors index 27782738f..563ddf759 100644 --- a/Authors +++ b/Authors @@ -3,6 +3,7 @@ Anne Gentle Anthony Young Antony Messerli Armando Migliaccio +Brian Waldon Chiradeep Vittal Chmouel Boudjnah Chris Behrens -- cgit From 2e7fd058bf68e3d4c7699a29645423b4f30af812 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Thu, 10 Feb 2011 05:13:45 +0000 Subject: Launchpad automatic translations update. --- locale/de.po | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/locale/de.po b/locale/de.po index e96292597..3b30c2fa9 100644 --- a/locale/de.po +++ b/locale/de.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-01-10 11:25-0800\n" -"PO-Revision-Date: 2011-02-08 13:06+0000\n" +"PO-Revision-Date: 2011-02-09 10:49+0000\n" "Last-Translator: Christian Berendt \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-09 05:41+0000\n" +"X-Launchpad-Export-Date: 2011-02-10 05:13+0000\n" "X-Generator: Launchpad (build 12177)\n" #: nova/crypto.py:46 @@ -146,12 +146,12 @@ msgstr "" #: nova/rpc.py:183 #, python-format msgid "no method for message: %s" -msgstr "" +msgstr "keine Methode für diese Nachricht gefunden: %s" #: nova/rpc.py:184 #, python-format msgid "No method for message: %s" -msgstr "" +msgstr "keine Methode für diese Nachricht gefunden: %s" #: nova/rpc.py:245 #, python-format @@ -165,12 +165,12 @@ msgstr "" #: nova/rpc.py:305 msgid "Making asynchronous call..." -msgstr "" +msgstr "führe asynchronen Aufruf durch..." #: nova/rpc.py:308 #, python-format msgid "MSG_ID is %s" -msgstr "" +msgstr "MSG_ID ist %s" #: nova/rpc.py:356 #, python-format @@ -180,12 +180,12 @@ msgstr "" #: nova/rpc.py:365 #, python-format msgid "topic is %s" -msgstr "" +msgstr "Betreff ist %s" #: nova/rpc.py:366 #, python-format msgid "message %s" -msgstr "" +msgstr "Nachricht %s" #: nova/service.py:157 #, python-format @@ -212,6 +212,7 @@ msgstr "" #, python-format msgid "Data store %s is unreachable. Trying again in %d seconds." msgstr "" +"Datastore %s ist nicht erreichbar. Versuche es erneut in %d Sekunden." #: nova/service.py:232 nova/twistd.py:232 #, python-format @@ -220,17 +221,17 @@ msgstr "" #: nova/service.py:234 nova/twistd.py:264 msgid "Full set of FLAGS:" -msgstr "" +msgstr "Alle vorhandenen FLAGS:" #: nova/twistd.py:211 #, python-format msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "" +msgstr "PID-Datei %s existiert nicht. Läuft der Daemon nicht?\n" #: nova/twistd.py:268 #, python-format msgid "Starting %s" -msgstr "" +msgstr "%s wird gestartet" #: nova/utils.py:53 #, python-format @@ -240,7 +241,7 @@ msgstr "" #: nova/utils.py:54 #, python-format msgid "Class %s cannot be found" -msgstr "" +msgstr "Klasse %s konnte nicht gefunden werden" #: nova/utils.py:113 #, python-format @@ -250,12 +251,12 @@ msgstr "" #: nova/utils.py:125 #, python-format msgid "Running cmd (subprocess): %s" -msgstr "" +msgstr "Führe Kommando (subprocess) aus: %s" #: nova/utils.py:138 #, python-format msgid "Result was %s" -msgstr "" +msgstr "Ergebnis war %s" #: nova/utils.py:171 #, python-format @@ -2095,22 +2096,22 @@ msgstr "" #: nova/volume/manager.py:93 #, python-format msgid "volume %s: creating" -msgstr "" +msgstr "Volume %s: wird erstellt" #: nova/volume/manager.py:102 #, python-format msgid "volume %s: creating lv of size %sG" -msgstr "" +msgstr "Volume %s: erstelle LV mit %sG" #: nova/volume/manager.py:106 #, python-format msgid "volume %s: creating export" -msgstr "" +msgstr "Volume %s: erstelle Export" #: nova/volume/manager.py:113 #, python-format msgid "volume %s: created successfully" -msgstr "" +msgstr "Volume %s: erfolgreich erstellt" #: nova/volume/manager.py:121 msgid "Volume is still attached" @@ -2123,14 +2124,14 @@ msgstr "" #: nova/volume/manager.py:124 #, python-format msgid "volume %s: removing export" -msgstr "" +msgstr "Volume %s: entferne Export" #: nova/volume/manager.py:126 #, python-format msgid "volume %s: deleting" -msgstr "" +msgstr "Volume %s: wird entfernt" #: nova/volume/manager.py:129 #, python-format msgid "volume %s: deleted successfully" -msgstr "" +msgstr "Volume %s: erfolgreich entfernt" -- cgit From 216822bdda290f964020134599749ebbadc76566 Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Thu, 10 Feb 2011 13:08:25 +0300 Subject: Wrap line to under 79 characters --- nova/compute/instance_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index a41f5fa79..309313fd0 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -38,8 +38,8 @@ def get_by_type(instance_type): if instance_type is None: return FLAGS.default_instance_type if instance_type not in INSTANCE_TYPES: - raise exception.ApiError(_("Unknown instance type: %s") % instance_type, - "Invalid") + raise exception.ApiError(_("Unknown instance type: %s") % \ + instance_type, "Invalid") return instance_type -- cgit From 1280c9e97b66b3914c3b7426ef6aeca57e6ff9e4 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Thu, 10 Feb 2011 12:49:52 +0100 Subject: Try setting isolation_level=immediate --- nova/db/sqlalchemy/session.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/db/sqlalchemy/session.py b/nova/db/sqlalchemy/session.py index dc885f138..2d90dc20f 100644 --- a/nova/db/sqlalchemy/session.py +++ b/nova/db/sqlalchemy/session.py @@ -39,6 +39,7 @@ def get_session(autocommit=True, expire_on_commit=False): if not _ENGINE: _ENGINE = create_engine(FLAGS.sql_connection, pool_recycle=FLAGS.sql_idle_timeout, + isolation_level="immediate", echo=False) _MAKER = (sessionmaker(bind=_ENGINE, autocommit=autocommit, -- cgit From 3e13d005c776b99604d1b8714a79709da1e76467 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Thu, 10 Feb 2011 13:23:06 +0100 Subject: Try using NullPool instead of SingletonPool --- nova/db/sqlalchemy/session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/session.py b/nova/db/sqlalchemy/session.py index 2d90dc20f..5dd72dc08 100644 --- a/nova/db/sqlalchemy/session.py +++ b/nova/db/sqlalchemy/session.py @@ -20,6 +20,7 @@ Session Handling for SQLAlchemy backend """ from sqlalchemy import create_engine +from sqlalchemy import pool from sqlalchemy.orm import sessionmaker from nova import exception @@ -39,7 +40,7 @@ def get_session(autocommit=True, expire_on_commit=False): if not _ENGINE: _ENGINE = create_engine(FLAGS.sql_connection, pool_recycle=FLAGS.sql_idle_timeout, - isolation_level="immediate", + poolclass=pool.NullPool, echo=False) _MAKER = (sessionmaker(bind=_ENGINE, autocommit=autocommit, -- cgit From 16ffc15b1fb45a09de14cece6b382357a030b9dc Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 10 Feb 2011 08:43:46 -0400 Subject: removed ZoneCommands from nova-manage --- bin/nova-manage | 10 ---------- nova/api/openstack/__init__.py | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index b62687aec..7835ca551 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -569,15 +569,6 @@ class DbCommands(object): print migration.db_version() -class ZoneCommands(object): - """Methods for defining zones.""" - - def create(self, name): - """Create a new Zone for this deployment.""" - ctxt = context.get_admin_context() - db.create_zone(ctxt, name) - - class VolumeCommands(object): """Methods for dealing with a cloud in an odd state""" @@ -629,7 +620,6 @@ CATEGORIES = [ ('service', ServiceCommands), ('log', LogCommands), ('db', DbCommands), - ('zone', ZoneCommands), ('volume', VolumeCommands)] diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 8901a8987..69a4d66c0 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -80,7 +80,7 @@ class APIRouter(wsgi.Router): server_members["actions"] = "GET" server_members['suspend'] = 'POST' server_members['resume'] = 'POST' - + mapper.resource("zone", "zones", controller=zones.Controller(), collection={'detail': 'GET'}, collection_name='zones') -- cgit From aff63810b287fdbd0eb6b2e8881dcf6683ed7d91 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Thu, 10 Feb 2011 14:31:51 +0100 Subject: Also add floating ip forwarding to OUTPUT chain. --- nova/network/linux_net.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index df54606db..ed37e8ba7 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -156,6 +156,8 @@ def ensure_floating_forward(floating_ip, fixed_ip): """Ensure floating ip forwarding rule""" _confirm_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s" % (floating_ip, fixed_ip)) + _confirm_rule("OUTPUT", "-t nat -d %s -j DNAT --to %s" + % (floating_ip, fixed_ip)) _confirm_rule("SNATTING", "-t nat -s %s -j SNAT --to %s" % (fixed_ip, floating_ip)) @@ -164,6 +166,8 @@ def remove_floating_forward(floating_ip, fixed_ip): """Remove forwarding for floating ip""" _remove_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s" % (floating_ip, fixed_ip)) + _remove_rule("OUTPUT", "-t nat -d %s -j DNAT --to %s" + % (floating_ip, fixed_ip)) _remove_rule("SNATTING", "-t nat -s %s -j SNAT --to %s" % (fixed_ip, floating_ip)) -- cgit From d65d2e2d34bffa2548dabcde2e230da185125026 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Thu, 10 Feb 2011 14:37:13 +0100 Subject: Only use NullPool when using sqlite. --- nova/db/sqlalchemy/session.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/nova/db/sqlalchemy/session.py b/nova/db/sqlalchemy/session.py index 5dd72dc08..4a9a28f43 100644 --- a/nova/db/sqlalchemy/session.py +++ b/nova/db/sqlalchemy/session.py @@ -38,10 +38,14 @@ def get_session(autocommit=True, expire_on_commit=False): global _MAKER if not _MAKER: if not _ENGINE: + kwargs = {'pool_recycle': FLAGS.sql_idle_timeout, + 'echo': False} + + if FLAGS.sql_connection.startswith('sqlite'): + kwargs['poolclass'] = pool.NullPool + _ENGINE = create_engine(FLAGS.sql_connection, - pool_recycle=FLAGS.sql_idle_timeout, - poolclass=pool.NullPool, - echo=False) + **kwargs) _MAKER = (sessionmaker(bind=_ENGINE, autocommit=autocommit, expire_on_commit=expire_on_commit)) -- cgit From 3f800a8ecde159cb34c5fe358da3aadce660a03a Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Thu, 10 Feb 2011 14:56:24 +0100 Subject: Make rpc.cast create a fresh amqp connection. Each API request has its own thread, and they don't multiplex well. --- nova/rpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/rpc.py b/nova/rpc.py index 01fc6d44b..c19ee4635 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -346,7 +346,7 @@ def cast(context, topic, msg): """Sends a message on a topic without waiting for a response""" LOG.debug(_("Making asynchronous cast...")) _pack_context(msg, context) - conn = Connection.instance() + conn = Connection.instance(True) publisher = TopicPublisher(connection=conn, topic=topic) publisher.send(msg) publisher.close() -- cgit From 493d4d73ce427a686a674e00a2090d5aaec55a46 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 10:20:33 -0800 Subject: refactored api call to use instance_types --- nova/compute/api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index fc18765f6..9c83cfd16 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -90,8 +90,9 @@ class API(base.Base): other arguments check out ok.""" # FIXME(kpepple) this needs to be factored for api.py:2065 refactor - type_data = db.instance_type_get_by_name(context,\ - instance_type) + type_data = instance_types.get_instance_type(instance_type) + # type_data = db.instance_type_get_by_name(context,\ + # instance_type) num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: pid = context.project_id -- cgit From d601471f54de5db95cf06f4a558362f90cc65c6b Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 10:28:52 -0800 Subject: typo --- nova/compute/api.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 9c83cfd16..5d6a42a6b 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -89,10 +89,7 @@ class API(base.Base): """Create the number of instances requested if quota and other arguments check out ok.""" - # FIXME(kpepple) this needs to be factored for api.py:2065 refactor type_data = instance_types.get_instance_type(instance_type) - # type_data = db.instance_type_get_by_name(context,\ - # instance_type) num_instances = quota.allowed_instances(context, max_count, type_data) if num_instances < min_count: pid = context.project_id -- cgit From 9029d89d26b9115cad282c6f3f9ee11c47a28444 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 11:19:02 -0800 Subject: flavorid needs to unique in model --- nova/db/sqlalchemy/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 3f418392c..955d373fd 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -218,7 +218,7 @@ class InstanceTypes(BASE, NovaBase): memory_mb = Column(Integer) vcpus = Column(Integer) local_gb = Column(Integer) - flavorid = Column(Integer) + flavorid = Column(Integer, unique=True) class Volume(BASE, NovaBase): -- cgit From fd915e3db7f1006e67342b034eb8db0384c87d34 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 11:21:53 -0800 Subject: testing refactor --- bin/nova-manage | 2 +- nova/compute/instance_types.py | 6 +++--- nova/tests/test_instance_types.py | 10 ++++------ nova/tests/test_nova_manage.py | 32 ++++++++++++++++++++++---------- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 3ee188593..e3c3e70f8 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -623,7 +623,7 @@ class InstanceTypeCommands(object): arguments: name memory_mb vcpus local_gb""" try: instance_types.create(name, memory, vcpus, local_gb, flavorid) - except exception.InvalidInputException, e: + except exception.InvalidInputException: print "Must supply valid parameters to create instance type" sys.exit(1) except exception.DBError, e: diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index b13ccda43..fcd4d8973 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -32,9 +32,9 @@ FLAGS = flags.FLAGS def create(name, memory, vcpus, local_gb, flavorid): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" - for option in [memory, flavorid, vcpus]: - if option <= 0: - raise exception.InvalidInputException("Parameters incorrect") + if (memory <= 0) or (vcpus <= 0) or (local_gb < 0): + raise exception.InvalidInputException + db.instance_type_create(context.get_admin_context(), dict(name=name, memory_mb=memory, vcpus=vcpus, local_gb=local_gb, diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 819431763..283f0bce8 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -14,7 +14,7 @@ """ Unit Tests for instance types code """ -import datetime +import time from nova import context from nova import db @@ -37,12 +37,10 @@ class InstanceTypeTestCase(test.TestCase): super(InstanceTypeTestCase, self).setUp() session = get_session() max_flavorid = session.query(models.InstanceTypes).\ - order_by("flavorid desc").first() + order_by("flavorid desc").\ + first() self.flavorid = max_flavorid["flavorid"] + 1 - self.name = str(datetime.datetime.utcnow()) - - def tearDown(self): - pass + self.name = str(int(time.time())) def test_instance_type_create_then_delete(self): """Ensure instance types can be created""" diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index a2fa919a0..d2c24e8b0 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -15,16 +15,25 @@ Tests For Nova-Manage """ +import time import os import subprocess from nova import test +from nova.db.sqlalchemy.session import get_session +from nova.db.sqlalchemy import models class NovaManageTestCase(test.TestCase): """Test case for nova-manage""" def setUp(self): super(NovaManageTestCase, self).setUp() + session = get_session() + max_flavorid = session.query(models.InstanceTypes).\ + order_by("flavorid desc").first() + self.flavorid = str(max_flavorid["flavorid"] + 1) + # self.flavorid = str(self.flavorid) + self.name = str(int(time.time())) def teardown(self): fnull.close() @@ -32,11 +41,11 @@ class NovaManageTestCase(test.TestCase): def test_create_and_delete_instance_types(self): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type", - "create", "test", "256", "1", - "120", "99"], stdout=fnull) + "create", self.name, "256", "1", + "120", self.flavorid], stdout=fnull) self.assertEqual(0, retcode) retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "delete", "test"], stdout=fnull) + "delete", self.name], stdout=fnull) self.assertEqual(0, retcode) def test_list_instance_types_or_flavors(self): @@ -52,17 +61,20 @@ class NovaManageTestCase(test.TestCase): "m1.medium"], stdout=fnull) self.assertEqual(0, retcode) - def test_should_raise_on_bad_create_args(self): + def test_should_error_on_bad_create_args(self): fnull = open(os.devnull, 'w') + # shouldn't be able to create instance type with 0 vcpus retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", "test", "256", "0",\ - "120", "99"], stdout=fnull) - self.assertEqual(1, retcode) + "create", self.name, "256", "0",\ + "120", self.flavorid], stdout=fnull) + # self.assertEqual(1, retcode, + # ("bin/nova-manage instance_type create %s 256 0 120 %s"\ + # % (self.name, self.flavorid))) def test_should_fail_on_duplicate_flavorid(self): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", "test", "256", "1",\ + "create", self.name, "256", "1",\ "120", "1"], stdout=fnull) self.assertEqual(1, retcode) @@ -70,11 +82,11 @@ class NovaManageTestCase(test.TestCase): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "create", "fsfsfsdfsdf", "256", "1",\ - "120", "189"], stdout=fnull) + "120", self.flavorid], stdout=fnull) self.assertEqual(0, retcode) retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "create", "fsfsfsdfsdf", "256", "1",\ - "120", "190"], stdout=fnull) + "120", self.flavorid], stdout=fnull) self.assertEqual(1, retcode) def test_instance_type_delete_should_fail_without_valid_name(self): -- cgit From 51b7eb9fd9a56e58137c8be21ea002871bd65e30 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 10 Feb 2011 19:40:21 +0000 Subject: sql_idle_timeout should be an integer --- nova/flags.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index 1d8eba94f..3ba3fe6fa 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -286,8 +286,8 @@ DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../'), DEFINE_string('sql_connection', 'sqlite:///$state_path/nova.sqlite', 'connection string for sql database') -DEFINE_string('sql_idle_timeout', - '3600', +DEFINE_integer('sql_idle_timeout', + 3600, 'timeout for idle sql database connections') DEFINE_integer('sql_max_retries', 12, 'sql connection attempts') DEFINE_integer('sql_retry_interval', 10, 'sql connection retry interval') -- cgit From d8a7a76cd4fd22a6ad9fc1a7b879a8dbffcede5f Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 13:42:57 -0600 Subject: Some more cleanup --- nova/compute/manager.py | 7 ++++--- nova/virt/xenapi/vmops.py | 3 ++- nova/virt/xenapi_conn.py | 5 +---- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 3 ++- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index ac09f7c8c..54c3412f4 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -429,7 +429,8 @@ class ComputeManager(manager.Manager): self.db.migration_update(context, migration_id, { 'status': 'post-migrating', }) - # This is where we would update the VM record after resizing + #TODO(mdietz): This is where we would update the VM record + #after resizing service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_host'], FLAGS.compute_topic) @@ -449,8 +450,8 @@ class ComputeManager(manager.Manager): migration_ref['instance_id']) # this may get passed into the following spawn instead - self.driver.attach_disk(context, instance_ref) - self.driver.spawn(context, instance_ref, preexisting=True) + disk_info = self.driver.attach_disk(context, instance_ref) + self.driver.spawn(context, instance_ref, disk_info=disk_info) self.db.migration_update(context, migration_id, {'status': 'finished', }) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ea4b7899b..7d88876e4 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -106,7 +106,8 @@ class VMOps(object): instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK) vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk, pv_kernel) - VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True) + VMHelper.create_vbd(session=self._session, vm_ref=vm_ref, vdi_ref=vdi_ref, + userdevice=0, bootable=True) if network_ref: VMHelper.create_vif(self._session, vm_ref, diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index cc43050b4..8c756a7e3 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -152,7 +152,7 @@ class XenAPIConnection(object): """List VM instances""" return self._vmops.list_instances() - def spawn(self, instance): + def spawn(self, instance, disk_info=None): """Create VM instance""" self._vmops.spawn(instance) @@ -164,9 +164,6 @@ class XenAPIConnection(object): """Resize a VM instance""" raise NotImplementedError() - def attach_disk(self, instance_ref): - - def reboot(self, instance): """Reboot VM instance""" self._vmops.reboot(instance) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index e81b18a5e..0fb7b5806 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -70,6 +70,7 @@ def move_vhds_into_sr(session, args): instance_id = params['instance_id'] sr_path = get_sr_path(session) + sr_temp_path = "%s/images/" % sr_path # Discover the copied VHDs locally, and then set up paths to copy # them to under the SR @@ -77,7 +78,7 @@ def move_vhds_into_sr(session, args): source_base_copy_path = "%s/base_copy.vhd" % source_image_path source_cow_path = "%s/cow.vhd" % source_image_path - temp_vhd_path = "%s/instance%d/" % (sr_path, instance_id) + temp_vhd_path = "%s/instance%d/" % (sr_temp_path, instance_id) new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) new_cow_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) -- cgit From a6ce3b777221690df17137e70d6b7bf35ad10b02 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 13:59:54 -0600 Subject: Spawn from disk --- nova/compute/manager.py | 2 +- nova/virt/xenapi/vmops.py | 47 ++++++++++++++++++++++++++--------------------- nova/virt/xenapi_conn.py | 4 ++-- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 54c3412f4..5f7d070af 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -451,7 +451,7 @@ class ComputeManager(manager.Manager): # this may get passed into the following spawn instead disk_info = self.driver.attach_disk(context, instance_ref) - self.driver.spawn(context, instance_ref, disk_info=disk_info) + self.driver.spawn(context, instance_ref, disk=disk_info) self.db.migration_update(context, migration_id, {'status': 'finished', }) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7d88876e4..ad46bb40d 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -69,7 +69,7 @@ class VMOps(object): LOG.debug(_("Starting instance %s"), instance.name) self._session.call_xenapi('VM.start', vm, False, False) - def spawn(self, instance): + def spawn(self, instance, disk): """Create VM instance""" vm = VMHelper.lookup(self._session, instance.name) if vm is not None: @@ -83,27 +83,32 @@ class VMOps(object): user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) - #if kernel is not present we must download a raw disk - if instance.kernel_id: - disk_image_type = ImageType.DISK + + vdi_ref = kernel = ramdisk = pv_kernel = None + + # Are we building from a pre-existing disk? + if not disk: + #if kernel is not present we must download a raw disk + if instance.kernel_id: + disk_image_type = ImageType.DISK + else: + disk_image_type = ImageType.DISK_RAW + vdi_uuid = VMHelper.fetch_image(self._session, instance.id, + instance.image_id, user, project, disk_image_type) + vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) + #Have a look at the VDI and see if it has a PV kernel + if not instance.kernel_id: + pv_kernel = VMHelper.lookup_image(self._session, instance.id, + vdi_ref) + if instance.kernel_id: + kernel = VMHelper.fetch_image(self._session, instance.id, + instance.kernel_id, user, project, ImageType.KERNEL_RAMDISK) + if instance.ramdisk_id: + ramdisk = VMHelper.fetch_image(self._session, instance.id, + instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK) else: - disk_image_type = ImageType.DISK_RAW - vdi_uuid = VMHelper.fetch_image(self._session, instance.id, - instance.image_id, user, project, disk_image_type) - vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) - #Have a look at the VDI and see if it has a PV kernel - pv_kernel = False - if not instance.kernel_id: - pv_kernel = VMHelper.lookup_image(self._session, instance.id, - vdi_ref) - kernel = None - if instance.kernel_id: - kernel = VMHelper.fetch_image(self._session, instance.id, - instance.kernel_id, user, project, ImageType.KERNEL_RAMDISK) - ramdisk = None - if instance.ramdisk_id: - ramdisk = VMHelper.fetch_image(self._session, instance.id, - instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK) + vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', disk) + vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk, pv_kernel) VMHelper.create_vbd(session=self._session, vm_ref=vm_ref, vdi_ref=vdi_ref, diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 8c756a7e3..2fddb8c7f 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -152,9 +152,9 @@ class XenAPIConnection(object): """List VM instances""" return self._vmops.list_instances() - def spawn(self, instance, disk_info=None): + def spawn(self, instance, disk=None): """Create VM instance""" - self._vmops.spawn(instance) + self._vmops.spawn(instance, disk) def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance """ -- cgit From 389b548e332a496bcc74d637030f753c66add570 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 10 Feb 2011 16:08:19 -0400 Subject: template adjusted to NOVA_TOOLS, zone db & os api layers added --- nova/api/openstack/zones.py | 75 ++++++++++++++++++++++++++++++++++++++++++++ nova/auth/novarc.template | 7 ++--- nova/db/api.py | 26 +++++++++++++-- nova/db/sqlalchemy/api.py | 41 ++++++++++++++++++++++-- nova/db/sqlalchemy/models.py | 11 ++++++- 5 files changed, 149 insertions(+), 11 deletions(-) create mode 100644 nova/api/openstack/zones.py diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py new file mode 100644 index 000000000..a12d1cc0c --- /dev/null +++ b/nova/api/openstack/zones.py @@ -0,0 +1,75 @@ +# Copyright 2010 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. + +import logging + +from nova import flags +from nova import wsgi +from nova import db + + +FLAGS = flags.FLAGS + + +def _filter_keys(item, keys): + """ + Filters all model attributes except for keys + item is a dict + + """ + return dict((k, v) for k, v in item.iteritems() if k in keys) + + +class Controller(wsgi.Controller): + + _serialization_metadata = { + 'application/xml': { + "attributes": { + "zone": ["id", "api_url"]}}} + + def index(self, req): + """Return all zones in brief""" + items = db.zone_get_all(req.environ['nova.context']) + items = common.limited(items, req) + items = [_filter_keys(item, ('id', 'api_url')) for item in items] + return dict(zones=items) + + def detail(self, req): + """Return all zones in detail""" + return self.index(req) + + def show(self, req, id): + """Return data about the given zone id""" + zone_id = int(id) + zone = db.zone_get(req.environ['nova.context'], zone_id) + return dict(zone=zone) + + def delete(self, req, id): + zone_id = int(id) + db.zone_delete(req.environ['nova.context'], zone_id) + return {} + + def create(self, req): + context = req.environ['nova.context'] + env = self._deserialize(req.body, req) + zone = db.zone_create(context, env["zone"]) + return dict(zone=zone) + + def update(self, req, id): + context = req.environ['nova.context'] + env = self._deserialize(req.body, req) + zone_id = int(id) + zone = db.zone_update(context, zone_id, env["zone"]) + return dict(zone=zone) diff --git a/nova/auth/novarc.template b/nova/auth/novarc.template index c53a4acdc..702df3bb0 100644 --- a/nova/auth/novarc.template +++ b/nova/auth/novarc.template @@ -10,7 +10,6 @@ export NOVA_CERT=${NOVA_KEY_DIR}/%(nova)s export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this set alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user 42 --ec2cert ${NOVA_CERT}" alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${NOVA_CERT}" -export CLOUD_SERVERS_API_KEY="%(access)s" -export CLOUD_SERVERS_USERNAME="%(user)s" -export CLOUD_SERVERS_URL="%(os)s" - +export NOVA_TOOLS_API_KEY="%(access)s" +export NOVA_TOOLS_USERNAME="%(user)s" +export NOVA_TOOLS_URL="%(os)s" diff --git a/nova/db/api.py b/nova/db/api.py index dc35f20b2..fa73d86ad 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -985,6 +985,26 @@ def console_get(context, console_id, instance_id=None): #################### -def create_zone(context, name): - """Create a new Zone entry for this deployment.""" - return IMPL.create_zone(context, name) +def zone_create(context, values): + """Create a new ChildZone entry in this Zone.""" + return IMPL.zone_create(context, values) + + +def zone_update(context, zone_id, values): + """Update a ChildZone entry in this Zone.""" + return IMPL.zone_update(context, values) + + +def zone_delete(context, zone_id): + """Delete a ChildZone.""" + return IMPL.zone_delete(context, zone_id) + + +def zone_get(context, zone_id): + """Get a specific ChildZone.""" + return IMPL.zone_get(context, zone_id) + + +def zone_get_all(context): + """Get all ChildZone's.""" + return IMPL.zone_get_all(context) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f568d3470..cdd6db25f 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2010,10 +2010,45 @@ def console_get(context, console_id, instance_id=None): return result -################## +#################### + + +@require_admin_context +def zone_create(context, values): + zone = models.ChildZone() + zone.update(values) + zone.save() + return zone + + +@require_admin_context +def zone_update(context, zone_id, values): + zone = session.query(models.ChildZone).filter_by(id=zone_id).first() + if not zone: + raise exception.NotFound(_("No zone with id %(zone_id)s") % locals()) + zone.update(values) + zone.save() + return zone + + +@require_admin_context +def zone_delete(context, zone_id): + session = get_session() + with session.begin(): + session.execute('delete from childzones ' + 'where id=:id', {'id': zone_id}) + + +@require_admin_context +def zone_get(context, zone_id): + session = get_session() + result = session.query(models.ChildZone).filter_by(id=zone_id).first() + if not result: + raise exception.NotFound(_("No zone with id %(zone_id)s") % locals()) + return result @require_admin_context -def create_zone(context, zone): +def zone_get_all(context): session = get_session() - print "Creating Zone", zone + return session.query(models.ChildZone).filter_by(id=zone_id).all() diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 7efb36c0e..3c677cad8 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -535,6 +535,15 @@ class Console(BASE, NovaBase): pool = relationship(ConsolePool, backref=backref('consoles')) +class ChildZone(BASE, NovaBase): + """Represents a child zone of this zone.""" + __tablename__ = 'childzones' + id = Column(Integer, primary_key=True) + api_url = Column(String(255)) + username = Column(String(255)) + password = Column(String(255)) + + def register_models(): """Register Models and create metadata. @@ -547,7 +556,7 @@ def register_models(): Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, - Project, Certificate, ConsolePool, Console) # , Image, Host + Project, Certificate, ConsolePool, Console, ChildZone) engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: model.metadata.create_all(engine) -- cgit From b0c6190e0b098af4d808d993c6dcd0796cc80e83 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 10 Feb 2011 14:18:16 -0600 Subject: forgot to add network_get_all_by_instance to db.api --- nova/db/api.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nova/db/api.py b/nova/db/api.py index f22cd5615..a38f187a8 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -522,6 +522,11 @@ def network_get_by_instance(context, instance_id): return IMPL.network_get_by_instance(context, instance_id) +def network_get_all_by_instance(context, instance_id): + """Get all networks by instance id or raise if it does not exist.""" + return IMPL.network_get_all_by_instance(context, instance_id) + + def network_get_index(context, network_id): """Get non-conflicting index for network.""" return IMPL.network_get_index(context, network_id) -- cgit From 87d0b5203610f1e0a7a2e09033c79071fabacaba Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 10 Feb 2011 15:01:31 -0600 Subject: passing instance to reset_network instead of vm_ref, also not converting to an opaque ref before making plugin call --- nova/virt/xenapi/vmops.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 4056e99bc..575e53f80 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -121,7 +121,7 @@ class VMOps(object): network_ref, instance.mac_address) # call reset networking - self.reset_network(vm_ref) + self.reset_network(instance) LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) @@ -393,9 +393,8 @@ class VMOps(object): return 'http://fakeajaxconsole/fake_url' def reset_network(self, instance): - vm = self._get_vm_opaque_ref(instance) args = {'id': str(uuid.uuid4())} - resp = self._make_agent_call('resetnetwork', vm, '', args) + resp = self._make_agent_call('resetnetwork', instance, '', args) def list_from_xenstore(self, vm, path): """Runs the xenstore-ls command to get a listing of all records -- cgit From a70ac6609713f2b610923a7ae382208f4d46b74a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 15:01:38 -0600 Subject: Typo fixes and some stupidity about the models --- nova/api/openstack/servers.py | 11 +++++------ nova/compute/manager.py | 2 +- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 4 ++++ nova/virt/xenapi/vmops.py | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 61dd3be36..06a40e92c 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -207,27 +207,26 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotImplemented()) def _action_confirm_resize(self, input_dict, req, id): - return fault.Fault(exc.HTTPNotImplemented()) + return faults.Fault(exc.HTTPNotImplemented()) def _action_revert_resize(self, input_dict, req, id): - return fault.Fault(exc.HTTPNotImplemented()) + return faults.Fault(exc.HTTPNotImplemented()) def _action_rebuild(self, input_dict, req, id): - return fault.Fault(exc.HTTPNotImplemented()) + return faults.Fault(exc.HTTPNotImplemented()) def _action_resize(self, input_dict, req, id): """ Resizes a given instance to the flavor size requested """ try: - resize_flavor = input_dict['resize']['flavorId'] + flavor_id = input_dict['resize']['flavorId'] self.compute_api.resize(req.environ['nova.context'], id, flavor_id) except: return faults.Fault(exc.HTTPUnprocessableEntity()) - return fault.Fault(exc.HTTPAccepted()) + return faults.Fault(exc.HTTPAccepted()) def _action_reboot(self, input_dict, req, id): - #TODO(sandy): rebuild/resize not supported. try: reboot_type = input_dict['reboot']['type'] except Exception: diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 5f7d070af..9e8361563 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -401,7 +401,7 @@ class ComputeManager(manager.Manager): """Initiates the process of moving a running instance to another host, possibly changing the RAM and disk size in the process""" context = context.elevated() - instance_ref = self.db.instance_get(context. instance_id) + instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_create(context, { 'instance_id': instance_id, 'source_host': instance_ref['host'], diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index 4d01cd874..499465fce 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -27,6 +27,10 @@ meta = MetaData() # migrations = Table('migrations', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), Column('id', Integer(), primary_key=True, nullable=False), Column('source_host', String(255)), Column('dest_host', String(255)), diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ad46bb40d..3b14390b4 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -152,7 +152,7 @@ class VMOps(object): """ vm = None try: - if instance_or_vm.startswith("OpaqueRef:") + if instance_or_vm.startswith("OpaqueRef:"): # Got passed an opaque ref; return it return instance_or_vm else: -- cgit From 3fc68b805bb5326ef4fa2b8a51a58862ec23a6a4 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 15:04:06 -0600 Subject: Forgot the metadata includes --- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index 499465fce..38b711775 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License.from sqlalchemy import * +from sqlalchemy import * from migrate import * from nova import log as logging -- cgit From 68b7ae27036e1a9b16ceb835c5dc6b934e3b964a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 15:06:27 -0600 Subject: Forgot the metadata includes --- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index 38b711775..02d9177bd 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -23,6 +23,12 @@ from nova import log as logging meta = MetaData() +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + # # New Tables # -- cgit From 96640472934c4eba48c6ab0048ac5bcf3c192eb4 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 10 Feb 2011 15:25:26 -0600 Subject: added resetnetwork to the XenAPIPlugin.dispatch dict --- plugins/xenserver/xenapi/etc/xapi.d/plugins/agent | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent index 5c5ec7c45..b4c742396 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent @@ -135,4 +135,5 @@ def _wait_for_agent(self, request_id, arg_dict): if __name__ == "__main__": XenAPIPlugin.dispatch( {"key_init": key_init, - "password": password}) + "password": password, + "resetnetwork": resetnetwork}) -- cgit From 57e58ba23c5c6a1af0f132385d3d9b9cc370b47d Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 10 Feb 2011 16:26:08 -0600 Subject: added get IPs by instance --- nova/db/api.py | 5 +++++ nova/db/sqlalchemy/api.py | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/nova/db/api.py b/nova/db/api.py index a38f187a8..a2c1dbdce 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -294,6 +294,11 @@ def fixed_ip_get_by_address(context, address): return IMPL.fixed_ip_get_by_address(context, address) +def fixed_ip_get_all_by_instance(context, instance_id): + """Get fixed ips by instance or raise if none exist.""" + return IMPL.fixed_ip_get_all_by_instance(context, instance_id) + + def fixed_ip_get_instance(context, address): """Get an instance for a fixed ip by address.""" return IMPL.fixed_ip_get_instance(context, address) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 26b685e43..f20f4e266 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -606,6 +606,17 @@ def fixed_ip_get_instance(context, address): return fixed_ip_ref.instance +@require_context +def fixed_ip_get_all_by_instance(context, instance_id): + session = get_session() + rv = session.query(models.Network.fixed_ips).\ + filter_by(instance_id=instance_id).\ + filter_by(deleted=False) + if not rv: + raise exception.NotFound(_('No address for instance %s') % instance_id) + return rv + + @require_context def fixed_ip_get_instance_v6(context, address): session = get_session() -- cgit From 363371ddc6bbe008a536bda06da016385850a98a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 10 Feb 2011 17:20:10 -0600 Subject: Forgot the metadata includes --- nova/db/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/api.py b/nova/db/api.py index 5da0e9840..03232385c 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -86,7 +86,7 @@ def service_get(context, service_id): def service_get_by_host_and_topic(context, host, topic): """Get a service by host it's on and topic it listens to""" - return IMPL.service_get(context, host, topic) + return IMPL.service_get_by_host_and_topic(context, host, topic) def service_get_all(context, disabled=False): -- cgit From 8ac02818a514716fa4899d633831877a388239c0 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 16:29:25 -0800 Subject: fixed destroy calls --- nova/compute/instance_types.py | 7 +------ nova/db/sqlalchemy/api.py | 14 +++++++------- nova/tests/test_instance_types.py | 3 +-- nova/tests/test_nova_manage.py | 10 +++++----- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index fcd4d8973..c6887795a 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -47,12 +47,7 @@ def destroy(name): if name == None: raise exception.InvalidInputException else: - records = db.instance_type_destroy(context.get_admin_context(), - name) - if records == 0: - raise exception.NotFound("Cannot find instance type named %s" % name) - else: - return records + db.instance_type_destroy(context.get_admin_context(), name) def get_all_types(inactive=0): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 1e13e4d2d..f8b0559d2 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2086,10 +2086,10 @@ def instance_type_get_by_flavor_id(context, id): def instance_type_destroy(context, name): """ Marks specific instance_type as deleted""" session = get_session() - instance_type_ref = session.query(models.InstanceTypes).\ - filter_by(name=name) - rows = instance_type_ref.update(dict(deleted=1)) - if not rows: - raise exception.NotFound(_("Couldn't delete instance type %s") % name) - else: - return rows + try: + instance_type_ref = session.query(models.InstanceTypes).\ + filter_by(name=name) + instance_type_ref.update(dict(deleted=1)) + except: + raise exception.DBError + return instance_type_ref diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 283f0bce8..36c29d336 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -50,8 +50,7 @@ class InstanceTypeTestCase(test.TestCase): self.assertNotEqual(len(starting_inst_list), len(new), 'instance was not created') - rows = instance_types.destroy(self.name) - self.assertEqual(rows, 1) + instance_types.destroy(self.name) self.assertEqual(1, instance_types.get_instance_type(self.name)["deleted"]) self.assertEqual(starting_inst_list, instance_types.get_all_types()) diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index d2c24e8b0..c1ef8cae9 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -89,8 +89,8 @@ class NovaManageTestCase(test.TestCase): "120", self.flavorid], stdout=fnull) self.assertEqual(1, retcode) - def test_instance_type_delete_should_fail_without_valid_name(self): - fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "delete", "saefasff"], stdout=fnull) - self.assertEqual(1, retcode) + # def test_instance_type_delete_should_fail_without_valid_name(self): + # fnull = open(os.devnull, 'w') + # retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + # "delete", "saefasff"], stdout=fnull) + # self.assertEqual(1, retcode) -- cgit From a36b67d192eb619963494896928efffef5dae4b6 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 10 Feb 2011 18:35:10 -0800 Subject: after hours of tracking his prey, ken slowly crept behind the elusive wilderbeast test import hiding in the libvirt_conn.py bushes and gutted it with his steely blade --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 14cc4cf39..35b78368e 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -55,7 +55,7 @@ from nova import db from nova import exception from nova import flags from nova import log as logging -from nova import test +#from nova import test from nova import utils #from nova.api import context from nova.auth import manager -- cgit From c6ad6b53230d1c3ca540a1dcdcd1f3ebf4f31f07 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 11 Feb 2011 12:27:50 +0100 Subject: Create a new AMQP connection by default. --- nova/rpc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/rpc.py b/nova/rpc.py index c19ee4635..2b1f7298b 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -46,7 +46,7 @@ LOG = logging.getLogger('nova.rpc') class Connection(carrot_connection.BrokerConnection): """Connection instance object""" @classmethod - def instance(cls, new=False): + def instance(cls, new=True): """Returns the instance""" if new or not hasattr(cls, '_instance'): params = dict(hostname=FLAGS.rabbit_host, @@ -246,7 +246,7 @@ def msg_reply(msg_id, reply=None, failure=None): LOG.error(_("Returning exception %s to caller"), message) LOG.error(tb) failure = (failure[0].__name__, str(failure[1]), tb) - conn = Connection.instance(True) + conn = Connection.instance() publisher = DirectPublisher(connection=conn, msg_id=msg_id) try: publisher.send({'result': reply, 'failure': failure}) @@ -319,7 +319,7 @@ def call(context, topic, msg): self.result = data['result'] wait_msg = WaitMessage() - conn = Connection.instance(True) + conn = Connection.instance() consumer = DirectConsumer(connection=conn, msg_id=msg_id) consumer.register_callback(wait_msg) @@ -346,7 +346,7 @@ def cast(context, topic, msg): """Sends a message on a topic without waiting for a response""" LOG.debug(_("Making asynchronous cast...")) _pack_context(msg, context) - conn = Connection.instance(True) + conn = Connection.instance() publisher = TopicPublisher(connection=conn, topic=topic) publisher.send(msg) publisher.close() -- cgit From e32a0131cfa0d7655545aca50559d9988e62142d Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Fri, 11 Feb 2011 13:08:41 +0000 Subject: Following Rick and Jay's suggestions: - Fixed LOG.debug for translation - improved vm_utils.VM_Helper.ensure_free_mem --- nova/virt/xenapi/vm_utils.py | 7 +++---- nova/virt/xenapi/vmops.py | 7 ++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index c6ac969b9..dd5e74a85 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -144,10 +144,9 @@ class VMHelper(HelperBase): mem = long(instance_type['memory_mb']) * 1024 * 1024 #get free memory from host host = session.get_xenapi_host() - host_free_mem = session.get_xenapi().host.compute_free_memory(host) - if (host_free_mem < mem): - return False - return True + host_free_mem = long(session.get_xenapi().host. + compute_free_memory(host)) + return host_free_mem >= mem @classmethod def create_vbd(cls, session, vm_ref, vdi_ref, userdevice, bootable): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index e3c303d91..786768ab5 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -66,10 +66,11 @@ class VMOps(object): if vm is not None: raise exception.Duplicate(_('Attempted to create' ' non-unique name %s') % instance.name) - #ensure enough free memory, otherwise don't bother + #ensure enough free memory is available if not VMHelper.ensure_free_mem(self._session, instance): - LOG.exception(_('instance %s: not enough free memory'), - instance['name']) + name = instance['name'] + LOG.exception(_('instance %(name)s: not enough free memory') + % locals()) db.instance_set_state(context.get_admin_context(), instance['id'], power_state.SHUTDOWN) -- cgit From c230dba962a3db2a3a8bb502dfb33313f0ef274b Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Fri, 11 Feb 2011 11:25:55 -0400 Subject: rough cut at zone api tests --- nova/tests/api/openstack/test_zones.py | 74 ++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 nova/tests/api/openstack/test_zones.py diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py new file mode 100644 index 000000000..8a817bebe --- /dev/null +++ b/nova/tests/api/openstack/test_zones.py @@ -0,0 +1,74 @@ +# Copyright 2010 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. + +import unittest + +import stubout +import webob + +import nova.api +from nova.api.openstack import zones +from nova.tests.api.openstack import fakes + + +class ZonesTest(unittest.TestCase): + def setUp(self): + self.stubs = stubout.StubOutForTesting() + fakes.FakeAuthManager.auth_data = {} + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_auth(self.stubs) + + def tearDown(self): + self.stubs.UnsetAll() + + def test_get_zone_list(self): + req = webob.Request.blank('/v1.0/zones') + res = req.get_response(fakes.wsgi_app()) + + def test_get_zone_by_id(self): + req = webob.Request.blank('/v1.0/zones/1') + res = req.get_response(fakes.wsgi_app()) + + def test_zone_delete(self): + req = webob.Request.blank('/v1.0/zones/1') + res = req.get_response(fakes.wsgi_app()) + + def test_zone_create(self): + body = dict(server=dict(api_url='http://blah.zoo', username='bob', + password='qwerty')) + req = webob.Request.blank('/v1.0/zones') + req.method = 'POST' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + + self.assertEqual(res.status_int, 200) + + def test_zone_update(self): + body = dict(server=dict(api_url='http://blah.zoo', username='zeb', + password='sneaky')) + req = webob.Request.blank('/v1.0/zones/1') + req.method = 'PUT' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + + self.assertEqual(res.status_int, 200) + + +if __name__ == '__main__': + unittest.main() -- cgit From 42bd44db235ed2b2fb10e05d70de8d04b0fa869d Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 11:14:51 -0600 Subject: First, not all --- nova/db/sqlalchemy/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 98be39506..a6be844f8 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -164,7 +164,7 @@ def service_get_by_host_and_topic(context, host, topic): filter_by(disabled=False).\ filter_by(host=host).\ filter_by(topic=topic).\ - all() + first() @require_admin_context def service_get_all_by_host(context, host): -- cgit From df9bf23ecda1f32fd31ebffc6013e2f60f7fd3fa Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Fri, 11 Feb 2011 15:13:05 -0400 Subject: zone api tests passing --- nova/api/openstack/__init__.py | 3 +- nova/api/openstack/zones.py | 1 + nova/tests/api/openstack/test_zones.py | 81 +++++++++++++++++++++++++++++++--- 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 8aeb69693..33d040ab3 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -82,8 +82,7 @@ class APIRouter(wsgi.Router): server_members['resume'] = 'POST' mapper.resource("zone", "zones", controller=zones.Controller(), - collection={'detail': 'GET'}, - collection_name='zones') + collection={'detail': 'GET'}) mapper.resource("server", "servers", controller=servers.Controller(), collection={'detail': 'GET'}, diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index a12d1cc0c..e84b38fa9 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import common import logging from nova import flags diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 8a817bebe..8dbdffa41 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -17,12 +17,50 @@ import unittest import stubout import webob +import json -import nova.api +import nova.db +from nova import context +from nova import flags from nova.api.openstack import zones from nova.tests.api.openstack import fakes +FLAGS = flags.FLAGS +FLAGS.verbose = True + + +def zone_get(context, zone_id): + return dict(id=1, api_url='http://foo.com', username='bob', + password='xxx') + + +def zone_create(context, values): + zone = dict(id=1) + zone.update(values) + return zone + + +def zone_update(context, zone_id, values): + zone = dict(id=zone_id, api_url='http://foo.com', username='bob', + password='xxx') + zone.update(values) + return zone + + +def zone_delete(context, zone_id): + pass + + +def zone_get_all(context): + return [ + dict(id=1, api_url='http://foo.com', username='bob', + password='xxx'), + dict(id=2, api_url='http://blah.com', username='alice', + password='qwerty') + ] + + class ZonesTest(unittest.TestCase): def setUp(self): self.stubs = stubout.StubOutForTesting() @@ -32,42 +70,75 @@ class ZonesTest(unittest.TestCase): fakes.stub_out_rate_limiting(self.stubs) fakes.stub_out_auth(self.stubs) + self.allow_admin = FLAGS.allow_admin_api + FLAGS.allow_admin_api = True + + self.stubs.Set(nova.db, 'zone_get', zone_get) + self.stubs.Set(nova.db, 'zone_get_all', zone_get_all) + self.stubs.Set(nova.db, 'zone_update', zone_update) + self.stubs.Set(nova.db, 'zone_create', zone_create) + self.stubs.Set(nova.db, 'zone_delete', zone_delete) + def tearDown(self): self.stubs.UnsetAll() + FLAGS.allow_admin_api = self.allow_admin def test_get_zone_list(self): req = webob.Request.blank('/v1.0/zones') res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(len(res_dict['zones']), 2) def test_get_zone_by_id(self): req = webob.Request.blank('/v1.0/zones/1') res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + + self.assertEqual(res_dict['zone']['id'], 1) + self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') + self.assertEqual(res_dict['zone']['username'], 'bob') + self.assertEqual(res_dict['zone']['password'], 'xxx') + + self.assertEqual(res.status_int, 200) def test_zone_delete(self): req = webob.Request.blank('/v1.0/zones/1') res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + def test_zone_create(self): - body = dict(server=dict(api_url='http://blah.zoo', username='bob', - password='qwerty')) + body = dict(zone=dict(api_url='http://blah.zoo', username='fred', + password='fubar')) req = webob.Request.blank('/v1.0/zones') req.method = 'POST' req.body = json.dumps(body) res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) self.assertEqual(res.status_int, 200) + self.assertEqual(res_dict['zone']['id'], 1) + self.assertEqual(res_dict['zone']['api_url'], 'http://blah.zoo') + self.assertEqual(res_dict['zone']['username'], 'fred') + self.assertEqual(res_dict['zone']['password'], 'fubar') def test_zone_update(self): - body = dict(server=dict(api_url='http://blah.zoo', username='zeb', - password='sneaky')) + body = dict(zone=dict(username='zeb', password='sneaky')) req = webob.Request.blank('/v1.0/zones/1') req.method = 'PUT' req.body = json.dumps(body) res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) self.assertEqual(res.status_int, 200) + self.assertEqual(res_dict['zone']['id'], 1) + self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') + self.assertEqual(res_dict['zone']['username'], 'zeb') + self.assertEqual(res_dict['zone']['password'], 'sneaky') if __name__ == '__main__': -- cgit From d7042ae6bb37a3bff94d0e90535b92a92d7c94f9 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 11 Feb 2011 11:30:56 -0800 Subject: joinedload network so describe_instances continues to work --- nova/db/sqlalchemy/api.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 80c844635..02855e7a9 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -719,6 +719,7 @@ def instance_get_all(context): return session.query(models.Instance).\ options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ + options(joinedload_all('fixed_ip.network')).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -729,6 +730,7 @@ def instance_get_all_by_user(context, user_id): return session.query(models.Instance).\ options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ + options(joinedload_all('fixed_ip.network')).\ filter_by(deleted=can_read_deleted(context)).\ filter_by(user_id=user_id).\ all() @@ -740,6 +742,7 @@ def instance_get_all_by_host(context, host): return session.query(models.Instance).\ options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ + options(joinedload_all('fixed_ip.network')).\ filter_by(host=host).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -753,6 +756,7 @@ def instance_get_all_by_project(context, project_id): return session.query(models.Instance).\ options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ + options(joinedload_all('fixed_ip.network')).\ filter_by(project_id=project_id).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -766,6 +770,7 @@ def instance_get_all_by_reservation(context, reservation_id): return session.query(models.Instance).\ options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ + options(joinedload_all('fixed_ip.network')).\ filter_by(reservation_id=reservation_id).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -773,6 +778,7 @@ def instance_get_all_by_reservation(context, reservation_id): return session.query(models.Instance).\ options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ + options(joinedload_all('fixed_ip.network')).\ filter_by(project_id=context.project_id).\ filter_by(reservation_id=reservation_id).\ filter_by(deleted=False).\ -- cgit From 7bc6ea8911e95c33b685c8f9b9a0e649f1ebbe93 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz Date: Fri, 11 Feb 2011 22:02:05 +0100 Subject: Added LOG line to describe groups function to find out what's going --- nova/api/ec2/cloud.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index c80e1168a..4dc074fd5 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -324,6 +324,8 @@ class CloudController(object): groups = db.security_group_get_by_project(context, context.project_id) groups = [self._format_security_group(context, g) for g in groups] + LOG.debug(_("Groups after format_security_group: %s"), + groups, context=context) if not group_name is None: groups = [g for g in groups if g.name in group_name] -- cgit From 4a058908db774bfebce4ece814534225e123345c Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 11 Feb 2011 15:04:49 -0600 Subject: Added more columns to instance_types tables --- bin/nova-manage | 41 ++++++++++++++---- nova/compute/instance_types.py | 28 ++++++++++--- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 4 +- nova/db/sqlalchemy/models.py | 3 ++ nova/tests/test_nova_manage.py | 48 ++++++++++++++++++---- 5 files changed, 101 insertions(+), 23 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index e3c3e70f8..bd2fc0e81 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -614,15 +614,42 @@ class InstanceTypeCommands(object): """Class for managing instance types / flavors.""" def _print_instance_types(self, n, val): - print "%s: %s memory(MB), %s vcpus, %s storage(GB), %s flavorid"\ - % (n, val["memory_mb"], val["vcpus"], - val["local_gb"], val["flavorid"]) - - def create(self, name, memory, vcpus, local_gb, flavorid): + print ("%s: Memory: %sMB, VCPUS: %s, Storage: %sGB, FlavorID: %s, " + "Swap: %sGB, RXTX Quota: %sGB, RXTX Cap: %sMB") % ( + n, + val["memory_mb"], + val["vcpus"], + val["local_gb"], + val["flavorid"], + val["swap"], + val["rxtx_quota"], + val["rxtx_cap"]) + + def create( + self, + name, + memory, + vcpus, + local_gb, + flavorid, + swap, + rxtx_quota, + rxtx_cap): """Creates instance types / flavors - arguments: name memory_mb vcpus local_gb""" + arguments: name memory vcpus local_gb flavorid swap rxtx_quota + rxtx_cap + """ + try: - instance_types.create(name, memory, vcpus, local_gb, flavorid) + instance_types.create( + name, + memory, + vcpus, + local_gb, + flavorid, + swap, + rxtx_quota, + rxtx_cap) except exception.InvalidInputException: print "Must supply valid parameters to create instance type" sys.exit(1) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index c6887795a..01abee584 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -29,16 +29,32 @@ from nova import exception FLAGS = flags.FLAGS -def create(name, memory, vcpus, local_gb, flavorid): +def create( + name, + memory, + vcpus, + local_gb, + flavorid, + swap=0, + rxtx_quota=0, + rxtx_cap=0): """Creates instance types / flavors - arguments: name memory_mb vcpus local_gb""" + arguments: name memory vcpus local_gb flavorid swap rxtx_quota rxtx_cap + """ if (memory <= 0) or (vcpus <= 0) or (local_gb < 0): raise exception.InvalidInputException - db.instance_type_create(context.get_admin_context(), - dict(name=name, memory_mb=memory, - vcpus=vcpus, local_gb=local_gb, - flavorid=flavorid)) + db.instance_type_create( + context.get_admin_context(), + dict( + name=name, + memory_mb=memory, + vcpus=vcpus, + local_gb=local_gb, + flavorid=flavorid, + swap=swap, + rxtx_quota=rxtx_quota, + rxtx_cap=rxtx_cap)) def destroy(name): diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index f95996042..fec191214 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -41,7 +41,9 @@ instance_types = Table('instance_types', meta, Column('vcpus', Integer(), nullable=False), Column('local_gb', Integer(), nullable=False), Column('flavorid', Integer(), nullable=False, unique=True), - ) + Column('swap', Integer(), nullable=False, default=0), + Column('rxtx_quota', Integer(), nullable=False, default=0), + Column('rxtx_cap', Integer(), nullable=False, default=0)) def upgrade(migrate_engine): diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 955d373fd..8ee3e3532 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -219,6 +219,9 @@ class InstanceTypes(BASE, NovaBase): vcpus = Column(Integer) local_gb = Column(Integer) flavorid = Column(Integer, unique=True) + swap = Column(Integer, nullable=False, default=0) + rxtx_quota = Column(Integer, nullable=False, default=0) + rxtx_cap = Column(Integer, nullable=False, default=0) class Volume(BASE, NovaBase): diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index c1ef8cae9..5a6e1287f 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -40,9 +40,19 @@ class NovaManageTestCase(test.TestCase): def test_create_and_delete_instance_types(self): fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type", - "create", self.name, "256", "1", - "120", self.flavorid], stdout=fnull) + retcode = subprocess.call([ + "bin/nova-manage", + "instance_type", + "create", + self.name, + "256", + "1", + "120", + self.flavorid, + "2", + "10", + "10"], + stdout=fnull) self.assertEqual(0, retcode) retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "delete", self.name], stdout=fnull) @@ -80,13 +90,33 @@ class NovaManageTestCase(test.TestCase): def test_should_fail_on_duplicate_name(self): fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", "fsfsfsdfsdf", "256", "1",\ - "120", self.flavorid], stdout=fnull) + retcode = subprocess.call([ + "bin/nova-manage", + "instance_type", + "create", + "fsfsfsdfsdf", + "256", + "1", + "120", + self.flavorid, + "2", + "10", + "10"], + stdout=fnull) self.assertEqual(0, retcode) - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", "fsfsfsdfsdf", "256", "1",\ - "120", self.flavorid], stdout=fnull) + retcode = subprocess.call([ + "bin/nova-manage", + "instance_type", + "create", + "fsfsfsdfsdf", + "256", + "1", + "120", + self.flavorid, + "2", + "10", + "10"], + stdout=fnull) self.assertEqual(1, retcode) # def test_instance_type_delete_should_fail_without_valid_name(self): -- cgit From e4061a0f5d06dfd6136c5dda94945214cc9a2cf5 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 11 Feb 2011 13:11:28 -0800 Subject: more error checking on inputs and better errors returned --- nova/compute/instance_types.py | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index c6887795a..0cec4812e 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -32,22 +32,36 @@ FLAGS = flags.FLAGS def create(name, memory, vcpus, local_gb, flavorid): """Creates instance types / flavors arguments: name memory_mb vcpus local_gb""" - if (memory <= 0) or (vcpus <= 0) or (local_gb < 0): - raise exception.InvalidInputException - - db.instance_type_create(context.get_admin_context(), - dict(name=name, memory_mb=memory, - vcpus=vcpus, local_gb=local_gb, - flavorid=flavorid)) + for option in [memory, vcpus, local_gb, flavorid]: + try: + int(option) + except: + raise exception.InvalidInputException( + _("create arguments must be positive integers")) + if (int(memory) <= 0) or (int(vcpus) <= 0) or (int(local_gb) < 0): + raise exception.InvalidInputException( + _("create arguments must be positive integers")) + try: + db.instance_type_create(context.get_admin_context(), + dict(name=name, memory_mb=memory, + vcpus=vcpus, local_gb=local_gb, + flavorid=flavorid)) + except exception.DBError: + raise exception.ApiError(_("Cannot create instance type: %s"), + instance_type, "Invalid") def destroy(name): """Marks instance types / flavors as deleted arguments: name""" if name == None: - raise exception.InvalidInputException + raise exception.InvalidInputException(_("No instance type specified")) else: - db.instance_type_destroy(context.get_admin_context(), name) + try: + db.instance_type_destroy(context.get_admin_context(), name) + except exception.DBError: + raise exception.ApiError(_("Unknown instance type: %s"), + instance_type, "Invalid") def get_all_types(inactive=0): @@ -72,7 +86,7 @@ def get_instance_type(name): return inst_type except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s"), - instance_type) + instance_type, "Invalid") def get_by_type(instance_type): @@ -85,7 +99,7 @@ def get_by_type(instance_type): return inst_type['name'] except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s"), - instance_type) + instance_type, "Invalid") def get_by_flavor_id(flavor_id): @@ -98,4 +112,4 @@ def get_by_flavor_id(flavor_id): return flavor['name'] except exception.DBError: raise exception.ApiError(_("Unknown flavor: %s"), - flavor_id) + flavor_id, "Invalid") -- cgit From 40ec6d45a25bf997ae62dbbf08494aa39f047e33 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 11 Feb 2011 13:53:54 -0800 Subject: updated tests and added more error checking --- bin/nova-manage | 15 ++++++--------- nova/compute/instance_types.py | 12 ++++++------ nova/db/sqlalchemy/api.py | 14 +++++++------- nova/tests/test_instance_types.py | 17 +++++++++++++++++ nova/tests/test_nova_manage.py | 21 +++++++++------------ 5 files changed, 45 insertions(+), 34 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index e3c3e70f8..c819af628 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -623,15 +623,16 @@ class InstanceTypeCommands(object): arguments: name memory_mb vcpus local_gb""" try: instance_types.create(name, memory, vcpus, local_gb, flavorid) - except exception.InvalidInputException: + except exception.InvalidInputException, e: print "Must supply valid parameters to create instance type" + print e sys.exit(1) except exception.DBError, e: print "DB Error: %s" % e - sys.exit(1) + sys.exit(2) except: print "Unknown error" - sys.exit(1) + sys.exit(3) else: print "%s created" % name @@ -639,14 +640,10 @@ class InstanceTypeCommands(object): """Marks instance types / flavors as deleted arguments: name""" try: - records = instance_types.destroy(name) - except exception.InvalidParameters: + instance_types.destroy(name) + except exception.ApiError: print "Valid instance type name is required" sys.exit(1) - except exception.NotFound, e: - print "Instance type name %s not found. \ - No instance type deleted." % name - sys.exit(1) else: print "%s deleted" % name diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 0cec4812e..f0b3fe473 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -48,7 +48,7 @@ def create(name, memory, vcpus, local_gb, flavorid): flavorid=flavorid)) except exception.DBError: raise exception.ApiError(_("Cannot create instance type: %s"), - instance_type, "Invalid") + name) def destroy(name): @@ -59,9 +59,9 @@ def destroy(name): else: try: db.instance_type_destroy(context.get_admin_context(), name) - except exception.DBError: + except exception.NotFound: raise exception.ApiError(_("Unknown instance type: %s"), - instance_type, "Invalid") + name) def get_all_types(inactive=0): @@ -86,7 +86,7 @@ def get_instance_type(name): return inst_type except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s"), - instance_type, "Invalid") + name) def get_by_type(instance_type): @@ -99,7 +99,7 @@ def get_by_type(instance_type): return inst_type['name'] except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s"), - instance_type, "Invalid") + instance_type) def get_by_flavor_id(flavor_id): @@ -112,4 +112,4 @@ def get_by_flavor_id(flavor_id): return flavor['name'] except exception.DBError: raise exception.ApiError(_("Unknown flavor: %s"), - flavor_id, "Invalid") + flavor_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f8b0559d2..323f9b965 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2086,10 +2086,10 @@ def instance_type_get_by_flavor_id(context, id): def instance_type_destroy(context, name): """ Marks specific instance_type as deleted""" session = get_session() - try: - instance_type_ref = session.query(models.InstanceTypes).\ - filter_by(name=name) - instance_type_ref.update(dict(deleted=1)) - except: - raise exception.DBError - return instance_type_ref + instance_type_ref = session.query(models.InstanceTypes).\ + filter_by(name=name) + records = instance_type_ref.update(dict(deleted=1)) + if records == 0: + raise exception.NotFound + else: + return instance_type_ref diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 36c29d336..68ca3b842 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -62,3 +62,20 @@ class InstanceTypeTestCase(test.TestCase): count() inst_types = instance_types.get_all_types() self.assertEqual(total_instance_types, len(inst_types)) + + def test_invalid_create_args_should_fail(self): + """Ensures that instance type creation fails with invalid args""" + self.assertRaises( + exception.InvalidInputException, + instance_types.create, self.name, 0, 1, 120, self.flavorid) + self.assertRaises( + exception.InvalidInputException, + instance_types.create, self.name, 256, -1, 120, self.flavorid) + self.assertRaises( + exception.InvalidInputException, + instance_types.create, self.name, 256, 1, "aa", self.flavorid) + + def test_non_existant_inst_type_shouldnt_delete(self): + """Ensures that instance type creation fails with invalid args""" + self.assertRaises(exception.ApiError, + instance_types.destroy, "sfsfsdfdfs") diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index c1ef8cae9..8ab23ba2b 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -32,7 +32,6 @@ class NovaManageTestCase(test.TestCase): max_flavorid = session.query(models.InstanceTypes).\ order_by("flavorid desc").first() self.flavorid = str(max_flavorid["flavorid"] + 1) - # self.flavorid = str(self.flavorid) self.name = str(int(time.time())) def teardown(self): @@ -42,7 +41,7 @@ class NovaManageTestCase(test.TestCase): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type", "create", self.name, "256", "1", - "120", self.flavorid], stdout=fnull) + "10", self.flavorid], stdout=fnull) self.assertEqual(0, retcode) retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "delete", self.name], stdout=fnull) @@ -67,16 +66,14 @@ class NovaManageTestCase(test.TestCase): retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "create", self.name, "256", "0",\ "120", self.flavorid], stdout=fnull) - # self.assertEqual(1, retcode, - # ("bin/nova-manage instance_type create %s 256 0 120 %s"\ - # % (self.name, self.flavorid))) + self.assertEqual(1, retcode) def test_should_fail_on_duplicate_flavorid(self): fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "create", self.name, "256", "1",\ "120", "1"], stdout=fnull) - self.assertEqual(1, retcode) + self.assertEqual(3, retcode) def test_should_fail_on_duplicate_name(self): fnull = open(os.devnull, 'w') @@ -87,10 +84,10 @@ class NovaManageTestCase(test.TestCase): retcode = subprocess.call(["bin/nova-manage", "instance_type",\ "create", "fsfsfsdfsdf", "256", "1",\ "120", self.flavorid], stdout=fnull) - self.assertEqual(1, retcode) + self.assertEqual(3, retcode) - # def test_instance_type_delete_should_fail_without_valid_name(self): - # fnull = open(os.devnull, 'w') - # retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - # "delete", "saefasff"], stdout=fnull) - # self.assertEqual(1, retcode) + def test_instance_type_delete_should_fail_without_valid_name(self): + fnull = open(os.devnull, 'w') + retcode = subprocess.call(["bin/nova-manage", "instance_type",\ + "delete", "doesntexist"], stdout=fnull) + self.assertEqual(1, retcode) -- cgit From b03d6f523a0dda7c942c298ac75bc46331085056 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 11 Feb 2011 14:06:33 -0800 Subject: added instance_type_purge() to actually remove records from db --- nova/db/api.py | 7 +++++++ nova/db/sqlalchemy/api.py | 15 +++++++++++++++ nova/tests/test_instance_types.py | 1 + 3 files changed, 23 insertions(+) diff --git a/nova/db/api.py b/nova/db/api.py index 69f577764..4e53a8eee 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1013,3 +1013,10 @@ def instance_type_get_by_flavor_id(context, id): def instance_type_destroy(context, name): """Delete a instance type""" return IMPL.instance_type_destroy(context, name) + + +def instance_type_purge(context, name): + """Purges (removes) an instance type from DB + Use instance_type_destroy for most cases + """ + return IMPL.instance_type_purge(context, name) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 323f9b965..a8ce22922 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2093,3 +2093,18 @@ def instance_type_destroy(context, name): raise exception.NotFound else: return instance_type_ref + + +@require_admin_context +def instance_type_purge(context, name): + """ Removes specific instance_type from DB + Usually instance_type_destroy should be used + """ + session = get_session() + instance_type_ref = session.query(models.InstanceTypes).\ + filter_by(name=name) + records = instance_type_ref.delete() + if records == 0: + raise exception.NotFound + else: + return instance_type_ref diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 68ca3b842..0d54cc283 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -54,6 +54,7 @@ class InstanceTypeTestCase(test.TestCase): self.assertEqual(1, instance_types.get_instance_type(self.name)["deleted"]) self.assertEqual(starting_inst_list, instance_types.get_all_types()) + db.instance_type_purge(context.get_admin_context(), self.name) def test_get_all_instance_types(self): """Ensures that all instance types can be retrieved""" -- cgit From f181051ac04084f2937438b61c988804fc2ef845 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 17:39:04 -0600 Subject: Cast to host --- nova/compute/manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 9e8361563..63632b538 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -411,7 +411,7 @@ class ComputeManager(manager.Manager): service = self.db.service_get_by_host_and_topic(context, instance_ref['host'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, - service['id']) + service['host']) rpc.cast(context, topic, { 'method': 'resize_instance', 'migration_id': migration_ref['id'], }) @@ -435,7 +435,7 @@ class ComputeManager(manager.Manager): service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_host'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, - service['id']) + service['host']) rpc.cast(context, topic, { 'method': 'finish_resize', 'migration_id': migration_ref['id'], }) -- cgit From c8521da3539d28dce56a914d89c43c4908e46e57 Mon Sep 17 00:00:00 2001 From: Brian Schott Date: Fri, 11 Feb 2011 19:03:45 -0500 Subject: fixed exceptions import from python migrate --- nova/db/sqlalchemy/migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py index 2a13c5466..acd03ba34 100644 --- a/nova/db/sqlalchemy/migration.py +++ b/nova/db/sqlalchemy/migration.py @@ -22,7 +22,7 @@ from nova import flags import sqlalchemy from migrate.versioning import api as versioning_api -from migrate.versioning import exceptions as versioning_exceptions +from migrate import exceptions as versioning_exceptions FLAGS = flags.FLAGS -- cgit From 66365ece306023c1cf848d452d5af2c418e4e14c Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 18:04:00 -0600 Subject: More typos --- nova/compute/manager.py | 12 ++++++++++-- nova/db/sqlalchemy/api.py | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 63632b538..9aa163b73 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -414,7 +414,11 @@ class ComputeManager(manager.Manager): service['host']) rpc.cast(context, topic, { 'method': 'resize_instance', - 'migration_id': migration_ref['id'], }) + 'args': { + 'migration_id': migration_ref['id'], + 'instance_id': instance_id, + }, + }) @exception.wrap_exception @checks_instance_lock @@ -438,7 +442,11 @@ class ComputeManager(manager.Manager): service['host']) rpc.cast(context, topic, { 'method': 'finish_resize', - 'migration_id': migration_ref['id'], }) + 'args': { + 'migration_id': migration_ref['id'], + 'instance_id': instance_id, + }, + }) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index a6be844f8..af343bc56 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1941,7 +1941,7 @@ def migration_update(context, migration_id, values): @require_admin_context def migration_get(context, migration_id): session = get_session() - result = session.query(models.Migration.\ + result = session.query(models.Migration).\ filter_by(migration_id=migration_id)).first() if not result: raise exception.NotFound(_("No migration found with id %s") @@ -1952,7 +1952,7 @@ def migration_get(context, migration_id): @require_admin_context def migration_get_by_instance(context, instance_id): session = get_session() - result = session.query(models.Migration.\ + result = session.query(models.Migration).\ filter_by(instance_id=instance_id)).first() if not result: raise exception.NotFound(_("No migration found with instance id %s") -- cgit From 384a5aff50926784590ad66b92919b4d0408319d Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 18:05:02 -0600 Subject: More typos --- nova/db/sqlalchemy/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index af343bc56..6d52790a5 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1942,7 +1942,7 @@ def migration_update(context, migration_id, values): def migration_get(context, migration_id): session = get_session() result = session.query(models.Migration).\ - filter_by(migration_id=migration_id)).first() + filter_by(migration_id=migration_id).first() if not result: raise exception.NotFound(_("No migration found with id %s") % migration_id) @@ -1953,7 +1953,7 @@ def migration_get(context, migration_id): def migration_get_by_instance(context, instance_id): session = get_session() result = session.query(models.Migration).\ - filter_by(instance_id=instance_id)).first() + filter_by(instance_id=instance_id).first() if not result: raise exception.NotFound(_("No migration found with instance id %s") % migration_id) -- cgit From f3b25fc06e3eff6f1b0e8fed4a0bf90612bf0230 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 18:07:34 -0600 Subject: More typos --- nova/compute/manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 9aa163b73..7c9e918fb 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -422,7 +422,7 @@ class ComputeManager(manager.Manager): @exception.wrap_exception @checks_instance_lock - def resize_instance(self, context, migration_id): + def resize_instance(self, context, instance_id, migration_id): """Starts the migration of a running instance to another host""" migration_ref = self.db.migration_get(context, migration_id) self.db.migration_update(context, migration_id, @@ -443,14 +443,14 @@ class ComputeManager(manager.Manager): rpc.cast(context, topic, { 'method': 'finish_resize', 'args': { - 'migration_id': migration_ref['id'], + 'migration_id': migration_id, 'instance_id': instance_id, }, }) @exception.wrap_exception @checks_instance_lock - def finish_resize(self, context, migration_id): + def finish_resize(self, context, instance_id, migration_id): """Completes the migration process by setting up the newly transferred disk and turning on the instance on its new host machine""" migration_ref = self.db.migration_get(context, migration_id) -- cgit From 520b1b50bc2b1d039ad2f89d791bba21b7a35f05 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 18:09:11 -0600 Subject: More typos --- nova/db/sqlalchemy/api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 6d52790a5..8566bb91f 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1930,19 +1930,19 @@ def migration_create(context, values): @require_admin_context -def migration_update(context, migration_id, values): +def migration_update(context, id, values): session = get_session() with session.begin(): - migration = migration_get(context, migration_id, session=session) + migration = migration_get(context, id, session=session) migration.update(values) return migration @require_admin_context -def migration_get(context, migration_id): +def migration_get(context, id): session = get_session() result = session.query(models.Migration).\ - filter_by(migration_id=migration_id).first() + filter_by(id=id).first() if not result: raise exception.NotFound(_("No migration found with id %s") % migration_id) -- cgit From 252ebfe9a039fb883e3e88eda8feafae037e750e Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 11 Feb 2011 18:12:18 -0600 Subject: More typos --- nova/db/api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nova/db/api.py b/nova/db/api.py index 03232385c..887f57885 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -259,6 +259,9 @@ def floating_ip_get_by_address(context, address): #################### +def migration_update(context, id, values): + """Update a migration instance""" + return IMPL.migration_update(context, id, values) def migration_create(context, values): """Create a migration record""" -- cgit From ae70e05c0dd0e703da0826e4d7087cef3283af56 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Sat, 12 Feb 2011 05:37:22 +0000 Subject: Launchpad automatic translations update. --- locale/zh_CN.po | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/locale/zh_CN.po b/locale/zh_CN.po index 6bc231e50..bc82115ae 100644 --- a/locale/zh_CN.po +++ b/locale/zh_CN.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-01-10 11:25-0800\n" -"PO-Revision-Date: 2011-01-22 03:11+0000\n" -"Last-Translator: combo \n" +"PO-Revision-Date: 2011-02-12 05:05+0000\n" +"Last-Translator: Winston Dillon \n" "Language-Team: Chinese (Simplified) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-05 05:36+0000\n" -"X-Generator: Launchpad (build 12177)\n" +"X-Launchpad-Export-Date: 2011-02-12 05:37+0000\n" +"X-Generator: Launchpad (build 12351)\n" #: nova/twistd.py:268 #, python-format @@ -315,7 +315,7 @@ msgstr "键为: %s\t\t值为: %s" #: nova/api/ec2/__init__.py:301 #, python-format msgid "Unauthorized request for controller=%s and action=%s" -msgstr "对于控制器=%s和执行=%s的请求,未审核" +msgstr "对控制器=%s及动作=%s未经授权" #: nova/api/ec2/__init__.py:339 #, python-format @@ -330,7 +330,7 @@ msgstr "引发了Api错误: %s" #: nova/api/ec2/__init__.py:349 #, python-format msgid "Unexpected error raised: %s" -msgstr "引发了未知的错误: %s" +msgstr "引发了意外的错误:%s" #: nova/api/ec2/__init__.py:354 msgid "An unknown error has occurred. Please try your request again." @@ -349,7 +349,7 @@ msgstr "删除用户: %s" #: nova/api/ec2/admin.py:114 #, python-format msgid "Adding role %s to user %s for project %s" -msgstr "增加角色 %s给用户 %s,在工程 %s中" +msgstr "正将%s角色赋予用户%s(在工程%s中)" #: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 #, python-format @@ -359,12 +359,12 @@ msgstr "增加站点范围的 %s角色给用户 %s" #: nova/api/ec2/admin.py:122 #, python-format msgid "Removing role %s from user %s for project %s" -msgstr "移除角色 %s从用户 %s中,在工程 %s" +msgstr "正将角色%s从用户%s在工程%s中移除" #: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 #, python-format msgid "Removing sitewide role %s from user %s" -msgstr "移除站点范围的 %s角色从用户 %s中" +msgstr "" #: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 msgid "operation must be add or remove" @@ -393,7 +393,7 @@ msgstr "增加用户%s到%s工程" #: nova/api/ec2/admin.py:188 #, python-format msgid "Removing user %s from project %s" -msgstr "移除用户%s从工程%s中" +msgstr "正将用户%s从工程%s中移除" #: nova/api/ec2/apirequest.py:95 #, python-format @@ -403,7 +403,7 @@ msgstr "不支持的API请求: 控制器 = %s,执行 = %s" #: nova/api/ec2/cloud.py:117 #, python-format msgid "Generating root CA: %s" -msgstr "" +msgstr "生成根证书: %s" #: nova/api/ec2/cloud.py:277 #, python-format -- cgit From 635f6c9fb2bc3a09fac43832d1e57913ce893714 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz Date: Sat, 12 Feb 2011 21:32:58 +0100 Subject: Preliminary fix for issue, need more thorough testing before pushing to lp --- nova/api/ec2/cloud.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 4dc074fd5..1d89b58cf 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -324,12 +324,11 @@ class CloudController(object): groups = db.security_group_get_by_project(context, context.project_id) groups = [self._format_security_group(context, g) for g in groups] - LOG.debug(_("Groups after format_security_group: %s"), - groups, context=context) if not group_name is None: groups = [g for g in groups if g.name in group_name] - return {'securityGroupInfo': groups} + return {'securityGroupInfo': + sorted(groups, key=lambda k: k['ownerId'])} def _format_security_group(self, context, group): g = {} -- cgit From 556e8a585cf3a344f0ecf386e6195e6e61848071 Mon Sep 17 00:00:00 2001 From: termie Date: Sat, 12 Feb 2011 13:30:31 -0800 Subject: fix :returns: and add pep-0257 --- HACKING | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/HACKING b/HACKING index 4e0dfc835..e58d60e58 100644 --- a/HACKING +++ b/HACKING @@ -57,8 +57,12 @@ Docstrings parameters and return types use the Sphinx format. After that an extra newline then close the quotations. + When writing the docstring for a class, an extra line should be placed + after the closing quotations. For more in-depth explanations for these + decisions see http://www.python.org/dev/peps/pep-0257/ + :param foo: the foo parameter :param bar: the bar parameter - :rtype: the return type + :returns: description of the return value """ -- cgit From 6f6a09216458ffdba17d1960bbad723a3e71e7b2 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Sat, 12 Feb 2011 18:33:49 -0800 Subject: First attempt to make all image services use similar schemas --- nova/api/ec2/cloud.py | 16 +++++++++++++++- nova/image/s3.py | 22 +++++++++++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 00d044e95..f1d3ecdd3 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -836,11 +836,25 @@ class CloudController(object): self.compute_api.update(context, instance_id=instance_id, **kwargs) return True + def _format_image(self, context, image): + i = {} + i['imageId'] = image.get('id') + i['kernelId'] = image.get('kernel_id') + i['ramdiskId'] = image.get('ramdisk_id') + i['imageLocation'] = image.get('image_location') + i['imageOwnerId'] = image.get('image_owner_id') + i['imageState'] = image.get('image_state') + i['type'] = image.get('type') + i['isPublic'] = image.get('is_public') + i['architecture'] = image.get('architecture') + return i + def describe_images(self, context, image_id=None, **kwargs): - # Note: image_id is a list! + # NOTE: image_id is a list! images = self.image_service.index(context) if image_id: images = filter(lambda x: x['imageId'] in image_id, images) + images = [self._format_image(context, i) for i in images] return {'imagesSet': images} def deregister_image(self, context, image_id, **kwargs): diff --git a/nova/image/s3.py b/nova/image/s3.py index 08a40f191..30a9e2841 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -65,19 +65,27 @@ class S3ImageService(service.BaseImageService): 'image_id': image_id})) return image_id - def _fix_image_id(self, images): - """S3 has imageId but OpenStack wants id""" - for image in images: - if 'imageId' in image: - image['id'] = image['imageId'] - return images + def _format_image(self, image): + """Convert from S3 format to format defined by BaseImageService.""" + i = {} + i['id'] = image.get('imageId') + i['kernel_id'] = image.get('kernelId') + i['ramdisk_id'] = image.get('ramdiskId') + i['image_location'] = image.get('imageLocation') + i['image_owner_id'] = image.get('imageOwnerId') + i['image_state'] = image.get('imageState') + i['type'] = image.get('type') + i['is_public'] = image.get('isPublic') + i['architecture'] = image.get('architecture') + return i def index(self, context): """Return a list of all images that a user can see.""" response = self._conn(context).make_request( method='GET', bucket='_images') - return self._fix_image_id(json.loads(response.read())) + images = json.loads(response.read()) + return [self._format_image(i) for i in images] def show(self, context, image_id): """return a image object if the context has permissions""" -- cgit From 07bff0f397b3b6463532f709daeb7a36ed4ad5b1 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Sat, 12 Feb 2011 20:58:22 -0800 Subject: Made a few tweaks to format of S3 service implementation --- nova/api/ec2/cloud.py | 8 ++++---- nova/image/s3.py | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index b16503589..cc41ed4ae 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -844,9 +844,9 @@ class CloudController(object): i['imageId'] = image.get('id') i['kernelId'] = image.get('kernel_id') i['ramdiskId'] = image.get('ramdisk_id') - i['imageLocation'] = image.get('image_location') - i['imageOwnerId'] = image.get('image_owner_id') - i['imageState'] = image.get('image_state') + i['imageOwnerId'] = image.get('owner_id') + i['imageLocation'] = image.get('location') + i['imageState'] = image.get('status') i['type'] = image.get('type') i['isPublic'] = image.get('is_public') i['architecture'] = image.get('architecture') @@ -856,7 +856,7 @@ class CloudController(object): # NOTE: image_id is a list! images = self.image_service.index(context) if image_id: - images = filter(lambda x: x['imageId'] in image_id, images) + images = filter(lambda x: x['id'] in image_id, images) images = [self._format_image(context, i) for i in images] return {'imagesSet': images} diff --git a/nova/image/s3.py b/nova/image/s3.py index 30a9e2841..ee6b7f0d0 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -69,11 +69,12 @@ class S3ImageService(service.BaseImageService): """Convert from S3 format to format defined by BaseImageService.""" i = {} i['id'] = image.get('imageId') + i['name'] = image.get('imageId') i['kernel_id'] = image.get('kernelId') i['ramdisk_id'] = image.get('ramdiskId') - i['image_location'] = image.get('imageLocation') - i['image_owner_id'] = image.get('imageOwnerId') - i['image_state'] = image.get('imageState') + i['location'] = image.get('imageLocation') + i['owner_id'] = image.get('imageOwnerId') + i['status'] = image.get('imageState') i['type'] = image.get('type') i['is_public'] = image.get('isPublic') i['architecture'] = image.get('architecture') -- cgit From b554867a3ff9dd67bb528c0731f14b6730a28cf4 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Sun, 13 Feb 2011 05:09:17 +0000 Subject: Launchpad automatic translations update. --- locale/zh_CN.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locale/zh_CN.po b/locale/zh_CN.po index bc82115ae..64e051a62 100644 --- a/locale/zh_CN.po +++ b/locale/zh_CN.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-12 05:37+0000\n" +"X-Launchpad-Export-Date: 2011-02-13 05:09+0000\n" "X-Generator: Launchpad (build 12351)\n" #: nova/twistd.py:268 -- cgit From abe00772e1b8eef361ff2ce614a4679603438228 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz Date: Sun, 13 Feb 2011 19:34:20 +0100 Subject: now sorting by project, then by group --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 1d89b58cf..41a7ac706 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -328,7 +328,7 @@ class CloudController(object): groups = [g for g in groups if g.name in group_name] return {'securityGroupInfo': - sorted(groups, key=lambda k: k['ownerId'])} + sorted(groups, key=lambda k: (k['ownerId'], k['groupName']))} def _format_security_group(self, context, group): g = {} -- cgit From 9f3d269c9b5b610846acbf4ba2bf6719577b199a Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 13 Feb 2011 10:45:20 -0800 Subject: fix typo in auth checking for describe_availability_zones --- nova/api/ec2/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index ddcdc673c..1a06b3f01 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -21,7 +21,6 @@ Starting point for routing EC2 requests. """ import datetime -import routes import webob import webob.dec import webob.exc @@ -233,7 +232,7 @@ class Authorizer(wsgi.Middleware): super(Authorizer, self).__init__(application) self.action_roles = { 'CloudController': { - 'DescribeAvailabilityzones': ['all'], + 'DescribeAvailabilityZones': ['all'], 'DescribeRegions': ['all'], 'DescribeSnapshots': ['all'], 'DescribeKeyPairs': ['all'], -- cgit From 27139d33ec027bae4b247c1651bfe635a32f5ebf Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Sun, 13 Feb 2011 11:55:50 -0800 Subject: Added missing doc string and made a few style tweaks --- nova/api/ec2/cloud.py | 1 + nova/image/s3.py | 33 +++++++++++++++++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index cc41ed4ae..47cd8bf37 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -840,6 +840,7 @@ class CloudController(object): return True def _format_image(self, context, image): + """Convert from format defined by BaseImageService to S3 format.""" i = {} i['imageId'] = image.get('id') i['kernelId'] = image.get('kernel_id') diff --git a/nova/image/s3.py b/nova/image/s3.py index ee6b7f0d0..71304cdd6 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -36,6 +36,22 @@ from nova.image import service FLAGS = flags.FLAGS +def map_s3_to_base(image): + """Convert from S3 format to format defined by BaseImageService.""" + i = {} + i['id'] = image.get('imageId') + i['name'] = image.get('imageId') + i['kernel_id'] = image.get('kernelId') + i['ramdisk_id'] = image.get('ramdiskId') + i['location'] = image.get('imageLocation') + i['owner_id'] = image.get('imageOwnerId') + i['status'] = image.get('imageState') + i['type'] = image.get('type') + i['is_public'] = image.get('isPublic') + i['architecture'] = image.get('architecture') + return i + + class S3ImageService(service.BaseImageService): def modify(self, context, image_id, operation): @@ -65,28 +81,13 @@ class S3ImageService(service.BaseImageService): 'image_id': image_id})) return image_id - def _format_image(self, image): - """Convert from S3 format to format defined by BaseImageService.""" - i = {} - i['id'] = image.get('imageId') - i['name'] = image.get('imageId') - i['kernel_id'] = image.get('kernelId') - i['ramdisk_id'] = image.get('ramdiskId') - i['location'] = image.get('imageLocation') - i['owner_id'] = image.get('imageOwnerId') - i['status'] = image.get('imageState') - i['type'] = image.get('type') - i['is_public'] = image.get('isPublic') - i['architecture'] = image.get('architecture') - return i - def index(self, context): """Return a list of all images that a user can see.""" response = self._conn(context).make_request( method='GET', bucket='_images') images = json.loads(response.read()) - return [self._format_image(i) for i in images] + return [map_s3_to_base(i) for i in images] def show(self, context, image_id): """return a image object if the context has permissions""" -- cgit From 7713cb99635f3a06e298d4a504dc1249e5bf3232 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 13 Feb 2011 15:44:41 -0800 Subject: return a list instead of a generator from describe_groups --- nova/api/ec2/cloud.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 41a7ac706..5d387d45d 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -328,7 +328,8 @@ class CloudController(object): groups = [g for g in groups if g.name in group_name] return {'securityGroupInfo': - sorted(groups, key=lambda k: (k['ownerId'], k['groupName']))} + list(sorted(groups, + key=lambda k: (k['ownerId'], k['groupName'])))} def _format_security_group(self, context, group): g = {} -- cgit From cad2e12da52c235f2b97a17a9151296861830901 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Mon, 14 Feb 2011 05:22:52 +0000 Subject: Launchpad automatic translations update. --- locale/zh_CN.po | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/locale/zh_CN.po b/locale/zh_CN.po index 64e051a62..01b8dc378 100644 --- a/locale/zh_CN.po +++ b/locale/zh_CN.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: nova\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-01-10 11:25-0800\n" -"PO-Revision-Date: 2011-02-12 05:05+0000\n" +"PO-Revision-Date: 2011-02-14 02:26+0000\n" "Last-Translator: Winston Dillon \n" "Language-Team: Chinese (Simplified) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-13 05:09+0000\n" +"X-Launchpad-Export-Date: 2011-02-14 05:22+0000\n" "X-Generator: Launchpad (build 12351)\n" #: nova/twistd.py:268 @@ -413,50 +413,50 @@ msgstr "创建键值对 %s" #: nova/api/ec2/cloud.py:285 #, python-format msgid "Delete key pair %s" -msgstr "" +msgstr "删除键值对 %s" #: nova/api/ec2/cloud.py:357 #, python-format msgid "%s is not a valid ipProtocol" -msgstr "" +msgstr "%s是无效的IP协议" #: nova/api/ec2/cloud.py:361 msgid "Invalid port range" -msgstr "" +msgstr "端口范围无效" #: nova/api/ec2/cloud.py:392 #, python-format msgid "Revoke security group ingress %s" -msgstr "" +msgstr "撤销输入安全组 %s" #: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 msgid "No rule for the specified parameters." -msgstr "" +msgstr "对给定的参数无特定规则。" #: nova/api/ec2/cloud.py:421 #, python-format msgid "Authorize security group ingress %s" -msgstr "" +msgstr "验证输入安全组 %s" #: nova/api/ec2/cloud.py:432 #, python-format msgid "This rule already exists in group %s" -msgstr "" +msgstr "这条规则已经存在安全组%s中。" #: nova/api/ec2/cloud.py:460 #, python-format msgid "Create Security Group %s" -msgstr "" +msgstr "创建安全组%s" #: nova/api/ec2/cloud.py:463 #, python-format msgid "group %s already exists" -msgstr "" +msgstr "安全组%s已经存在" #: nova/api/ec2/cloud.py:475 #, python-format msgid "Delete security group %s" -msgstr "" +msgstr "删除安全组 %s" #: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 #, python-format -- cgit From 17cca3c90f203a023d921e7b01a667e68ef695b5 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 13 Feb 2011 21:48:14 -0800 Subject: re-add input_chain because it got deleted at some point --- nova/network/linux_net.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index ed37e8ba7..c1cbff7d8 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -54,6 +54,8 @@ flags.DEFINE_string('routing_source_ip', '$my_ip', 'Public IP of network host') flags.DEFINE_bool('use_nova_chains', False, 'use the nova_ routing chains instead of default') +flags.DEFINE_string('input_chain', 'INPUT', + 'chain to add nova_input to') flags.DEFINE_string('dns_server', None, 'if set, uses specific dns server for dnsmasq') -- cgit From 64a2a487d8ad524c0e948545a2318cebfefb36fe Mon Sep 17 00:00:00 2001 From: Vasiliy Shlykov Date: Mon, 14 Feb 2011 16:02:58 +0300 Subject: Fixed tables creation order and added clearing db after errors. --- nova/db/sqlalchemy/migrate_repo/versions/001_austin.py | 14 ++++++++------ nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py | 7 +++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py b/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py index 366944591..9e7ab3554 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py @@ -508,17 +508,19 @@ def upgrade(migrate_engine): # bind migrate_engine to your metadata meta.bind = migrate_engine - for table in (auth_tokens, export_devices, fixed_ips, floating_ips, - instances, key_pairs, networks, - projects, quotas, security_groups, security_group_inst_assoc, - security_group_rules, services, users, - user_project_association, user_project_role_association, - user_role_association, volumes): + tables = [auth_tokens, + instances, key_pairs, networks, fixed_ips, floating_ips, + quotas, security_groups, security_group_inst_assoc, + security_group_rules, services, users, projects, + user_project_association, user_project_role_association, + user_role_association, volumes, export_devices] + for table in tables: try: table.create() except Exception: logging.info(repr(table)) logging.exception('Exception while creating table') + meta.drop_all(tables=tables) raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py index 699b837f8..413536a59 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py @@ -209,13 +209,16 @@ def upgrade(migrate_engine): # Upgrade operations go here. Don't create your own engine; # bind migrate_engine to your metadata meta.bind = migrate_engine - for table in (certificates, consoles, console_pools, instance_actions, - iscsi_targets): + + tables = [certificates, console_pools, consoles, instance_actions, + iscsi_targets] + for table in tables: try: table.create() except Exception: logging.info(repr(table)) logging.exception('Exception while creating table') + meta.drop_all(tables=tables) raise auth_tokens.c.user_id.alter(type=String(length=255, -- cgit From 8001f334221a16d8328289f6954ef549844f76f3 Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Mon, 14 Feb 2011 14:26:32 +0100 Subject: Fix DescribeRegion answer by using specific 'listen' configuration parameter instead of overloading ec2_host --- bin/nova-api | 4 ++-- bin/nova-combined | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/nova-api b/bin/nova-api index 11176a021..eb59d0191 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -59,12 +59,12 @@ def run_app(paste_config_file): LOG.debug(_("App Config: %(api)s\n%(config)r") % locals()) wsgi.paste_config_to_flags(config, { "verbose": FLAGS.verbose, - "%s_host" % api: config.get('host', '0.0.0.0'), + "%s_host" % api: getattr(FLAGS, "%s_host" % api), "%s_port" % api: getattr(FLAGS, "%s_port" % api)}) LOG.info(_("Running %s API"), api) app = wsgi.load_paste_app(paste_config_file, api) apps.append((app, getattr(FLAGS, "%s_port" % api), - getattr(FLAGS, "%s_host" % api))) + config.get('listen', '0.0.0.0'))) if len(apps) == 0: LOG.error(_("No known API applications configured in %s."), paste_config_file) diff --git a/bin/nova-combined b/bin/nova-combined index 913c866bf..889600eb7 100755 --- a/bin/nova-combined +++ b/bin/nova-combined @@ -67,11 +67,11 @@ if __name__ == '__main__': continue wsgi.paste_config_to_flags(config, { "verbose": FLAGS.verbose, - "%s_host" % api: config.get('host', '0.0.0.0'), + "%s_host" % api: getattr(FLAGS, "%s_host" % api), "%s_port" % api: getattr(FLAGS, "%s_port" % api)}) app = wsgi.load_paste_app(paste_config_file, api) apps.append((app, getattr(FLAGS, "%s_port" % api), - getattr(FLAGS, "%s_host" % api))) + config.get('listen', '0.0.0.0'))) if len(apps) > 0: logging.basicConfig() server = wsgi.Server() -- cgit From 3e412a5f34c6dae44d8f4d6bce030fb267aa5aea Mon Sep 17 00:00:00 2001 From: "jaypipes@gmail.com" <> Date: Mon, 14 Feb 2011 11:04:45 -0500 Subject: Merge Distutils.Extra changes for automating translation message catalog compilation --- babel.cfg | 2 - locale/nova.pot | 2130 ------------------------------------------- po/nova.pot | 2705 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 12 +- 4 files changed, 2715 insertions(+), 2134 deletions(-) delete mode 100644 babel.cfg delete mode 100644 locale/nova.pot create mode 100644 po/nova.pot diff --git a/babel.cfg b/babel.cfg deleted file mode 100644 index 15cd6cb76..000000000 --- a/babel.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[python: **.py] - diff --git a/locale/nova.pot b/locale/nova.pot deleted file mode 100644 index a96411e33..000000000 --- a/locale/nova.pot +++ /dev/null @@ -1,2130 +0,0 @@ -# Translations template for nova. -# Copyright (C) 2011 ORGANIZATION -# This file is distributed under the same license as the nova project. -# FIRST AUTHOR , 2011. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: nova 2011.1\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2011-01-10 11:25-0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" - -#: nova/crypto.py:46 -msgid "Filename of root CA" -msgstr "" - -#: nova/crypto.py:49 -msgid "Filename of private key" -msgstr "" - -#: nova/crypto.py:51 -msgid "Filename of root Certificate Revokation List" -msgstr "" - -#: nova/crypto.py:53 -msgid "Where we keep our keys" -msgstr "" - -#: nova/crypto.py:55 -msgid "Where we keep our root CA" -msgstr "" - -#: nova/crypto.py:57 -msgid "Should we use a CA for each project?" -msgstr "" - -#: nova/crypto.py:61 -#, python-format -msgid "Subject for certificate for users, %s for project, user, timestamp" -msgstr "" - -#: nova/crypto.py:66 -#, python-format -msgid "Subject for certificate for projects, %s for project, timestamp" -msgstr "" - -#: nova/crypto.py:71 -#, python-format -msgid "Subject for certificate for vpns, %s for project, timestamp" -msgstr "" - -#: nova/crypto.py:258 -#, python-format -msgid "Flags path: %s" -msgstr "" - -#: nova/exception.py:33 -msgid "Unexpected error while running command." -msgstr "" - -#: nova/exception.py:36 -#, python-format -msgid "" -"%s\n" -"Command: %s\n" -"Exit code: %s\n" -"Stdout: %r\n" -"Stderr: %r" -msgstr "" - -#: nova/exception.py:86 -msgid "Uncaught exception" -msgstr "" - -#: nova/fakerabbit.py:48 -#, python-format -msgid "(%s) publish (key: %s) %s" -msgstr "" - -#: nova/fakerabbit.py:53 -#, python-format -msgid "Publishing to route %s" -msgstr "" - -#: nova/fakerabbit.py:83 -#, python-format -msgid "Declaring queue %s" -msgstr "" - -#: nova/fakerabbit.py:89 -#, python-format -msgid "Declaring exchange %s" -msgstr "" - -#: nova/fakerabbit.py:95 -#, python-format -msgid "Binding %s to %s with key %s" -msgstr "" - -#: nova/fakerabbit.py:120 -#, python-format -msgid "Getting from %s: %s" -msgstr "" - -#: nova/rpc.py:92 -#, python-format -msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds." -msgstr "" - -#: nova/rpc.py:99 -#, python-format -msgid "Unable to connect to AMQP server after %d tries. Shutting down." -msgstr "" - -#: nova/rpc.py:118 -msgid "Reconnected to queue" -msgstr "" - -#: nova/rpc.py:125 -msgid "Failed to fetch message from queue" -msgstr "" - -#: nova/rpc.py:155 -#, python-format -msgid "Initing the Adapter Consumer for %s" -msgstr "" - -#: nova/rpc.py:170 -#, python-format -msgid "received %s" -msgstr "" - -#: nova/rpc.py:183 -#, python-format -msgid "no method for message: %s" -msgstr "" - -#: nova/rpc.py:184 -#, python-format -msgid "No method for message: %s" -msgstr "" - -#: nova/rpc.py:245 -#, python-format -msgid "Returning exception %s to caller" -msgstr "" - -#: nova/rpc.py:286 -#, python-format -msgid "unpacked context: %s" -msgstr "" - -#: nova/rpc.py:305 -msgid "Making asynchronous call..." -msgstr "" - -#: nova/rpc.py:308 -#, python-format -msgid "MSG_ID is %s" -msgstr "" - -#: nova/rpc.py:356 -#, python-format -msgid "response %s" -msgstr "" - -#: nova/rpc.py:365 -#, python-format -msgid "topic is %s" -msgstr "" - -#: nova/rpc.py:366 -#, python-format -msgid "message %s" -msgstr "" - -#: nova/service.py:157 -#, python-format -msgid "Starting %s node" -msgstr "" - -#: nova/service.py:169 -msgid "Service killed that has no database entry" -msgstr "" - -#: nova/service.py:190 -msgid "The service database object disappeared, Recreating it." -msgstr "" - -#: nova/service.py:202 -msgid "Recovered model server connection!" -msgstr "" - -#: nova/service.py:208 -msgid "model server went away" -msgstr "" - -#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43 -#, python-format -msgid "Data store %s is unreachable. Trying again in %d seconds." -msgstr "" - -#: nova/service.py:232 nova/twistd.py:232 -#, python-format -msgid "Serving %s" -msgstr "" - -#: nova/service.py:234 nova/twistd.py:264 -msgid "Full set of FLAGS:" -msgstr "" - -#: nova/twistd.py:211 -#, python-format -msgid "pidfile %s does not exist. Daemon not running?\n" -msgstr "" - -#: nova/twistd.py:268 -#, python-format -msgid "Starting %s" -msgstr "" - -#: nova/utils.py:53 -#, python-format -msgid "Inner Exception: %s" -msgstr "" - -#: nova/utils.py:54 -#, python-format -msgid "Class %s cannot be found" -msgstr "" - -#: nova/utils.py:113 -#, python-format -msgid "Fetching %s" -msgstr "" - -#: nova/utils.py:125 -#, python-format -msgid "Running cmd (subprocess): %s" -msgstr "" - -#: nova/utils.py:138 -#, python-format -msgid "Result was %s" -msgstr "" - -#: nova/utils.py:171 -#, python-format -msgid "debug in callback: %s" -msgstr "" - -#: nova/utils.py:176 -#, python-format -msgid "Running %s" -msgstr "" - -#: nova/utils.py:207 -#, python-format -msgid "Couldn't get IP, using 127.0.0.1 %s" -msgstr "" - -#: nova/utils.py:289 -#, python-format -msgid "Invalid backend: %s" -msgstr "" - -#: nova/utils.py:300 -#, python-format -msgid "backend %s" -msgstr "" - -#: nova/api/ec2/__init__.py:133 -msgid "Too many failed authentications." -msgstr "" - -#: nova/api/ec2/__init__.py:142 -#, python-format -msgid "" -"Access key %s has had %d failed authentications and will be locked out " -"for %d minutes." -msgstr "" - -#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140 -#, python-format -msgid "Authentication Failure: %s" -msgstr "" - -#: nova/api/ec2/__init__.py:190 -#, python-format -msgid "Authenticated Request For %s:%s)" -msgstr "" - -#: nova/api/ec2/__init__.py:227 -#, python-format -msgid "action: %s" -msgstr "" - -#: nova/api/ec2/__init__.py:229 -#, python-format -msgid "arg: %s\t\tval: %s" -msgstr "" - -#: nova/api/ec2/__init__.py:301 -#, python-format -msgid "Unauthorized request for controller=%s and action=%s" -msgstr "" - -#: nova/api/ec2/__init__.py:339 -#, python-format -msgid "NotFound raised: %s" -msgstr "" - -#: nova/api/ec2/__init__.py:342 -#, python-format -msgid "ApiError raised: %s" -msgstr "" - -#: nova/api/ec2/__init__.py:349 -#, python-format -msgid "Unexpected error raised: %s" -msgstr "" - -#: nova/api/ec2/__init__.py:354 -msgid "An unknown error has occurred. Please try your request again." -msgstr "" - -#: nova/api/ec2/admin.py:84 -#, python-format -msgid "Creating new user: %s" -msgstr "" - -#: nova/api/ec2/admin.py:92 -#, python-format -msgid "Deleting user: %s" -msgstr "" - -#: nova/api/ec2/admin.py:114 -#, python-format -msgid "Adding role %s to user %s for project %s" -msgstr "" - -#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415 -#, python-format -msgid "Adding sitewide role %s to user %s" -msgstr "" - -#: nova/api/ec2/admin.py:122 -#, python-format -msgid "Removing role %s from user %s for project %s" -msgstr "" - -#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441 -#, python-format -msgid "Removing sitewide role %s from user %s" -msgstr "" - -#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192 -msgid "operation must be add or remove" -msgstr "" - -#: nova/api/ec2/admin.py:142 -#, python-format -msgid "Getting x509 for user: %s on project: %s" -msgstr "" - -#: nova/api/ec2/admin.py:159 -#, python-format -msgid "Create project %s managed by %s" -msgstr "" - -#: nova/api/ec2/admin.py:170 -#, python-format -msgid "Delete project: %s" -msgstr "" - -#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533 -#, python-format -msgid "Adding user %s to project %s" -msgstr "" - -#: nova/api/ec2/admin.py:188 -#, python-format -msgid "Removing user %s from project %s" -msgstr "" - -#: nova/api/ec2/apirequest.py:95 -#, python-format -msgid "Unsupported API request: controller = %s,action = %s" -msgstr "" - -#: nova/api/ec2/cloud.py:117 -#, python-format -msgid "Generating root CA: %s" -msgstr "" - -#: nova/api/ec2/cloud.py:277 -#, python-format -msgid "Create key pair %s" -msgstr "" - -#: nova/api/ec2/cloud.py:285 -#, python-format -msgid "Delete key pair %s" -msgstr "" - -#: nova/api/ec2/cloud.py:357 -#, python-format -msgid "%s is not a valid ipProtocol" -msgstr "" - -#: nova/api/ec2/cloud.py:361 -msgid "Invalid port range" -msgstr "" - -#: nova/api/ec2/cloud.py:392 -#, python-format -msgid "Revoke security group ingress %s" -msgstr "" - -#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414 -msgid "No rule for the specified parameters." -msgstr "" - -#: nova/api/ec2/cloud.py:421 -#, python-format -msgid "Authorize security group ingress %s" -msgstr "" - -#: nova/api/ec2/cloud.py:432 -#, python-format -msgid "This rule already exists in group %s" -msgstr "" - -#: nova/api/ec2/cloud.py:460 -#, python-format -msgid "Create Security Group %s" -msgstr "" - -#: nova/api/ec2/cloud.py:463 -#, python-format -msgid "group %s already exists" -msgstr "" - -#: nova/api/ec2/cloud.py:475 -#, python-format -msgid "Delete security group %s" -msgstr "" - -#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452 -#, python-format -msgid "Get console output for instance %s" -msgstr "" - -#: nova/api/ec2/cloud.py:543 -#, python-format -msgid "Create volume of %s GB" -msgstr "" - -#: nova/api/ec2/cloud.py:567 -#, python-format -msgid "Attach volume %s to instacne %s at %s" -msgstr "" - -#: nova/api/ec2/cloud.py:579 -#, python-format -msgid "Detach volume %s" -msgstr "" - -#: nova/api/ec2/cloud.py:686 -msgid "Allocate address" -msgstr "" - -#: nova/api/ec2/cloud.py:691 -#, python-format -msgid "Release address %s" -msgstr "" - -#: nova/api/ec2/cloud.py:696 -#, python-format -msgid "Associate address %s to instance %s" -msgstr "" - -#: nova/api/ec2/cloud.py:703 -#, python-format -msgid "Disassociate address %s" -msgstr "" - -#: nova/api/ec2/cloud.py:730 -msgid "Going to start terminating instances" -msgstr "" - -#: nova/api/ec2/cloud.py:738 -#, python-format -msgid "Reboot instance %r" -msgstr "" - -#: nova/api/ec2/cloud.py:775 -#, python-format -msgid "De-registering image %s" -msgstr "" - -#: nova/api/ec2/cloud.py:783 -#, python-format -msgid "Registered image %s with id %s" -msgstr "" - -#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804 -#, python-format -msgid "attribute not supported: %s" -msgstr "" - -#: nova/api/ec2/cloud.py:794 -#, python-format -msgid "invalid id: %s" -msgstr "" - -#: nova/api/ec2/cloud.py:807 -msgid "user or group not specified" -msgstr "" - -#: nova/api/ec2/cloud.py:809 -msgid "only group \"all\" is supported" -msgstr "" - -#: nova/api/ec2/cloud.py:811 -msgid "operation_type must be add or remove" -msgstr "" - -#: nova/api/ec2/cloud.py:812 -#, python-format -msgid "Updating image %s publicity" -msgstr "" - -#: nova/api/ec2/metadatarequesthandler.py:75 -#, python-format -msgid "Failed to get metadata for ip: %s" -msgstr "" - -#: nova/api/openstack/__init__.py:70 -#, python-format -msgid "Caught error: %s" -msgstr "" - -#: nova/api/openstack/__init__.py:86 -msgid "Including admin operations in API." -msgstr "" - -#: nova/api/openstack/servers.py:184 -#, python-format -msgid "Compute.api::lock %s" -msgstr "" - -#: nova/api/openstack/servers.py:199 -#, python-format -msgid "Compute.api::unlock %s" -msgstr "" - -#: nova/api/openstack/servers.py:213 -#, python-format -msgid "Compute.api::get_lock %s" -msgstr "" - -#: nova/api/openstack/servers.py:224 -#, python-format -msgid "Compute.api::pause %s" -msgstr "" - -#: nova/api/openstack/servers.py:235 -#, python-format -msgid "Compute.api::unpause %s" -msgstr "" - -#: nova/api/openstack/servers.py:246 -#, python-format -msgid "compute.api::suspend %s" -msgstr "" - -#: nova/api/openstack/servers.py:257 -#, python-format -msgid "compute.api::resume %s" -msgstr "" - -#: nova/auth/dbdriver.py:84 -#, python-format -msgid "User %s already exists" -msgstr "" - -#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207 -#, python-format -msgid "Project can't be created because manager %s doesn't exist" -msgstr "" - -#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204 -#, python-format -msgid "Project can't be created because project %s already exists" -msgstr "" - -#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241 -#, python-format -msgid "Project can't be modified because manager %s doesn't exist" -msgstr "" - -#: nova/auth/dbdriver.py:245 -#, python-format -msgid "User \"%s\" not found" -msgstr "" - -#: nova/auth/dbdriver.py:248 -#, python-format -msgid "Project \"%s\" not found" -msgstr "" - -#: nova/auth/fakeldap.py:33 -msgid "Attempted to instantiate singleton" -msgstr "" - -#: nova/auth/ldapdriver.py:181 -#, python-format -msgid "LDAP object for %s doesn't exist" -msgstr "" - -#: nova/auth/ldapdriver.py:218 -#, python-format -msgid "Project can't be created because user %s doesn't exist" -msgstr "" - -#: nova/auth/ldapdriver.py:478 -#, python-format -msgid "User %s is already a member of the group %s" -msgstr "" - -#: nova/auth/ldapdriver.py:507 -#, python-format -msgid "" -"Attempted to remove the last member of a group. Deleting the group at %s " -"instead." -msgstr "" - -#: nova/auth/ldapdriver.py:528 -#, python-format -msgid "Group at dn %s doesn't exist" -msgstr "" - -#: nova/auth/manager.py:259 -#, python-format -msgid "Looking up user: %r" -msgstr "" - -#: nova/auth/manager.py:263 -#, python-format -msgid "Failed authorization for access key %s" -msgstr "" - -#: nova/auth/manager.py:264 -#, python-format -msgid "No user found for access key %s" -msgstr "" - -#: nova/auth/manager.py:270 -#, python-format -msgid "Using project name = user name (%s)" -msgstr "" - -#: nova/auth/manager.py:275 -#, python-format -msgid "failed authorization: no project named %s (user=%s)" -msgstr "" - -#: nova/auth/manager.py:277 -#, python-format -msgid "No project called %s could be found" -msgstr "" - -#: nova/auth/manager.py:281 -#, python-format -msgid "Failed authorization: user %s not admin and not member of project %s" -msgstr "" - -#: nova/auth/manager.py:283 -#, python-format -msgid "User %s is not a member of project %s" -msgstr "" - -#: nova/auth/manager.py:292 nova/auth/manager.py:303 -#, python-format -msgid "Invalid signature for user %s" -msgstr "" - -#: nova/auth/manager.py:293 nova/auth/manager.py:304 -msgid "Signature does not match" -msgstr "" - -#: nova/auth/manager.py:374 -msgid "Must specify project" -msgstr "" - -#: nova/auth/manager.py:408 -#, python-format -msgid "The %s role can not be found" -msgstr "" - -#: nova/auth/manager.py:410 -#, python-format -msgid "The %s role is global only" -msgstr "" - -#: nova/auth/manager.py:412 -#, python-format -msgid "Adding role %s to user %s in project %s" -msgstr "" - -#: nova/auth/manager.py:438 -#, python-format -msgid "Removing role %s from user %s on project %s" -msgstr "" - -#: nova/auth/manager.py:505 -#, python-format -msgid "Created project %s with manager %s" -msgstr "" - -#: nova/auth/manager.py:523 -#, python-format -msgid "modifying project %s" -msgstr "" - -#: nova/auth/manager.py:553 -#, python-format -msgid "Remove user %s from project %s" -msgstr "" - -#: nova/auth/manager.py:581 -#, python-format -msgid "Deleting project %s" -msgstr "" - -#: nova/auth/manager.py:637 -#, python-format -msgid "Created user %s (admin: %r)" -msgstr "" - -#: nova/auth/manager.py:645 -#, python-format -msgid "Deleting user %s" -msgstr "" - -#: nova/auth/manager.py:655 -#, python-format -msgid "Access Key change for user %s" -msgstr "" - -#: nova/auth/manager.py:657 -#, python-format -msgid "Secret Key change for user %s" -msgstr "" - -#: nova/auth/manager.py:659 -#, python-format -msgid "Admin status set to %r for user %s" -msgstr "" - -#: nova/auth/manager.py:708 -#, python-format -msgid "No vpn data for project %s" -msgstr "" - -#: nova/cloudpipe/pipelib.py:45 -msgid "Template for script to run on cloudpipe instance boot" -msgstr "" - -#: nova/cloudpipe/pipelib.py:48 -msgid "Network to push into openvpn config" -msgstr "" - -#: nova/cloudpipe/pipelib.py:51 -msgid "Netmask to push into openvpn config" -msgstr "" - -#: nova/cloudpipe/pipelib.py:97 -#, python-format -msgid "Launching VPN for %s" -msgstr "" - -#: nova/compute/api.py:67 -#, python-format -msgid "Instance %d was not found in get_network_topic" -msgstr "" - -#: nova/compute/api.py:73 -#, python-format -msgid "Instance %d has no host" -msgstr "" - -#: nova/compute/api.py:92 -#, python-format -msgid "Quota exceeeded for %s, tried to run %s instances" -msgstr "" - -#: nova/compute/api.py:94 -#, python-format -msgid "Instance quota exceeded. You can only run %s more instances of this type." -msgstr "" - -#: nova/compute/api.py:109 -msgid "Creating a raw instance" -msgstr "" - -#: nova/compute/api.py:156 -#, python-format -msgid "Going to run %s instances..." -msgstr "" - -#: nova/compute/api.py:180 -#, python-format -msgid "Casting to scheduler for %s/%s's instance %s" -msgstr "" - -#: nova/compute/api.py:279 -#, python-format -msgid "Going to try and terminate %s" -msgstr "" - -#: nova/compute/api.py:283 -#, python-format -msgid "Instance %d was not found during terminate" -msgstr "" - -#: nova/compute/api.py:288 -#, python-format -msgid "Instance %d is already being terminated" -msgstr "" - -#: nova/compute/api.py:450 -#, python-format -msgid "Invalid device specified: %s. Example device: /dev/vdb" -msgstr "" - -#: nova/compute/api.py:465 -msgid "Volume isn't attached to anything!" -msgstr "" - -#: nova/compute/disk.py:71 -#, python-format -msgid "Input partition size not evenly divisible by sector size: %d / %d" -msgstr "" - -#: nova/compute/disk.py:75 -#, python-format -msgid "Bytes for local storage not evenly divisible by sector size: %d / %d" -msgstr "" - -#: nova/compute/disk.py:128 -#, python-format -msgid "Could not attach image to loopback: %s" -msgstr "" - -#: nova/compute/disk.py:136 -#, python-format -msgid "Failed to load partition: %s" -msgstr "" - -#: nova/compute/disk.py:158 -#, python-format -msgid "Failed to mount filesystem: %s" -msgstr "" - -#: nova/compute/instance_types.py:41 -#, python-format -msgid "Unknown instance type: %s" -msgstr "" - -#: nova/compute/manager.py:69 -#, python-format -msgid "check_instance_lock: decorating: |%s|" -msgstr "" - -#: nova/compute/manager.py:71 -#, python-format -msgid "check_instance_lock: arguments: |%s| |%s| |%s|" -msgstr "" - -#: nova/compute/manager.py:75 -#, python-format -msgid "check_instance_lock: locked: |%s|" -msgstr "" - -#: nova/compute/manager.py:77 -#, python-format -msgid "check_instance_lock: admin: |%s|" -msgstr "" - -#: nova/compute/manager.py:82 -#, python-format -msgid "check_instance_lock: executing: |%s|" -msgstr "" - -#: nova/compute/manager.py:86 -#, python-format -msgid "check_instance_lock: not executing |%s|" -msgstr "" - -#: nova/compute/manager.py:157 -msgid "Instance has already been created" -msgstr "" - -#: nova/compute/manager.py:158 -#, python-format -msgid "instance %s: starting..." -msgstr "" - -#: nova/compute/manager.py:197 -#, python-format -msgid "instance %s: Failed to spawn" -msgstr "" - -#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228 -#, python-format -msgid "Terminating instance %s" -msgstr "" - -#: nova/compute/manager.py:217 -#, python-format -msgid "Disassociating address %s" -msgstr "" - -#: nova/compute/manager.py:230 -#, python-format -msgid "Deallocating address %s" -msgstr "" - -#: nova/compute/manager.py:243 -#, python-format -msgid "trying to destroy already destroyed instance: %s" -msgstr "" - -#: nova/compute/manager.py:257 -#, python-format -msgid "Rebooting instance %s" -msgstr "" - -#: nova/compute/manager.py:260 -#, python-format -msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)" -msgstr "" - -#: nova/compute/manager.py:286 -#, python-format -msgid "instance %s: snapshotting" -msgstr "" - -#: nova/compute/manager.py:289 -#, python-format -msgid "trying to snapshot a non-running instance: %s (state: %s excepted: %s)" -msgstr "" - -#: nova/compute/manager.py:301 -#, python-format -msgid "instance %s: rescuing" -msgstr "" - -#: nova/compute/manager.py:316 -#, python-format -msgid "instance %s: unrescuing" -msgstr "" - -#: nova/compute/manager.py:335 -#, python-format -msgid "instance %s: pausing" -msgstr "" - -#: nova/compute/manager.py:352 -#, python-format -msgid "instance %s: unpausing" -msgstr "" - -#: nova/compute/manager.py:369 -#, python-format -msgid "instance %s: retrieving diagnostics" -msgstr "" - -#: nova/compute/manager.py:382 -#, python-format -msgid "instance %s: suspending" -msgstr "" - -#: nova/compute/manager.py:401 -#, python-format -msgid "instance %s: resuming" -msgstr "" - -#: nova/compute/manager.py:420 -#, python-format -msgid "instance %s: locking" -msgstr "" - -#: nova/compute/manager.py:432 -#, python-format -msgid "instance %s: unlocking" -msgstr "" - -#: nova/compute/manager.py:442 -#, python-format -msgid "instance %s: getting locked state" -msgstr "" - -#: nova/compute/manager.py:462 -#, python-format -msgid "instance %s: attaching volume %s to %s" -msgstr "" - -#: nova/compute/manager.py:478 -#, python-format -msgid "instance %s: attach failed %s, removing" -msgstr "" - -#: nova/compute/manager.py:493 -#, python-format -msgid "Detach volume %s from mountpoint %s on instance %s" -msgstr "" - -#: nova/compute/manager.py:497 -#, python-format -msgid "Detaching volume from unknown instance %s" -msgstr "" - -#: nova/compute/monitor.py:259 -#, python-format -msgid "updating %s..." -msgstr "" - -#: nova/compute/monitor.py:289 -msgid "unexpected error during update" -msgstr "" - -#: nova/compute/monitor.py:355 -#, python-format -msgid "Cannot get blockstats for \"%s\" on \"%s\"" -msgstr "" - -#: nova/compute/monitor.py:377 -#, python-format -msgid "Cannot get ifstats for \"%s\" on \"%s\"" -msgstr "" - -#: nova/compute/monitor.py:412 -msgid "unexpected exception getting connection" -msgstr "" - -#: nova/compute/monitor.py:427 -#, python-format -msgid "Found instance: %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:43 -msgid "Use of empty request context is deprecated" -msgstr "" - -#: nova/db/sqlalchemy/api.py:132 -#, python-format -msgid "No service for id %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:229 -#, python-format -msgid "No service for %s, %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:574 -#, python-format -msgid "No floating ip for address %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:668 -#, python-format -msgid "No instance for id %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598 -#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103 -#, python-format -msgid "Instance %s not found" -msgstr "" - -#: nova/db/sqlalchemy/api.py:891 -#, python-format -msgid "no keypair for user %s, name %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064 -#, python-format -msgid "No network for id %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1036 -#, python-format -msgid "No network for bridge %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1050 -#, python-format -msgid "No network for instance %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1180 -#, python-format -msgid "Token %s does not exist" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1205 -#, python-format -msgid "No quota for project_id %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1356 -#, python-format -msgid "No volume for id %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1401 -#, python-format -msgid "Volume %s not found" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1413 -#, python-format -msgid "No export device found for volume %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1426 -#, python-format -msgid "No target id found for volume %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1471 -#, python-format -msgid "No security group with id %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1488 -#, python-format -msgid "No security group named %s for project: %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1576 -#, python-format -msgid "No secuity group rule with id %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1650 -#, python-format -msgid "No user for id %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1666 -#, python-format -msgid "No user for access key %s" -msgstr "" - -#: nova/db/sqlalchemy/api.py:1728 -#, python-format -msgid "No project with id %s" -msgstr "" - -#: nova/image/glance.py:78 -#, python-format -msgid "Parallax returned HTTP error %d from request for /images" -msgstr "" - -#: nova/image/glance.py:97 -#, python-format -msgid "Parallax returned HTTP error %d from request for /images/detail" -msgstr "" - -#: nova/image/s3.py:82 -#, python-format -msgid "Image %s could not be found" -msgstr "" - -#: nova/network/api.py:39 -#, python-format -msgid "Quota exceeeded for %s, tried to allocate address" -msgstr "" - -#: nova/network/api.py:42 -msgid "Address quota exceeded. You cannot allocate any more addresses" -msgstr "" - -#: nova/network/linux_net.py:176 -#, python-format -msgid "Starting VLAN inteface %s" -msgstr "" - -#: nova/network/linux_net.py:186 -#, python-format -msgid "Starting Bridge interface for %s" -msgstr "" - -#: nova/network/linux_net.py:254 -#, python-format -msgid "Hupping dnsmasq threw %s" -msgstr "" - -#: nova/network/linux_net.py:256 -#, python-format -msgid "Pid %d is stale, relaunching dnsmasq" -msgstr "" - -#: nova/network/linux_net.py:334 -#, python-format -msgid "Killing dnsmasq threw %s" -msgstr "" - -#: nova/network/manager.py:135 -msgid "setting network host" -msgstr "" - -#: nova/network/manager.py:190 -#, python-format -msgid "Leasing IP %s" -msgstr "" - -#: nova/network/manager.py:194 -#, python-format -msgid "IP %s leased that isn't associated" -msgstr "" - -#: nova/network/manager.py:197 -#, python-format -msgid "IP %s leased to bad mac %s vs %s" -msgstr "" - -#: nova/network/manager.py:205 -#, python-format -msgid "IP %s leased that was already deallocated" -msgstr "" - -#: nova/network/manager.py:214 -#, python-format -msgid "IP %s released that isn't associated" -msgstr "" - -#: nova/network/manager.py:217 -#, python-format -msgid "IP %s released from bad mac %s vs %s" -msgstr "" - -#: nova/network/manager.py:220 -#, python-format -msgid "IP %s released that was not leased" -msgstr "" - -#: nova/network/manager.py:442 -#, python-format -msgid "Dissassociated %s stale fixed ip(s)" -msgstr "" - -#: nova/objectstore/handler.py:106 -#, python-format -msgid "Unknown S3 value type %r" -msgstr "" - -#: nova/objectstore/handler.py:137 -msgid "Authenticated request" -msgstr "" - -#: nova/objectstore/handler.py:182 -msgid "List of buckets requested" -msgstr "" - -#: nova/objectstore/handler.py:209 -#, python-format -msgid "List keys for bucket %s" -msgstr "" - -#: nova/objectstore/handler.py:217 -#, python-format -msgid "Unauthorized attempt to access bucket %s" -msgstr "" - -#: nova/objectstore/handler.py:235 -#, python-format -msgid "Creating bucket %s" -msgstr "" - -#: nova/objectstore/handler.py:245 -#, python-format -msgid "Deleting bucket %s" -msgstr "" - -#: nova/objectstore/handler.py:249 -#, python-format -msgid "Unauthorized attempt to delete bucket %s" -msgstr "" - -#: nova/objectstore/handler.py:271 -#, python-format -msgid "Getting object: %s / %s" -msgstr "" - -#: nova/objectstore/handler.py:274 -#, python-format -msgid "Unauthorized attempt to get object %s from bucket %s" -msgstr "" - -#: nova/objectstore/handler.py:292 -#, python-format -msgid "Putting object: %s / %s" -msgstr "" - -#: nova/objectstore/handler.py:295 -#, python-format -msgid "Unauthorized attempt to upload object %s to bucket %s" -msgstr "" - -#: nova/objectstore/handler.py:314 -#, python-format -msgid "Deleting object: %s / %s" -msgstr "" - -#: nova/objectstore/handler.py:393 -#, python-format -msgid "Not authorized to upload image: invalid directory %s" -msgstr "" - -#: nova/objectstore/handler.py:401 -#, python-format -msgid "Not authorized to upload image: unauthorized bucket %s" -msgstr "" - -#: nova/objectstore/handler.py:406 -#, python-format -msgid "Starting image upload: %s" -msgstr "" - -#: nova/objectstore/handler.py:420 -#, python-format -msgid "Not authorized to update attributes of image %s" -msgstr "" - -#: nova/objectstore/handler.py:428 -#, python-format -msgid "Toggling publicity flag of image %s %r" -msgstr "" - -#: nova/objectstore/handler.py:433 -#, python-format -msgid "Updating user fields on image %s" -msgstr "" - -#: nova/objectstore/handler.py:447 -#, python-format -msgid "Unauthorized attempt to delete image %s" -msgstr "" - -#: nova/objectstore/handler.py:452 -#, python-format -msgid "Deleted image: %s" -msgstr "" - -#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73 -#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118 -msgid "No hosts found" -msgstr "" - -#: nova/scheduler/driver.py:66 -msgid "Must implement a fallback schedule" -msgstr "" - -#: nova/scheduler/manager.py:69 -#, python-format -msgid "Casting to %s %s for %s" -msgstr "" - -#: nova/scheduler/simple.py:63 -msgid "All hosts have too many cores" -msgstr "" - -#: nova/scheduler/simple.py:95 -msgid "All hosts have too many gigabytes" -msgstr "" - -#: nova/scheduler/simple.py:115 -msgid "All hosts have too many networks" -msgstr "" - -#: nova/tests/test_cloud.py:198 -msgid "Can't test instances without a real virtual env." -msgstr "" - -#: nova/tests/test_cloud.py:210 -#, python-format -msgid "Need to watch instance %s until it's running..." -msgstr "" - -#: nova/tests/test_compute.py:104 -#, python-format -msgid "Running instances: %s" -msgstr "" - -#: nova/tests/test_compute.py:110 -#, python-format -msgid "After terminating instances: %s" -msgstr "" - -#: nova/tests/test_rpc.py:89 -#, python-format -msgid "Nested received %s, %s" -msgstr "" - -#: nova/tests/test_rpc.py:94 -#, python-format -msgid "Nested return %s" -msgstr "" - -#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125 -#, python-format -msgid "Received %s" -msgstr "" - -#: nova/tests/test_volume.py:162 -#, python-format -msgid "Target %s allocated" -msgstr "" - -#: nova/virt/connection.py:73 -msgid "Failed to open connection to the hypervisor" -msgstr "" - -#: nova/virt/fake.py:210 -#, python-format -msgid "Instance %s Not Found" -msgstr "" - -#: nova/virt/hyperv.py:118 -msgid "In init host" -msgstr "" - -#: nova/virt/hyperv.py:131 -#, python-format -msgid "Attempt to create duplicate vm %s" -msgstr "" - -#: nova/virt/hyperv.py:148 -#, python-format -msgid "Starting VM %s " -msgstr "" - -#: nova/virt/hyperv.py:150 -#, python-format -msgid "Started VM %s " -msgstr "" - -#: nova/virt/hyperv.py:152 -#, python-format -msgid "spawn vm failed: %s" -msgstr "" - -#: nova/virt/hyperv.py:169 -#, python-format -msgid "Failed to create VM %s" -msgstr "" - -#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125 -#, python-format -msgid "Created VM %s..." -msgstr "" - -#: nova/virt/hyperv.py:188 -#, python-format -msgid "Set memory for vm %s..." -msgstr "" - -#: nova/virt/hyperv.py:198 -#, python-format -msgid "Set vcpus for vm %s..." -msgstr "" - -#: nova/virt/hyperv.py:202 -#, python-format -msgid "Creating disk for %s by attaching disk file %s" -msgstr "" - -#: nova/virt/hyperv.py:227 -#, python-format -msgid "Failed to add diskdrive to VM %s" -msgstr "" - -#: nova/virt/hyperv.py:230 -#, python-format -msgid "New disk drive path is %s" -msgstr "" - -#: nova/virt/hyperv.py:247 -#, python-format -msgid "Failed to add vhd file to VM %s" -msgstr "" - -#: nova/virt/hyperv.py:249 -#, python-format -msgid "Created disk for %s" -msgstr "" - -#: nova/virt/hyperv.py:253 -#, python-format -msgid "Creating nic for %s " -msgstr "" - -#: nova/virt/hyperv.py:272 -msgid "Failed creating a port on the external vswitch" -msgstr "" - -#: nova/virt/hyperv.py:273 -#, python-format -msgid "Failed creating port for %s" -msgstr "" - -#: nova/virt/hyperv.py:275 -#, python-format -msgid "Created switch port %s on switch %s" -msgstr "" - -#: nova/virt/hyperv.py:285 -#, python-format -msgid "Failed to add nic to VM %s" -msgstr "" - -#: nova/virt/hyperv.py:287 -#, python-format -msgid "Created nic for %s " -msgstr "" - -#: nova/virt/hyperv.py:320 -#, python-format -msgid "WMI job failed: %s" -msgstr "" - -#: nova/virt/hyperv.py:322 -#, python-format -msgid "WMI job succeeded: %s, Elapsed=%s " -msgstr "" - -#: nova/virt/hyperv.py:358 -#, python-format -msgid "Got request to destroy vm %s" -msgstr "" - -#: nova/virt/hyperv.py:383 -#, python-format -msgid "Failed to destroy vm %s" -msgstr "" - -#: nova/virt/hyperv.py:389 -#, python-format -msgid "Del: disk %s vm %s" -msgstr "" - -#: nova/virt/hyperv.py:405 -#, python-format -msgid "" -"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, " -"cpu_time=%s" -msgstr "" - -#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301 -#, python-format -msgid "duplicate name found: %s" -msgstr "" - -#: nova/virt/hyperv.py:444 -#, python-format -msgid "Successfully changed vm state of %s to %s" -msgstr "" - -#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449 -#, python-format -msgid "Failed to change vm state of %s to %s" -msgstr "" - -#: nova/virt/images.py:70 -#, python-format -msgid "Finished retreving %s -- placed in %s" -msgstr "" - -#: nova/virt/libvirt_conn.py:144 -#, python-format -msgid "Connecting to libvirt: %s" -msgstr "" - -#: nova/virt/libvirt_conn.py:157 -msgid "Connection to libvirt broke" -msgstr "" - -#: nova/virt/libvirt_conn.py:229 -#, python-format -msgid "instance %s: deleting instance files %s" -msgstr "" - -#: nova/virt/libvirt_conn.py:271 -#, python-format -msgid "No disk at %s" -msgstr "" - -#: nova/virt/libvirt_conn.py:278 -msgid "Instance snapshotting is not supported for libvirtat this time" -msgstr "" - -#: nova/virt/libvirt_conn.py:294 -#, python-format -msgid "instance %s: rebooted" -msgstr "" - -#: nova/virt/libvirt_conn.py:297 -#, python-format -msgid "_wait_for_reboot failed: %s" -msgstr "" - -#: nova/virt/libvirt_conn.py:340 -#, python-format -msgid "instance %s: rescued" -msgstr "" - -#: nova/virt/libvirt_conn.py:343 -#, python-format -msgid "_wait_for_rescue failed: %s" -msgstr "" - -#: nova/virt/libvirt_conn.py:370 -#, python-format -msgid "instance %s: is running" -msgstr "" - -#: nova/virt/libvirt_conn.py:381 -#, python-format -msgid "instance %s: booted" -msgstr "" - -#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116 -#, python-format -msgid "instance %s: failed to boot" -msgstr "" - -#: nova/virt/libvirt_conn.py:395 -#, python-format -msgid "virsh said: %r" -msgstr "" - -#: nova/virt/libvirt_conn.py:399 -msgid "cool, it's a device" -msgstr "" - -#: nova/virt/libvirt_conn.py:407 -#, python-format -msgid "data: %r, fpath: %r" -msgstr "" - -#: nova/virt/libvirt_conn.py:415 -#, python-format -msgid "Contents of file %s: %r" -msgstr "" - -#: nova/virt/libvirt_conn.py:449 -#, python-format -msgid "instance %s: Creating image" -msgstr "" - -#: nova/virt/libvirt_conn.py:505 -#, python-format -msgid "instance %s: injecting key into image %s" -msgstr "" - -#: nova/virt/libvirt_conn.py:508 -#, python-format -msgid "instance %s: injecting net into image %s" -msgstr "" - -#: nova/virt/libvirt_conn.py:516 -#, python-format -msgid "instance %s: ignoring error injecting data into image %s (%s)" -msgstr "" - -#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547 -#, python-format -msgid "instance %s: starting toXML method" -msgstr "" - -#: nova/virt/libvirt_conn.py:589 -#, python-format -msgid "instance %s: finished toXML method" -msgstr "" - -#: nova/virt/xenapi_conn.py:113 -msgid "" -"Must specify xenapi_connection_url, xenapi_connection_username " -"(optionally), and xenapi_connection_password to use " -"connection_type=xenapi" -msgstr "" - -#: nova/virt/xenapi_conn.py:263 -#, python-format -msgid "Task [%s] %s status: success %s" -msgstr "" - -#: nova/virt/xenapi_conn.py:271 -#, python-format -msgid "Task [%s] %s status: %s %s" -msgstr "" - -#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300 -#, python-format -msgid "Got exception: %s" -msgstr "" - -#: nova/virt/xenapi/fake.py:72 -#, python-format -msgid "%s: _db_content => %s" -msgstr "" - -#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338 -#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404 -msgid "Raising NotImplemented" -msgstr "" - -#: nova/virt/xenapi/fake.py:249 -#, python-format -msgid "xenapi.fake does not have an implementation for %s" -msgstr "" - -#: nova/virt/xenapi/fake.py:283 -#, python-format -msgid "Calling %s %s" -msgstr "" - -#: nova/virt/xenapi/fake.py:288 -#, python-format -msgid "Calling getter %s" -msgstr "" - -#: nova/virt/xenapi/fake.py:340 -#, python-format -msgid "" -"xenapi.fake does not have an implementation for %s or it has been called " -"with the wrong number of arguments" -msgstr "" - -#: nova/virt/xenapi/network_utils.py:40 -#, python-format -msgid "Found non-unique network for bridge %s" -msgstr "" - -#: nova/virt/xenapi/network_utils.py:43 -#, python-format -msgid "Found no network for bridge %s" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:127 -#, python-format -msgid "Created VM %s as %s." -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:147 -#, python-format -msgid "Creating VBD for VM %s, VDI %s ... " -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:149 -#, python-format -msgid "Created VBD %s for VM %s, VDI %s." -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:165 -#, python-format -msgid "VBD not found in instance %s" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:175 -#, python-format -msgid "Unable to unplug VBD %s" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:187 -#, python-format -msgid "Unable to destroy VBD %s" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:202 -#, python-format -msgid "Creating VIF for VM %s, network %s." -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:205 -#, python-format -msgid "Created VIF %s for VM %s, network %s." -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:216 -#, python-format -msgid "Snapshotting VM %s with label '%s'..." -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:229 -#, python-format -msgid "Created snapshot %s from VM %s." -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:243 -#, python-format -msgid "Asking xapi to upload %s as '%s'" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:261 -#, python-format -msgid "Asking xapi to fetch %s as %s" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:279 -#, python-format -msgid "Looking up vdi %s for PV kernel" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:290 -#, python-format -msgid "PV Kernel in VDI:%d" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:318 -#, python-format -msgid "VDI %s is still available" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:331 -#, python-format -msgid "(VM_UTILS) xenserver vm state -> |%s|" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:333 -#, python-format -msgid "(VM_UTILS) xenapi power_state -> |%s|" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:390 -#, python-format -msgid "VHD %s has parent %s" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:407 -#, python-format -msgid "Re-scanning SR %s" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:431 -#, python-format -msgid "Parent %s doesn't match original parent %s, waiting for coalesce..." -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:448 -#, python-format -msgid "No VDIs found for VM %s" -msgstr "" - -#: nova/virt/xenapi/vm_utils.py:452 -#, python-format -msgid "Unexpected number of VDIs (%s) found for VM %s" -msgstr "" - -#: nova/virt/xenapi/vmops.py:62 -#, python-format -msgid "Attempted to create non-unique name %s" -msgstr "" - -#: nova/virt/xenapi/vmops.py:99 -#, python-format -msgid "Starting VM %s..." -msgstr "" - -#: nova/virt/xenapi/vmops.py:101 -#, python-format -msgid "Spawning VM %s created %s." -msgstr "" - -#: nova/virt/xenapi/vmops.py:112 -#, python-format -msgid "Instance %s: booted" -msgstr "" - -#: nova/virt/xenapi/vmops.py:137 -#, python-format -msgid "Instance not present %s" -msgstr "" - -#: nova/virt/xenapi/vmops.py:166 -#, python-format -msgid "Starting snapshot for VM %s" -msgstr "" - -#: nova/virt/xenapi/vmops.py:174 -#, python-format -msgid "Unable to Snapshot %s: %s" -msgstr "" - -#: nova/virt/xenapi/vmops.py:184 -#, python-format -msgid "Finished snapshot and upload for VM %s" -msgstr "" - -#: nova/virt/xenapi/vmops.py:252 -#, python-format -msgid "suspend: instance not present %s" -msgstr "" - -#: nova/virt/xenapi/vmops.py:262 -#, python-format -msgid "resume: instance not present %s" -msgstr "" - -#: nova/virt/xenapi/vmops.py:271 -#, python-format -msgid "Instance not found %s" -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:57 -#, python-format -msgid "Introducing %s..." -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:74 -#, python-format -msgid "Introduced %s as %s." -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:78 -msgid "Unable to create Storage Repository" -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:90 -#, python-format -msgid "Unable to find SR from VBD %s" -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:96 -#, python-format -msgid "Forgetting SR %s ... " -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:101 -#, python-format -msgid "Ignoring exception %s when getting PBDs for %s" -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:107 -#, python-format -msgid "Ignoring exception %s when unplugging PBD %s" -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:111 -#, python-format -msgid "Forgetting SR %s done." -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:113 -#, python-format -msgid "Ignoring exception %s when forgetting SR %s" -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:123 -#, python-format -msgid "Unable to introduce VDI on SR %s" -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:128 -#, python-format -msgid "Unable to get record of VDI %s on" -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:146 -#, python-format -msgid "Unable to introduce VDI for SR %s" -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:175 -#, python-format -msgid "Unable to obtain target information %s, %s" -msgstr "" - -#: nova/virt/xenapi/volume_utils.py:197 -#, python-format -msgid "Mountpoint cannot be translated: %s" -msgstr "" - -#: nova/virt/xenapi/volumeops.py:51 -#, python-format -msgid "Attach_volume: %s, %s, %s" -msgstr "" - -#: nova/virt/xenapi/volumeops.py:69 -#, python-format -msgid "Unable to create VDI on SR %s for instance %s" -msgstr "" - -#: nova/virt/xenapi/volumeops.py:81 -#, python-format -msgid "Unable to use SR %s for instance %s" -msgstr "" - -#: nova/virt/xenapi/volumeops.py:93 -#, python-format -msgid "Unable to attach volume to instance %s" -msgstr "" - -#: nova/virt/xenapi/volumeops.py:95 -#, python-format -msgid "Mountpoint %s attached to instance %s" -msgstr "" - -#: nova/virt/xenapi/volumeops.py:106 -#, python-format -msgid "Detach_volume: %s, %s" -msgstr "" - -#: nova/virt/xenapi/volumeops.py:113 -#, python-format -msgid "Unable to locate volume %s" -msgstr "" - -#: nova/virt/xenapi/volumeops.py:121 -#, python-format -msgid "Unable to detach volume %s" -msgstr "" - -#: nova/virt/xenapi/volumeops.py:128 -#, python-format -msgid "Mountpoint %s detached from instance %s" -msgstr "" - -#: nova/volume/api.py:44 -#, python-format -msgid "Quota exceeeded for %s, tried to create %sG volume" -msgstr "" - -#: nova/volume/api.py:46 -#, python-format -msgid "Volume quota exceeded. You cannot create a volume of size %s" -msgstr "" - -#: nova/volume/api.py:70 nova/volume/api.py:95 -msgid "Volume status must be available" -msgstr "" - -#: nova/volume/api.py:97 -msgid "Volume is already attached" -msgstr "" - -#: nova/volume/api.py:103 -msgid "Volume is already detached" -msgstr "" - -#: nova/volume/driver.py:76 -#, python-format -msgid "Recovering from a failed execute. Try number %s" -msgstr "" - -#: nova/volume/driver.py:85 -#, python-format -msgid "volume group %s doesn't exist" -msgstr "" - -#: nova/volume/driver.py:210 -#, python-format -msgid "FAKE AOE: %s" -msgstr "" - -#: nova/volume/driver.py:315 -#, python-format -msgid "FAKE ISCSI: %s" -msgstr "" - -#: nova/volume/manager.py:85 -#, python-format -msgid "Re-exporting %s volumes" -msgstr "" - -#: nova/volume/manager.py:93 -#, python-format -msgid "volume %s: creating" -msgstr "" - -#: nova/volume/manager.py:102 -#, python-format -msgid "volume %s: creating lv of size %sG" -msgstr "" - -#: nova/volume/manager.py:106 -#, python-format -msgid "volume %s: creating export" -msgstr "" - -#: nova/volume/manager.py:113 -#, python-format -msgid "volume %s: created successfully" -msgstr "" - -#: nova/volume/manager.py:121 -msgid "Volume is still attached" -msgstr "" - -#: nova/volume/manager.py:123 -msgid "Volume is not local to this node" -msgstr "" - -#: nova/volume/manager.py:124 -#, python-format -msgid "volume %s: removing export" -msgstr "" - -#: nova/volume/manager.py:126 -#, python-format -msgid "volume %s: deleting" -msgstr "" - -#: nova/volume/manager.py:129 -#, python-format -msgid "volume %s: deleted successfully" -msgstr "" - diff --git a/po/nova.pot b/po/nova.pot new file mode 100644 index 000000000..576621ce9 --- /dev/null +++ b/po/nova.pot @@ -0,0 +1,2705 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-02-09 09:26-0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../nova/scheduler/chance.py:37 ../nova/scheduler/simple.py:75 +#: ../nova/scheduler/simple.py:110 ../nova/scheduler/simple.py:122 +#: ../nova/scheduler/zone.py:55 +msgid "No hosts found" +msgstr "" + +#: ../nova/exception.py:33 +msgid "Unexpected error while running command." +msgstr "" + +#: ../nova/exception.py:36 +#, python-format +msgid "" +"%(description)s\n" +"Command: %(cmd)s\n" +"Exit code: %(exit_code)s\n" +"Stdout: %(stdout)r\n" +"Stderr: %(stderr)r" +msgstr "" + +#: ../nova/exception.py:107 +msgid "DB exception wrapped" +msgstr "" + +#. exc_type, exc_value, exc_traceback = sys.exc_info() +#: ../nova/exception.py:120 +msgid "Uncaught exception" +msgstr "" + +#: ../nova/volume/api.py:45 +#, python-format +msgid "Quota exceeeded for %(pid)s, tried to create %(size)sG volume" +msgstr "" + +#: ../nova/volume/api.py:47 +#, python-format +msgid "Volume quota exceeded. You cannot create a volume of size %sG" +msgstr "" + +#: ../nova/volume/api.py:71 ../nova/volume/api.py:96 +msgid "Volume status must be available" +msgstr "" + +#: ../nova/volume/api.py:98 +msgid "Volume is already attached" +msgstr "" + +#: ../nova/volume/api.py:104 +msgid "Volume is already detached" +msgstr "" + +#: ../nova/virt/fake.py:224 +#, python-format +msgid "Instance %s Not Found" +msgstr "" + +#: ../nova/api/openstack/servers.py:138 +#, python-format +msgid "%(param)s property not found for image %(_image_id)s" +msgstr "" + +#: ../nova/api/openstack/servers.py:219 +#, python-format +msgid "Compute.api::lock %s" +msgstr "" + +#: ../nova/api/openstack/servers.py:234 +#, python-format +msgid "Compute.api::unlock %s" +msgstr "" + +#: ../nova/api/openstack/servers.py:248 +#, python-format +msgid "Compute.api::get_lock %s" +msgstr "" + +#: ../nova/api/openstack/servers.py:259 +#, python-format +msgid "Compute.api::pause %s" +msgstr "" + +#: ../nova/api/openstack/servers.py:270 +#, python-format +msgid "Compute.api::unpause %s" +msgstr "" + +#: ../nova/api/openstack/servers.py:281 +#, python-format +msgid "compute.api::suspend %s" +msgstr "" + +#: ../nova/api/openstack/servers.py:292 +#, python-format +msgid "compute.api::resume %s" +msgstr "" + +#: ../nova/twistd.py:159 +msgid "Wrong number of arguments." +msgstr "" + +#: ../nova/twistd.py:211 +#, python-format +msgid "pidfile %s does not exist. Daemon not running?\n" +msgstr "" + +#: ../nova/twistd.py:223 +msgid "No such process" +msgstr "" + +#: ../nova/twistd.py:232 ../nova/service.py:224 +#, python-format +msgid "Serving %s" +msgstr "" + +#: ../nova/twistd.py:264 ../nova/service.py:225 +msgid "Full set of FLAGS:" +msgstr "" + +#: ../nova/twistd.py:268 +#, python-format +msgid "Starting %s" +msgstr "" + +#: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 +#: ../nova/db/sqlalchemy/api.py:709 ../nova/virt/libvirt_conn.py:741 +#: ../nova/api/ec2/__init__.py:322 +#, python-format +msgid "Instance %s not found" +msgstr "" + +#. NOTE: No Resource Pool concept so far +#: ../nova/virt/xenapi/volumeops.py:51 +#, python-format +msgid "Attach_volume: %(instance_name)s, %(device_path)s, %(mountpoint)s" +msgstr "" + +#: ../nova/virt/xenapi/volumeops.py:69 +#, python-format +msgid "Unable to create VDI on SR %(sr_ref)s for instance %(instance_name)s" +msgstr "" + +#: ../nova/virt/xenapi/volumeops.py:80 +#, python-format +msgid "Unable to use SR %(sr_ref)s for instance %(instance_name)s" +msgstr "" + +#: ../nova/virt/xenapi/volumeops.py:91 +#, python-format +msgid "Unable to attach volume to instance %s" +msgstr "" + +#: ../nova/virt/xenapi/volumeops.py:93 +#, python-format +msgid "Mountpoint %(mountpoint)s attached to instance %(instance_name)s" +msgstr "" + +#. Detach VBD from VM +#: ../nova/virt/xenapi/volumeops.py:104 +#, python-format +msgid "Detach_volume: %(instance_name)s, %(mountpoint)s" +msgstr "" + +#: ../nova/virt/xenapi/volumeops.py:112 +#, python-format +msgid "Unable to locate volume %s" +msgstr "" + +#: ../nova/virt/xenapi/volumeops.py:120 +#, python-format +msgid "Unable to detach volume %s" +msgstr "" + +#: ../nova/virt/xenapi/volumeops.py:127 +#, python-format +msgid "Mountpoint %(mountpoint)s detached from instance %(instance_name)s" +msgstr "" + +#: ../nova/compute/instance_types.py:41 +#, python-format +msgid "Unknown instance type: %s" +msgstr "" + +#: ../nova/crypto.py:46 +msgid "Filename of root CA" +msgstr "" + +#: ../nova/crypto.py:49 +msgid "Filename of private key" +msgstr "" + +#: ../nova/crypto.py:51 +msgid "Filename of root Certificate Revokation List" +msgstr "" + +#: ../nova/crypto.py:53 +msgid "Where we keep our keys" +msgstr "" + +#: ../nova/crypto.py:55 +msgid "Where we keep our root CA" +msgstr "" + +#: ../nova/crypto.py:57 +msgid "Should we use a CA for each project?" +msgstr "" + +#: ../nova/crypto.py:61 +#, python-format +msgid "Subject for certificate for users, %s for project, user, timestamp" +msgstr "" + +#: ../nova/crypto.py:66 +#, python-format +msgid "Subject for certificate for projects, %s for project, timestamp" +msgstr "" + +#: ../nova/crypto.py:71 +#, python-format +msgid "Subject for certificate for vpns, %s for project, timestamp" +msgstr "" + +#: ../nova/crypto.py:258 +#, python-format +msgid "Flags path: %s" +msgstr "" + +#: ../nova/scheduler/manager.py:69 +#, python-format +msgid "Casting to %(topic)s %(host)s for %(method)s" +msgstr "" + +#: ../nova/compute/manager.py:77 +#, python-format +msgid "check_instance_lock: decorating: |%s|" +msgstr "" + +#: ../nova/compute/manager.py:79 +#, python-format +msgid "" +"check_instance_lock: arguments: |%(self)s| |%(context)s| |%(instance_id)s|" +msgstr "" + +#: ../nova/compute/manager.py:83 +#, python-format +msgid "check_instance_lock: locked: |%s|" +msgstr "" + +#: ../nova/compute/manager.py:85 +#, python-format +msgid "check_instance_lock: admin: |%s|" +msgstr "" + +#: ../nova/compute/manager.py:90 +#, python-format +msgid "check_instance_lock: executing: |%s|" +msgstr "" + +#: ../nova/compute/manager.py:94 +#, python-format +msgid "check_instance_lock: not executing |%s|" +msgstr "" + +#: ../nova/compute/manager.py:177 +msgid "Instance has already been created" +msgstr "" + +#: ../nova/compute/manager.py:178 +#, python-format +msgid "instance %s: starting..." +msgstr "" + +#. pylint: disable-msg=W0702 +#: ../nova/compute/manager.py:217 +#, python-format +msgid "instance %s: Failed to spawn" +msgstr "" + +#: ../nova/compute/manager.py:231 ../nova/tests/test_cloud.py:286 +#, python-format +msgid "Terminating instance %s" +msgstr "" + +#: ../nova/compute/manager.py:253 +#, python-format +msgid "Deallocating address %s" +msgstr "" + +#: ../nova/compute/manager.py:266 +#, python-format +msgid "trying to destroy already destroyed instance: %s" +msgstr "" + +#: ../nova/compute/manager.py:280 +#, python-format +msgid "Rebooting instance %s" +msgstr "" + +#: ../nova/compute/manager.py:285 +#, python-format +msgid "" +"trying to reboot a non-running instance: %(instance_id)s (state: %(state)s " +"expected: %(running)s)" +msgstr "" + +#: ../nova/compute/manager.py:309 +#, python-format +msgid "instance %s: snapshotting" +msgstr "" + +#: ../nova/compute/manager.py:314 +#, python-format +msgid "" +"trying to snapshot a non-running instance: %(instance_id)s (state: %(state)s " +"expected: %(running)s)" +msgstr "" + +#: ../nova/compute/manager.py:355 +#, python-format +msgid "instance %s: rescuing" +msgstr "" + +#: ../nova/compute/manager.py:370 +#, python-format +msgid "instance %s: unrescuing" +msgstr "" + +#: ../nova/compute/manager.py:389 +#, python-format +msgid "instance %s: pausing" +msgstr "" + +#: ../nova/compute/manager.py:406 +#, python-format +msgid "instance %s: unpausing" +msgstr "" + +#: ../nova/compute/manager.py:423 +#, python-format +msgid "instance %s: retrieving diagnostics" +msgstr "" + +#: ../nova/compute/manager.py:436 +#, python-format +msgid "instance %s: suspending" +msgstr "" + +#: ../nova/compute/manager.py:455 +#, python-format +msgid "instance %s: resuming" +msgstr "" + +#: ../nova/compute/manager.py:474 +#, python-format +msgid "instance %s: locking" +msgstr "" + +#: ../nova/compute/manager.py:486 +#, python-format +msgid "instance %s: unlocking" +msgstr "" + +#: ../nova/compute/manager.py:496 +#, python-format +msgid "instance %s: getting locked state" +msgstr "" + +#: ../nova/compute/manager.py:506 ../nova/api/ec2/cloud.py:513 +#, python-format +msgid "Get console output for instance %s" +msgstr "" + +#: ../nova/compute/manager.py:514 +#, python-format +msgid "instance %s: getting ajax console" +msgstr "" + +#: ../nova/compute/manager.py:524 +#, python-format +msgid "" +"instance %(instance_id)s: attaching volume %(volume_id)s to %(mountpoint)s" +msgstr "" + +#. pylint: disable-msg=W0702 +#. NOTE(vish): The inline callback eats the exception info so we +#. log the traceback here and reraise the same +#. ecxception below. +#: ../nova/compute/manager.py:540 +#, python-format +msgid "instance %(instance_id)s: attach failed %(mountpoint)s, removing" +msgstr "" + +#: ../nova/compute/manager.py:556 +#, python-format +msgid "" +"Detach volume %(volume_id)s from mountpoint %(mp)s on instance " +"%(instance_id)s" +msgstr "" + +#: ../nova/compute/manager.py:559 +#, python-format +msgid "Detaching volume from unknown instance %s" +msgstr "" + +#: ../nova/scheduler/simple.py:53 +#, python-format +msgid "Host %s is not alive" +msgstr "" + +#: ../nova/scheduler/simple.py:65 +msgid "All hosts have too many cores" +msgstr "" + +#: ../nova/scheduler/simple.py:87 +#, python-format +msgid "Host %s not available" +msgstr "" + +#: ../nova/scheduler/simple.py:99 +msgid "All hosts have too many gigabytes" +msgstr "" + +#: ../nova/scheduler/simple.py:119 +msgid "All hosts have too many networks" +msgstr "" + +#: ../nova/volume/manager.py:85 +#, python-format +msgid "Re-exporting %s volumes" +msgstr "" + +#: ../nova/volume/manager.py:90 +#, python-format +msgid "volume %s: skipping export" +msgstr "" + +#: ../nova/volume/manager.py:96 +#, python-format +msgid "volume %s: creating" +msgstr "" + +#: ../nova/volume/manager.py:108 +#, python-format +msgid "volume %(vol_name)s: creating lv of size %(vol_size)sG" +msgstr "" + +#: ../nova/volume/manager.py:112 +#, python-format +msgid "volume %s: creating export" +msgstr "" + +#: ../nova/volume/manager.py:123 +#, python-format +msgid "volume %s: created successfully" +msgstr "" + +#: ../nova/volume/manager.py:131 +msgid "Volume is still attached" +msgstr "" + +#: ../nova/volume/manager.py:133 +msgid "Volume is not local to this node" +msgstr "" + +#: ../nova/volume/manager.py:136 +#, python-format +msgid "volume %s: removing export" +msgstr "" + +#: ../nova/volume/manager.py:138 +#, python-format +msgid "volume %s: deleting" +msgstr "" + +#: ../nova/volume/manager.py:147 +#, python-format +msgid "volume %s: deleted successfully" +msgstr "" + +#: ../nova/virt/xenapi/fake.py:74 +#, python-format +msgid "%(text)s: _db_content => %(content)s" +msgstr "" + +#: ../nova/virt/xenapi/fake.py:300 ../nova/virt/xenapi/fake.py:400 +#: ../nova/virt/xenapi/fake.py:418 ../nova/virt/xenapi/fake.py:474 +msgid "Raising NotImplemented" +msgstr "" + +#: ../nova/virt/xenapi/fake.py:302 +#, python-format +msgid "xenapi.fake does not have an implementation for %s" +msgstr "" + +#: ../nova/virt/xenapi/fake.py:337 +#, python-format +msgid "Calling %(localname)s %(impl)s" +msgstr "" + +#: ../nova/virt/xenapi/fake.py:342 +#, python-format +msgid "Calling getter %s" +msgstr "" + +#: ../nova/virt/xenapi/fake.py:402 +#, python-format +msgid "" +"xenapi.fake does not have an implementation for %s or it has been called " +"with the wrong number of arguments" +msgstr "" + +#: ../nova/tests/test_cloud.py:256 +msgid "Can't test instances without a real virtual env." +msgstr "" + +#: ../nova/tests/test_cloud.py:268 +#, python-format +msgid "Need to watch instance %s until it's running..." +msgstr "" + +#: ../nova/virt/connection.py:73 +msgid "Failed to open connection to the hypervisor" +msgstr "" + +#: ../nova/network/linux_net.py:181 +#, python-format +msgid "Starting VLAN inteface %s" +msgstr "" + +#: ../nova/network/linux_net.py:202 +#, python-format +msgid "Starting Bridge interface for %s" +msgstr "" + +#. pylint: disable-msg=W0703 +#: ../nova/network/linux_net.py:308 +#, python-format +msgid "Hupping dnsmasq threw %s" +msgstr "" + +#: ../nova/network/linux_net.py:310 +#, python-format +msgid "Pid %d is stale, relaunching dnsmasq" +msgstr "" + +#. pylint: disable-msg=W0703 +#: ../nova/network/linux_net.py:352 +#, python-format +msgid "killing radvd threw %s" +msgstr "" + +#: ../nova/network/linux_net.py:354 +#, python-format +msgid "Pid %d is stale, relaunching radvd" +msgstr "" + +#. pylint: disable-msg=W0703 +#: ../nova/network/linux_net.py:443 +#, python-format +msgid "Killing dnsmasq threw %s" +msgstr "" + +#: ../nova/utils.py:56 +#, python-format +msgid "Inner Exception: %s" +msgstr "" + +#: ../nova/utils.py:57 +#, python-format +msgid "Class %s cannot be found" +msgstr "" + +#: ../nova/utils.py:116 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: ../nova/utils.py:128 +#, python-format +msgid "Running cmd (subprocess): %s" +msgstr "" + +#: ../nova/utils.py:141 +#, python-format +msgid "Result was %s" +msgstr "" + +#: ../nova/utils.py:179 +#, python-format +msgid "debug in callback: %s" +msgstr "" + +#: ../nova/utils.py:184 +#, python-format +msgid "Running %s" +msgstr "" + +#: ../nova/utils.py:215 +#, python-format +msgid "Link Local address is not found.:%s" +msgstr "" + +#: ../nova/utils.py:218 +#, python-format +msgid "Couldn't get Link Local IP of %(interface)s :%(ex)s" +msgstr "" + +#: ../nova/utils.py:316 +#, python-format +msgid "Invalid backend: %s" +msgstr "" + +#: ../nova/utils.py:327 +#, python-format +msgid "backend %s" +msgstr "" + +#: ../nova/fakerabbit.py:49 +#, python-format +msgid "(%(nm)s) publish (key: %(routing_key)s) %(message)s" +msgstr "" + +#: ../nova/fakerabbit.py:54 +#, python-format +msgid "Publishing to route %s" +msgstr "" + +#: ../nova/fakerabbit.py:84 +#, python-format +msgid "Declaring queue %s" +msgstr "" + +#: ../nova/fakerabbit.py:90 +#, python-format +msgid "Declaring exchange %s" +msgstr "" + +#: ../nova/fakerabbit.py:96 +#, python-format +msgid "Binding %(queue)s to %(exchange)s with key %(routing_key)s" +msgstr "" + +#: ../nova/fakerabbit.py:121 +#, python-format +msgid "Getting from %(queue)s: %(message)s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:135 ../nova/virt/hyperv.py:171 +#, python-format +msgid "Created VM %s..." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:138 +#, python-format +msgid "Created VM %(instance_name)s as %(vm_ref)s." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:158 +#, python-format +msgid "Creating VBD for VM %(vm_ref)s, VDI %(vdi_ref)s ... " +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:161 +#, python-format +msgid "Created VBD %(vbd_ref)s for VM %(vm_ref)s, VDI %(vdi_ref)s." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:177 +#, python-format +msgid "VBD not found in instance %s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:187 +#, python-format +msgid "Unable to unplug VBD %s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:199 +#, python-format +msgid "Unable to destroy VBD %s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:214 +#, python-format +msgid "Creating VIF for VM %(vm_ref)s, network %(network_ref)s." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:217 +#, python-format +msgid "Created VIF %(vif_ref)s for VM %(vm_ref)s, network %(network_ref)s." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:236 +#, python-format +msgid "" +"Created VDI %(vdi_ref)s (%(name_label)s, %(virtual_size)s, %(read_only)s) on " +"%(sr_ref)s." +msgstr "" + +#. TODO(sirp): Add quiesce and VSS locking support when Windows support +#. is added +#: ../nova/virt/xenapi/vm_utils.py:248 +#, python-format +msgid "Snapshotting VM %(vm_ref)s with label '%(label)s'..." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:262 +#, python-format +msgid "Created snapshot %(template_vm_ref)s from VM %(vm_ref)s." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:276 +#, python-format +msgid "Asking xapi to upload %(vdi_uuids)s as ID %(image_id)s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:317 +#, python-format +msgid "Size for image %(image)s:%(virtual_size)d" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:322 +#, python-format +msgid "Glance image %s" +msgstr "" + +#. we need to invoke a plugin for copying VDI's +#. content into proper path +#: ../nova/virt/xenapi/vm_utils.py:332 +#, python-format +msgid "Copying VDI %s to /boot/guest on dom0" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:342 +#, python-format +msgid "Kernel/Ramdisk VDI %s destroyed" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:351 +#, python-format +msgid "Asking xapi to fetch %(url)s as %(access)s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:376 ../nova/virt/xenapi/vm_utils.py:392 +#, python-format +msgid "Looking up vdi %s for PV kernel" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:387 +#, python-format +msgid "PV Kernel in VDI:%d" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:395 +#, python-format +msgid "Running pygrub against %s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:401 +#, python-format +msgid "Found Xen kernel %s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:403 +msgid "No Xen kernel found. Booting HVM." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:415 ../nova/virt/hyperv.py:431 +#, python-format +msgid "duplicate name found: %s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:432 +#, python-format +msgid "VDI %s is still available" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:445 +#, python-format +msgid "(VM_UTILS) xenserver vm state -> |%s|" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:447 +#, python-format +msgid "(VM_UTILS) xenapi power_state -> |%s|" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:507 +#, python-format +msgid "VHD %(vdi_uuid)s has parent %(parent_ref)s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:524 +#, python-format +msgid "Re-scanning SR %s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:549 +#, python-format +msgid "" +"VHD coalesce attempts exceeded (%(counter)d > %(max_attempts)d), giving up..." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:556 +#, python-format +msgid "" +"Parent %(parent_uuid)s doesn't match original parent " +"%(original_parent_uuid)s, waiting for coalesce..." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:572 +#, python-format +msgid "No VDIs found for VM %s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:576 +#, python-format +msgid "Unexpected number of VDIs (%(num_vdis)s) found for VM %(vm_ref)s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:635 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:188 +#, python-format +msgid "Creating VBD for VDI %s ... " +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:637 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:190 +#, python-format +msgid "Creating VBD for VDI %s done." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:639 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:192 +#, python-format +msgid "Plugging VBD %s ... " +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:641 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:194 +#, python-format +msgid "Plugging VBD %s done." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:643 +#, python-format +msgid "VBD %(vbd)s plugged as %(orig_dev)s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:646 +#, python-format +msgid "VBD %(vbd)s plugged into wrong dev, remapping to %(dev)s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:650 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:197 +#, python-format +msgid "Destroying VBD for VDI %s ... " +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:653 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:200 +#, python-format +msgid "Destroying VBD for VDI %s done." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:665 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:211 +msgid "VBD.unplug successful first time." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:670 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:216 +msgid "VBD.unplug rejected: retrying..." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:674 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:220 +msgid "VBD.unplug successful eventually." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:677 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:223 +#, python-format +msgid "Ignoring XenAPI.Failure in VBD.unplug: %s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:686 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:66 +#, python-format +msgid "Ignoring XenAPI.Failure %s" +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:717 +#, python-format +msgid "" +"Writing partition table %(primary_first)d %(primary_last)d to %(dest)s..." +msgstr "" + +#: ../nova/virt/xenapi/vm_utils.py:729 +#, python-format +msgid "Writing partition table %s done." +msgstr "" + +#: ../nova/tests/test_rpc.py:89 +#, python-format +msgid "Nested received %(queue)s, %(value)s" +msgstr "" + +#: ../nova/tests/test_rpc.py:95 +#, python-format +msgid "Nested return %s" +msgstr "" + +#: ../nova/tests/test_rpc.py:120 ../nova/tests/test_rpc.py:126 +#, python-format +msgid "Received %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:44 +msgid "Use of empty request context is deprecated" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:133 +#, python-format +msgid "No service for id %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:251 +#, python-format +msgid "No service for %(host)s, %(binary)s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:597 +#, python-format +msgid "No floating ip for address %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:933 +#, python-format +msgid "no keypair for user %(user_id)s, name %(name)s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1048 ../nova/db/sqlalchemy/api.py:1106 +#, python-format +msgid "No network for id %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1078 +#, python-format +msgid "No network for bridge %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1092 +#, python-format +msgid "No network for instance %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1227 +#, python-format +msgid "Token %s does not exist" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1252 +#, python-format +msgid "No quota for project_id %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1405 ../nova/db/sqlalchemy/api.py:1451 +#: ../nova/api/ec2/__init__.py:328 +#, python-format +msgid "Volume %s not found" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1464 +#, python-format +msgid "No export device found for volume %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1477 +#, python-format +msgid "No target id found for volume %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1522 +#, python-format +msgid "No security group with id %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1539 +#, python-format +msgid "No security group named %(group_name)s for project: %(project_id)s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1632 +#, python-format +msgid "No secuity group rule with id %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1706 +#, python-format +msgid "No user for id %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1722 +#, python-format +msgid "No user for access key %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1784 +#, python-format +msgid "No project with id %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1929 +#, python-format +msgid "No console pool with id %(pool_id)s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1946 +#, python-format +msgid "" +"No console pool of type %(console_type)s for compute host %(compute_host)s " +"on proxy host %(host)s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:1985 +#, python-format +msgid "No console for instance %(instance_id)s in pool %(pool_id)s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:2007 +#, python-format +msgid "on instance %s" +msgstr "" + +#: ../nova/db/sqlalchemy/api.py:2008 +#, python-format +msgid "No console with id %(console_id)s %(idesc)s" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:160 +#, python-format +msgid "Checking state of %s" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:165 +#, python-format +msgid "Current state of %(name)s was %(state)s." +msgstr "" + +#: ../nova/virt/libvirt_conn.py:183 +#, python-format +msgid "Connecting to libvirt: %s" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:196 +msgid "Connection to libvirt broke" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:258 +#, python-format +msgid "instance %(instance_name)s: deleting instance files %(target)s" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:283 +#, python-format +msgid "Invalid device path %s" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:313 +#, python-format +msgid "No disk at %s" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:320 +msgid "Instance snapshotting is not supported for libvirtat this time" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:336 +#, python-format +msgid "instance %s: rebooted" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:339 +#, python-format +msgid "_wait_for_reboot failed: %s" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:382 +#, python-format +msgid "instance %s: rescued" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:385 +#, python-format +msgid "_wait_for_rescue failed: %s" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:411 +#, python-format +msgid "instance %s: is running" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:422 +#, python-format +msgid "instance %s: booted" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:425 ../nova/virt/xenapi/vmops.py:124 +#, python-format +msgid "instance %s: failed to boot" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:436 +#, python-format +msgid "virsh said: %r" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:440 +msgid "cool, it's a device" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:448 +#, python-format +msgid "data: %(data)r, fpath: %(fpath)r" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:456 +#, python-format +msgid "Contents of file %(fpath)s: %(contents)r" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:489 +msgid "Unable to find an open port" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:563 +#, python-format +msgid "instance %s: Creating image" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:646 +#, python-format +msgid "instance %(inst_name)s: injecting key into image %(img_id)s" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:649 +#, python-format +msgid "instance %(inst_name)s: injecting net into image %(img_id)s" +msgstr "" + +#. This could be a windows image, or a vmdk format disk +#: ../nova/virt/libvirt_conn.py:657 +#, python-format +msgid "" +"instance %(inst_name)s: ignoring error injecting data into image %(img_id)s " +"(%(e)s)" +msgstr "" + +#. TODO(termie): cache? +#: ../nova/virt/libvirt_conn.py:665 +#, python-format +msgid "instance %s: starting toXML method" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:732 +#, python-format +msgid "instance %s: finished toXML method" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:751 +msgid "diagnostics are not supported for libvirt" +msgstr "" + +#: ../nova/virt/libvirt_conn.py:1225 +#, python-format +msgid "Attempted to unfilter instance %s which is not filtered" +msgstr "" + +#: ../nova/api/ec2/metadatarequesthandler.py:76 +#, python-format +msgid "Failed to get metadata for ip: %s" +msgstr "" + +#: ../nova/auth/fakeldap.py:33 +msgid "Attempted to instantiate singleton" +msgstr "" + +#: ../nova/network/api.py:39 +#, python-format +msgid "Quota exceeeded for %s, tried to allocate address" +msgstr "" + +#: ../nova/network/api.py:42 +msgid "Address quota exceeded. You cannot allocate any more addresses" +msgstr "" + +#: ../nova/tests/test_volume.py:162 +#, python-format +msgid "Target %s allocated" +msgstr "" + +#: ../nova/virt/images.py:70 +#, python-format +msgid "Finished retreving %(url)s -- placed in %(path)s" +msgstr "" + +#: ../nova/scheduler/driver.py:66 +msgid "Must implement a fallback schedule" +msgstr "" + +#: ../nova/console/manager.py:70 +msgid "Adding console" +msgstr "" + +#: ../nova/console/manager.py:90 +#, python-format +msgid "Tried to remove non-existant console %(console_id)s." +msgstr "" + +#: ../nova/api/direct.py:149 +msgid "not available" +msgstr "" + +#: ../nova/api/ec2/cloud.py:62 +#, python-format +msgid "The key_pair %s already exists" +msgstr "" + +#. TODO(vish): Do this with M2Crypto instead +#: ../nova/api/ec2/cloud.py:118 +#, python-format +msgid "Generating root CA: %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:303 +#, python-format +msgid "Create key pair %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:311 +#, python-format +msgid "Delete key pair %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:384 +#, python-format +msgid "%s is not a valid ipProtocol" +msgstr "" + +#: ../nova/api/ec2/cloud.py:388 +msgid "Invalid port range" +msgstr "" + +#: ../nova/api/ec2/cloud.py:419 +#, python-format +msgid "Revoke security group ingress %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:428 ../nova/api/ec2/cloud.py:457 +msgid "Not enough parameters to build a valid rule." +msgstr "" + +#: ../nova/api/ec2/cloud.py:441 +msgid "No rule for the specified parameters." +msgstr "" + +#: ../nova/api/ec2/cloud.py:448 +#, python-format +msgid "Authorize security group ingress %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:462 +#, python-format +msgid "This rule already exists in group %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:490 +#, python-format +msgid "Create Security Group %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:493 +#, python-format +msgid "group %s already exists" +msgstr "" + +#: ../nova/api/ec2/cloud.py:505 +#, python-format +msgid "Delete security group %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:582 +#, python-format +msgid "Create volume of %s GB" +msgstr "" + +#: ../nova/api/ec2/cloud.py:610 +#, python-format +msgid "Attach volume %(volume_id)s to instance %(instance_id)s at %(device)s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:627 +#, python-format +msgid "Detach volume %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:759 +msgid "Allocate address" +msgstr "" + +#: ../nova/api/ec2/cloud.py:764 +#, python-format +msgid "Release address %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:769 +#, python-format +msgid "Associate address %(public_ip)s to instance %(instance_id)s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:778 +#, python-format +msgid "Disassociate address %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:805 +msgid "Going to start terminating instances" +msgstr "" + +#: ../nova/api/ec2/cloud.py:813 +#, python-format +msgid "Reboot instance %r" +msgstr "" + +#: ../nova/api/ec2/cloud.py:850 +#, python-format +msgid "De-registering image %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:858 +#, python-format +msgid "Registered image %(image_location)s with id %(image_id)s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:865 ../nova/api/ec2/cloud.py:880 +#, python-format +msgid "attribute not supported: %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:870 +#, python-format +msgid "invalid id: %s" +msgstr "" + +#: ../nova/api/ec2/cloud.py:883 +msgid "user or group not specified" +msgstr "" + +#: ../nova/api/ec2/cloud.py:885 +msgid "only group \"all\" is supported" +msgstr "" + +#: ../nova/api/ec2/cloud.py:887 +msgid "operation_type must be add or remove" +msgstr "" + +#: ../nova/api/ec2/cloud.py:888 +#, python-format +msgid "Updating image %s publicity" +msgstr "" + +#: ../bin/nova-api.py:52 +#, python-format +msgid "Using paste.deploy config at: %s" +msgstr "" + +#: ../bin/nova-api.py:57 +#, python-format +msgid "No paste configuration for app: %s" +msgstr "" + +#: ../bin/nova-api.py:59 +#, python-format +msgid "" +"App Config: %(api)s\n" +"%(config)r" +msgstr "" + +#: ../bin/nova-api.py:64 +#, python-format +msgid "Running %s API" +msgstr "" + +#: ../bin/nova-api.py:69 +#, python-format +msgid "No known API applications configured in %s." +msgstr "" + +#: ../bin/nova-api.py:83 +#, python-format +msgid "Starting nova-api node (version %s)" +msgstr "" + +#: ../bin/nova-api.py:89 +#, python-format +msgid "No paste configuration found for: %s" +msgstr "" + +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:84 +#, python-format +msgid "Argument %(key)s value %(value)s is too short." +msgstr "" + +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:89 +#, python-format +msgid "Argument %(key)s value %(value)s contains invalid characters." +msgstr "" + +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:94 +#, python-format +msgid "Argument %(key)s value %(value)s starts with a hyphen." +msgstr "" + +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:102 +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:130 +#, python-format +msgid "Argument %s is required." +msgstr "" + +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:117 +#, python-format +msgid "" +"Argument %(key)s may not take value %(value)s. Valid values are ['true', " +"'false']." +msgstr "" + +#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:163 +#, python-format +msgid "" +"Created VDI %(vdi_ref)s (%(label)s, %(size)s, %(read_only)s) on %(sr_ref)s." +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:67 +#, python-format +msgid "Attempted to create non-unique name %s" +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:105 +#, python-format +msgid "Starting VM %s..." +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:108 +#, python-format +msgid "Spawning VM %(instance_name)s created %(vm_ref)s." +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:120 +#, python-format +msgid "Instance %s: booted" +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:164 +#, python-format +msgid "Instance not present %s" +msgstr "" + +#. TODO(sirp): Add quiesce and VSS locking support when Windows support +#. is added +#: ../nova/virt/xenapi/vmops.py:193 +#, python-format +msgid "Starting snapshot for VM %s" +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:201 +#, python-format +msgid "Unable to Snapshot %(vm_ref)s: %(exc)s" +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:212 +#, python-format +msgid "Finished snapshot and upload for VM %s" +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:262 +#, python-format +msgid "VM %(vm)s already halted, skipping shutdown..." +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:444 +#, python-format +msgid "" +"TIMEOUT: The call to %(method)s timed out. VM id=%(instance_id)s; args=" +"%(strargs)s" +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:447 +#, python-format +msgid "" +"The call to %(method)s returned an error: %(e)s. VM id=%(instance_id)s; args=" +"%(strargs)s" +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:638 +#, python-format +msgid "OpenSSL error: %s" +msgstr "" + +#: ../nova/tests/test_compute.py:148 +#, python-format +msgid "Running instances: %s" +msgstr "" + +#: ../nova/tests/test_compute.py:154 +#, python-format +msgid "After terminating instances: %s" +msgstr "" + +#: ../nova/cloudpipe/pipelib.py:45 +msgid "Template for script to run on cloudpipe instance boot" +msgstr "" + +#: ../nova/cloudpipe/pipelib.py:48 +msgid "Network to push into openvpn config" +msgstr "" + +#: ../nova/cloudpipe/pipelib.py:51 +msgid "Netmask to push into openvpn config" +msgstr "" + +#: ../nova/cloudpipe/pipelib.py:97 +#, python-format +msgid "Launching VPN for %s" +msgstr "" + +#: ../nova/image/s3.py:89 +#, python-format +msgid "Image %s could not be found" +msgstr "" + +#: ../nova/api/ec2/__init__.py:126 +msgid "Too many failed authentications." +msgstr "" + +#: ../nova/api/ec2/__init__.py:136 +#, python-format +msgid "" +"Access key %(access_key)s has had %(failures)d failed authentications and " +"will be locked out for %(lock_mins)d minutes." +msgstr "" + +#: ../nova/api/ec2/__init__.py:174 ../nova/objectstore/handler.py:140 +#, python-format +msgid "Authentication Failure: %s" +msgstr "" + +#: ../nova/api/ec2/__init__.py:187 +#, python-format +msgid "Authenticated Request For %(uname)s:%(pname)s)" +msgstr "" + +#: ../nova/api/ec2/__init__.py:212 +#, python-format +msgid "action: %s" +msgstr "" + +#: ../nova/api/ec2/__init__.py:214 +#, python-format +msgid "arg: %(key)s\t\tval: %(value)s" +msgstr "" + +#: ../nova/api/ec2/__init__.py:286 +#, python-format +msgid "" +"Unauthorized request for controller=%(controller)s and action=%(action)s" +msgstr "" + +#: ../nova/api/ec2/__init__.py:319 +#, python-format +msgid "InstanceNotFound raised: %s" +msgstr "" + +#: ../nova/api/ec2/__init__.py:325 +#, python-format +msgid "VolumeNotFound raised: %s" +msgstr "" + +#: ../nova/api/ec2/__init__.py:331 +#, python-format +msgid "NotFound raised: %s" +msgstr "" + +#: ../nova/api/ec2/__init__.py:334 +#, python-format +msgid "ApiError raised: %s" +msgstr "" + +#: ../nova/api/ec2/__init__.py:343 +#, python-format +msgid "Unexpected error raised: %s" +msgstr "" + +#: ../nova/api/ec2/__init__.py:348 +msgid "An unknown error has occurred. Please try your request again." +msgstr "" + +#: ../nova/auth/dbdriver.py:84 +#, python-format +msgid "User %s already exists" +msgstr "" + +#: ../nova/auth/dbdriver.py:106 ../nova/auth/ldapdriver.py:206 +#, python-format +msgid "Project can't be created because manager %s doesn't exist" +msgstr "" + +#: ../nova/auth/dbdriver.py:122 ../nova/auth/ldapdriver.py:217 +#, python-format +msgid "Project can't be created because user %s doesn't exist" +msgstr "" + +#: ../nova/auth/dbdriver.py:135 ../nova/auth/ldapdriver.py:203 +#, python-format +msgid "Project can't be created because project %s already exists" +msgstr "" + +#: ../nova/auth/dbdriver.py:157 ../nova/auth/ldapdriver.py:241 +#, python-format +msgid "Project can't be modified because manager %s doesn't exist" +msgstr "" + +#: ../nova/auth/dbdriver.py:245 +#, python-format +msgid "User \"%s\" not found" +msgstr "" + +#: ../nova/auth/dbdriver.py:248 +#, python-format +msgid "Project \"%s\" not found" +msgstr "" + +#: ../nova/virt/xenapi_conn.py:129 +msgid "" +"Must specify xenapi_connection_url, xenapi_connection_username (optionally), " +"and xenapi_connection_password to use connection_type=xenapi" +msgstr "" + +#: ../nova/virt/xenapi_conn.py:301 +#, python-format +msgid "Task [%(name)s] %(task)s status: success %(result)s" +msgstr "" + +#: ../nova/virt/xenapi_conn.py:307 +#, python-format +msgid "Task [%(name)s] %(task)s status: %(status)s %(error_info)s" +msgstr "" + +#: ../nova/virt/xenapi_conn.py:321 ../nova/virt/xenapi_conn.py:334 +#, python-format +msgid "Got exception: %s" +msgstr "" + +#: ../nova/compute/monitor.py:259 +#, python-format +msgid "updating %s..." +msgstr "" + +#: ../nova/compute/monitor.py:289 +msgid "unexpected error during update" +msgstr "" + +#: ../nova/compute/monitor.py:356 +#, python-format +msgid "Cannot get blockstats for \"%(disk)s\" on \"%(iid)s\"" +msgstr "" + +#: ../nova/compute/monitor.py:379 +#, python-format +msgid "Cannot get ifstats for \"%(interface)s\" on \"%(iid)s\"" +msgstr "" + +#: ../nova/compute/monitor.py:414 +msgid "unexpected exception getting connection" +msgstr "" + +#: ../nova/compute/monitor.py:429 +#, python-format +msgid "Found instance: %s" +msgstr "" + +#: ../nova/api/ec2/apirequest.py:99 +#, python-format +msgid "" +"Unsupported API request: controller = %(controller)s, action = %(action)s" +msgstr "" + +#: ../nova/api/openstack/__init__.py:54 +#, python-format +msgid "Caught error: %s" +msgstr "" + +#: ../nova/api/openstack/__init__.py:75 +msgid "Including admin operations in API." +msgstr "" + +#: ../nova/console/xvp.py:99 +msgid "Rebuilding xvp conf" +msgstr "" + +#: ../nova/console/xvp.py:116 +#, python-format +msgid "Re-wrote %s" +msgstr "" + +#: ../nova/console/xvp.py:121 +msgid "Stopping xvp" +msgstr "" + +#: ../nova/console/xvp.py:134 +msgid "Starting xvp" +msgstr "" + +#: ../nova/console/xvp.py:141 +#, python-format +msgid "Error starting xvp: %s" +msgstr "" + +#: ../nova/console/xvp.py:144 +msgid "Restarting xvp" +msgstr "" + +#: ../nova/console/xvp.py:146 +msgid "xvp not running..." +msgstr "" + +#: ../bin/nova-manage.py:272 +msgid "" +"The above error may show that the database has not been created.\n" +"Please create a database using nova-manage sync db before running this " +"command." +msgstr "" + +#: ../bin/nova-manage.py:426 +msgid "" +"No more networks available. If this is a new installation, you need\n" +"to call something like this:\n" +"\n" +" nova-manage network create 10.0.0.0/8 10 64\n" +"\n" +msgstr "" + +#: ../bin/nova-manage.py:431 +msgid "" +"The above error may show that the certificate db has not been created.\n" +"Please create a database by running a nova-api server on this host." +msgstr "" + +#: ../nova/virt/disk.py:69 +#, python-format +msgid "Failed to load partition: %s" +msgstr "" + +#: ../nova/virt/disk.py:91 +#, python-format +msgid "Failed to mount filesystem: %s" +msgstr "" + +#: ../nova/virt/disk.py:124 +#, python-format +msgid "nbd device %s did not show up" +msgstr "" + +#: ../nova/virt/disk.py:128 +#, python-format +msgid "Could not attach image to loopback: %s" +msgstr "" + +#: ../nova/virt/disk.py:151 +msgid "No free nbd devices" +msgstr "" + +#: ../doc/ext/nova_todo.py:46 +#, python-format +msgid "%(filename)s, line %(line_info)d" +msgstr "" + +#. FIXME(chiradeep): implement this +#: ../nova/virt/hyperv.py:118 +msgid "In init host" +msgstr "" + +#: ../nova/virt/hyperv.py:131 +#, python-format +msgid "Attempt to create duplicate vm %s" +msgstr "" + +#: ../nova/virt/hyperv.py:148 +#, python-format +msgid "Starting VM %s " +msgstr "" + +#: ../nova/virt/hyperv.py:150 +#, python-format +msgid "Started VM %s " +msgstr "" + +#: ../nova/virt/hyperv.py:152 +#, python-format +msgid "spawn vm failed: %s" +msgstr "" + +#: ../nova/virt/hyperv.py:169 +#, python-format +msgid "Failed to create VM %s" +msgstr "" + +#: ../nova/virt/hyperv.py:188 +#, python-format +msgid "Set memory for vm %s..." +msgstr "" + +#: ../nova/virt/hyperv.py:198 +#, python-format +msgid "Set vcpus for vm %s..." +msgstr "" + +#: ../nova/virt/hyperv.py:202 +#, python-format +msgid "Creating disk for %(vm_name)s by attaching disk file %(vhdfile)s" +msgstr "" + +#: ../nova/virt/hyperv.py:227 +#, python-format +msgid "Failed to add diskdrive to VM %s" +msgstr "" + +#: ../nova/virt/hyperv.py:230 +#, python-format +msgid "New disk drive path is %s" +msgstr "" + +#: ../nova/virt/hyperv.py:247 +#, python-format +msgid "Failed to add vhd file to VM %s" +msgstr "" + +#: ../nova/virt/hyperv.py:249 +#, python-format +msgid "Created disk for %s" +msgstr "" + +#: ../nova/virt/hyperv.py:253 +#, python-format +msgid "Creating nic for %s " +msgstr "" + +#: ../nova/virt/hyperv.py:272 +msgid "Failed creating a port on the external vswitch" +msgstr "" + +#: ../nova/virt/hyperv.py:273 +#, python-format +msgid "Failed creating port for %s" +msgstr "" + +#: ../nova/virt/hyperv.py:276 +#, python-format +msgid "Created switch port %(vm_name)s on switch %(ext_path)s" +msgstr "" + +#: ../nova/virt/hyperv.py:286 +#, python-format +msgid "Failed to add nic to VM %s" +msgstr "" + +#: ../nova/virt/hyperv.py:288 +#, python-format +msgid "Created nic for %s " +msgstr "" + +#: ../nova/virt/hyperv.py:321 +#, python-format +msgid "WMI job failed: %s" +msgstr "" + +#: ../nova/virt/hyperv.py:325 +#, python-format +msgid "WMI job succeeded: %(desc)s, Elapsed=%(elap)s " +msgstr "" + +#: ../nova/virt/hyperv.py:361 +#, python-format +msgid "Got request to destroy vm %s" +msgstr "" + +#: ../nova/virt/hyperv.py:386 +#, python-format +msgid "Failed to destroy vm %s" +msgstr "" + +#: ../nova/virt/hyperv.py:393 +#, python-format +msgid "Del: disk %(vhdfile)s vm %(instance_name)s" +msgstr "" + +#: ../nova/virt/hyperv.py:415 +#, python-format +msgid "" +"Got Info for vm %(instance_id)s: state=%(state)s, mem=%(memusage)s, num_cpu=" +"%(numprocs)s, cpu_time=%(uptime)s" +msgstr "" + +#: ../nova/virt/hyperv.py:451 +#, python-format +msgid "Successfully changed vm state of %(vm_name)s to %(req_state)s" +msgstr "" + +#: ../nova/virt/hyperv.py:454 +#, python-format +msgid "Failed to change vm state of %(vm_name)s to %(req_state)s" +msgstr "" + +#: ../nova/compute/api.py:71 +#, python-format +msgid "Instance %d was not found in get_network_topic" +msgstr "" + +#: ../nova/compute/api.py:77 +#, python-format +msgid "Instance %d has no host" +msgstr "" + +#: ../nova/compute/api.py:96 +#, python-format +msgid "Quota exceeeded for %(pid)s, tried to run %(min_count)s instances" +msgstr "" + +#: ../nova/compute/api.py:98 +#, python-format +msgid "" +"Instance quota exceeded. You can only run %s more instances of this type." +msgstr "" + +#: ../nova/compute/api.py:113 +msgid "Creating a raw instance" +msgstr "" + +#: ../nova/compute/api.py:162 +#, python-format +msgid "Going to run %s instances..." +msgstr "" + +#: ../nova/compute/api.py:189 +#, python-format +msgid "Casting to scheduler for %(pid)s/%(uid)s's instance %(instance_id)s" +msgstr "" + +#: ../nova/compute/api.py:293 +#, python-format +msgid "Going to try to terminate %s" +msgstr "" + +#: ../nova/compute/api.py:297 +#, python-format +msgid "Instance %d was not found during terminate" +msgstr "" + +#: ../nova/compute/api.py:302 +#, python-format +msgid "Instance %d is already being terminated" +msgstr "" + +#: ../nova/compute/api.py:471 +#, python-format +msgid "Invalid device specified: %s. Example device: /dev/vdb" +msgstr "" + +#: ../nova/compute/api.py:486 +msgid "Volume isn't attached to anything!" +msgstr "" + +#: ../nova/rpc.py:95 +#, python-format +msgid "" +"AMQP server on %(fl_host)s:%(fl_port)d is unreachable. Trying again in " +"%(fl_intv)d seconds." +msgstr "" + +#: ../nova/rpc.py:100 +#, python-format +msgid "Unable to connect to AMQP server after %d tries. Shutting down." +msgstr "" + +#: ../nova/rpc.py:119 +msgid "Reconnected to queue" +msgstr "" + +#: ../nova/rpc.py:126 +msgid "Failed to fetch message from queue" +msgstr "" + +#: ../nova/rpc.py:156 +#, python-format +msgid "Initing the Adapter Consumer for %s" +msgstr "" + +#: ../nova/rpc.py:171 +#, python-format +msgid "received %s" +msgstr "" + +#. NOTE(vish): we may not want to ack here, but that means that bad +#. messages stay in the queue indefinitely, so for now +#. we just log the message and send an error string +#. back to the caller +#: ../nova/rpc.py:184 +#, python-format +msgid "no method for message: %s" +msgstr "" + +#: ../nova/rpc.py:185 +#, python-format +msgid "No method for message: %s" +msgstr "" + +#: ../nova/rpc.py:246 +#, python-format +msgid "Returning exception %s to caller" +msgstr "" + +#: ../nova/rpc.py:287 +#, python-format +msgid "unpacked context: %s" +msgstr "" + +#: ../nova/rpc.py:306 +msgid "Making asynchronous call..." +msgstr "" + +#: ../nova/rpc.py:309 +#, python-format +msgid "MSG_ID is %s" +msgstr "" + +#: ../nova/rpc.py:347 +msgid "Making asynchronous cast..." +msgstr "" + +#: ../nova/rpc.py:357 +#, python-format +msgid "response %s" +msgstr "" + +#: ../nova/rpc.py:366 +#, python-format +msgid "topic is %s" +msgstr "" + +#: ../nova/rpc.py:367 +#, python-format +msgid "message %s" +msgstr "" + +#: ../nova/volume/driver.py:78 +#, python-format +msgid "Recovering from a failed execute. Try number %s" +msgstr "" + +#: ../nova/volume/driver.py:87 +#, python-format +msgid "volume group %s doesn't exist" +msgstr "" + +#: ../nova/volume/driver.py:220 +#, python-format +msgid "FAKE AOE: %s" +msgstr "" + +#: ../nova/volume/driver.py:233 +msgid "Skipping ensure_export. No iscsi_target " +msgstr "" + +#: ../nova/volume/driver.py:279 ../nova/volume/driver.py:288 +msgid "Skipping remove_export. No iscsi_target " +msgstr "" + +#: ../nova/volume/driver.py:347 +#, python-format +msgid "FAKE ISCSI: %s" +msgstr "" + +#: ../nova/volume/driver.py:359 +#, python-format +msgid "rbd has no pool %s" +msgstr "" + +#: ../nova/volume/driver.py:414 +#, python-format +msgid "Sheepdog is not working: %s" +msgstr "" + +#: ../nova/volume/driver.py:416 +msgid "Sheepdog is not working" +msgstr "" + +#: ../nova/wsgi.py:68 +#, python-format +msgid "Starting %(arg0)s on %(host)s:%(port)s" +msgstr "" + +#: ../nova/wsgi.py:147 +msgid "You must implement __call__" +msgstr "" + +#: ../bin/nova-instancemonitor.py:55 +msgid "Starting instance monitor" +msgstr "" + +#: ../bin/nova-dhcpbridge.py:58 +msgid "leasing ip" +msgstr "" + +#: ../bin/nova-dhcpbridge.py:73 +msgid "Adopted old lease or got a change of mac/hostname" +msgstr "" + +#: ../bin/nova-dhcpbridge.py:80 +msgid "releasing ip" +msgstr "" + +#: ../bin/nova-dhcpbridge.py:123 +#, python-format +msgid "" +"Called %(action)s for mac %(mac)s with ip %(ip)s and hostname %(hostname)s " +"on interface %(interface)s" +msgstr "" + +#: ../nova/network/manager.py:139 +msgid "setting network host" +msgstr "" + +#: ../nova/network/manager.py:194 +#, python-format +msgid "Leasing IP %s" +msgstr "" + +#: ../nova/network/manager.py:198 +#, python-format +msgid "IP %s leased that isn't associated" +msgstr "" + +#: ../nova/network/manager.py:202 +#, python-format +msgid "IP %(address)s leased to bad mac %(inst_addr)s vs %(mac)s" +msgstr "" + +#: ../nova/network/manager.py:210 +#, python-format +msgid "IP %s leased that was already deallocated" +msgstr "" + +#: ../nova/network/manager.py:215 +#, python-format +msgid "Releasing IP %s" +msgstr "" + +#: ../nova/network/manager.py:219 +#, python-format +msgid "IP %s released that isn't associated" +msgstr "" + +#: ../nova/network/manager.py:223 +#, python-format +msgid "IP %(address)s released from bad mac %(inst_addr)s vs %(mac)s" +msgstr "" + +#: ../nova/network/manager.py:226 +#, python-format +msgid "IP %s released that was not leased" +msgstr "" + +#: ../nova/network/manager.py:461 +#, python-format +msgid "Dissassociated %s stale fixed ip(s)" +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:57 +#, python-format +msgid "Introducing %s..." +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:74 +#, python-format +msgid "Introduced %(label)s as %(sr_ref)s." +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:78 +msgid "Unable to create Storage Repository" +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:90 +#, python-format +msgid "Unable to find SR from VBD %s" +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:96 +#, python-format +msgid "Forgetting SR %s ... " +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:101 +#, python-format +msgid "Ignoring exception %(exc)s when getting PBDs for %(sr_ref)s" +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:107 +#, python-format +msgid "Ignoring exception %(exc)s when unplugging PBD %(pbd)s" +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:111 +#, python-format +msgid "Forgetting SR %s done." +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:113 +#, python-format +msgid "Ignoring exception %(exc)s when forgetting SR %(sr_ref)s" +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:123 +#, python-format +msgid "Unable to introduce VDI on SR %s" +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:128 +#, python-format +msgid "Unable to get record of VDI %s on" +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:146 +#, python-format +msgid "Unable to introduce VDI for SR %s" +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:175 +#, python-format +msgid "Unable to obtain target information %(device_path)s, %(mountpoint)s" +msgstr "" + +#: ../nova/virt/xenapi/volume_utils.py:197 +#, python-format +msgid "Mountpoint cannot be translated: %s" +msgstr "" + +#: ../nova/objectstore/image.py:262 +#, python-format +msgid "Failed to decrypt private key: %s" +msgstr "" + +#: ../nova/objectstore/image.py:269 +#, python-format +msgid "Failed to decrypt initialization vector: %s" +msgstr "" + +#: ../nova/objectstore/image.py:277 +#, python-format +msgid "Failed to decrypt image file %(image_file)s: %(err)s" +msgstr "" + +#: ../nova/objectstore/handler.py:106 +#, python-format +msgid "Unknown S3 value type %r" +msgstr "" + +#: ../nova/objectstore/handler.py:137 +msgid "Authenticated request" +msgstr "" + +#: ../nova/objectstore/handler.py:182 +msgid "List of buckets requested" +msgstr "" + +#: ../nova/objectstore/handler.py:209 +#, python-format +msgid "List keys for bucket %s" +msgstr "" + +#: ../nova/objectstore/handler.py:217 +#, python-format +msgid "Unauthorized attempt to access bucket %s" +msgstr "" + +#: ../nova/objectstore/handler.py:235 +#, python-format +msgid "Creating bucket %s" +msgstr "" + +#: ../nova/objectstore/handler.py:245 +#, python-format +msgid "Deleting bucket %s" +msgstr "" + +#: ../nova/objectstore/handler.py:249 +#, python-format +msgid "Unauthorized attempt to delete bucket %s" +msgstr "" + +#: ../nova/objectstore/handler.py:273 +#, python-format +msgid "Getting object: %(bname)s / %(nm)s" +msgstr "" + +#: ../nova/objectstore/handler.py:276 +#, python-format +msgid "Unauthorized attempt to get object %(nm)s from bucket %(bname)s" +msgstr "" + +#: ../nova/objectstore/handler.py:296 +#, python-format +msgid "Putting object: %(bname)s / %(nm)s" +msgstr "" + +#: ../nova/objectstore/handler.py:299 +#, python-format +msgid "Unauthorized attempt to upload object %(nm)s to bucket %(bname)s" +msgstr "" + +#: ../nova/objectstore/handler.py:318 +#, python-format +msgid "Deleting object: %(bname)s / %(nm)s" +msgstr "" + +#: ../nova/objectstore/handler.py:322 +#, python-format +msgid "Unauthorized attempt to delete object %(nm)s from bucket %(bname)s" +msgstr "" + +#: ../nova/objectstore/handler.py:396 +#, python-format +msgid "Not authorized to upload image: invalid directory %s" +msgstr "" + +#: ../nova/objectstore/handler.py:404 +#, python-format +msgid "Not authorized to upload image: unauthorized bucket %s" +msgstr "" + +#: ../nova/objectstore/handler.py:409 +#, python-format +msgid "Starting image upload: %s" +msgstr "" + +#: ../nova/objectstore/handler.py:423 +#, python-format +msgid "Not authorized to update attributes of image %s" +msgstr "" + +#: ../nova/objectstore/handler.py:431 +#, python-format +msgid "Toggling publicity flag of image %(image_id)s %(newstatus)r" +msgstr "" + +#. other attributes imply update +#: ../nova/objectstore/handler.py:436 +#, python-format +msgid "Updating user fields on image %s" +msgstr "" + +#: ../nova/objectstore/handler.py:450 +#, python-format +msgid "Unauthorized attempt to delete image %s" +msgstr "" + +#: ../nova/objectstore/handler.py:455 +#, python-format +msgid "Deleted image: %s" +msgstr "" + +#: ../nova/auth/manager.py:259 +#, python-format +msgid "Looking up user: %r" +msgstr "" + +#: ../nova/auth/manager.py:263 +#, python-format +msgid "Failed authorization for access key %s" +msgstr "" + +#: ../nova/auth/manager.py:264 +#, python-format +msgid "No user found for access key %s" +msgstr "" + +#: ../nova/auth/manager.py:270 +#, python-format +msgid "Using project name = user name (%s)" +msgstr "" + +#: ../nova/auth/manager.py:277 +#, python-format +msgid "failed authorization: no project named %(pjid)s (user=%(uname)s)" +msgstr "" + +#: ../nova/auth/manager.py:279 +#, python-format +msgid "No project called %s could be found" +msgstr "" + +#: ../nova/auth/manager.py:287 +#, python-format +msgid "" +"Failed authorization: user %(uname)s not admin and not member of project " +"%(pjname)s" +msgstr "" + +#: ../nova/auth/manager.py:289 +#, python-format +msgid "User %(uid)s is not a member of project %(pjid)s" +msgstr "" + +#: ../nova/auth/manager.py:298 ../nova/auth/manager.py:309 +#, python-format +msgid "Invalid signature for user %s" +msgstr "" + +#: ../nova/auth/manager.py:299 ../nova/auth/manager.py:310 +msgid "Signature does not match" +msgstr "" + +#: ../nova/auth/manager.py:380 +msgid "Must specify project" +msgstr "" + +#: ../nova/auth/manager.py:414 +#, python-format +msgid "The %s role can not be found" +msgstr "" + +#: ../nova/auth/manager.py:416 +#, python-format +msgid "The %s role is global only" +msgstr "" + +#: ../nova/auth/manager.py:420 +#, python-format +msgid "Adding role %(role)s to user %(uid)s in project %(pid)s" +msgstr "" + +#: ../nova/auth/manager.py:423 +#, python-format +msgid "Adding sitewide role %(role)s to user %(uid)s" +msgstr "" + +#: ../nova/auth/manager.py:448 +#, python-format +msgid "Removing role %(role)s from user %(uid)s on project %(pid)s" +msgstr "" + +#: ../nova/auth/manager.py:451 +#, python-format +msgid "Removing sitewide role %(role)s from user %(uid)s" +msgstr "" + +#: ../nova/auth/manager.py:515 +#, python-format +msgid "Created project %(name)s with manager %(manager_user)s" +msgstr "" + +#: ../nova/auth/manager.py:533 +#, python-format +msgid "modifying project %s" +msgstr "" + +#: ../nova/auth/manager.py:545 +#, python-format +msgid "Adding user %(uid)s to project %(pid)s" +msgstr "" + +#: ../nova/auth/manager.py:566 +#, python-format +msgid "Remove user %(uid)s from project %(pid)s" +msgstr "" + +#: ../nova/auth/manager.py:592 +#, python-format +msgid "Deleting project %s" +msgstr "" + +#: ../nova/auth/manager.py:650 +#, python-format +msgid "Created user %(rvname)s (admin: %(rvadmin)r)" +msgstr "" + +#: ../nova/auth/manager.py:659 +#, python-format +msgid "Deleting user %s" +msgstr "" + +#: ../nova/auth/manager.py:669 +#, python-format +msgid "Access Key change for user %s" +msgstr "" + +#: ../nova/auth/manager.py:671 +#, python-format +msgid "Secret Key change for user %s" +msgstr "" + +#: ../nova/auth/manager.py:673 +#, python-format +msgid "Admin status set to %(admin)r for user %(uid)s" +msgstr "" + +#: ../nova/auth/manager.py:722 +#, python-format +msgid "No vpn data for project %s" +msgstr "" + +#: ../nova/service.py:161 +#, python-format +msgid "Starting %(topic)s node (version %(vcs_string)s)" +msgstr "" + +#: ../nova/service.py:174 +msgid "Service killed that has no database entry" +msgstr "" + +#: ../nova/service.py:195 +msgid "The service database object disappeared, Recreating it." +msgstr "" + +#: ../nova/service.py:207 +msgid "Recovered model server connection!" +msgstr "" + +#: ../nova/service.py:213 +msgid "model server went away" +msgstr "" + +#: ../nova/auth/ldapdriver.py:149 +#, python-format +msgid "LDAP user %s already exists" +msgstr "" + +#: ../nova/auth/ldapdriver.py:180 +#, python-format +msgid "LDAP object for %s doesn't exist" +msgstr "" + +#: ../nova/auth/ldapdriver.py:313 +#, python-format +msgid "User %s doesn't exist" +msgstr "" + +#: ../nova/auth/ldapdriver.py:435 +#, python-format +msgid "Group can't be created because group %s already exists" +msgstr "" + +#: ../nova/auth/ldapdriver.py:441 +#, python-format +msgid "Group can't be created because user %s doesn't exist" +msgstr "" + +#: ../nova/auth/ldapdriver.py:458 +#, python-format +msgid "User %s can't be searched in group because the user doesn't exist" +msgstr "" + +#: ../nova/auth/ldapdriver.py:470 +#, python-format +msgid "User %s can't be added to the group because the user doesn't exist" +msgstr "" + +#: ../nova/auth/ldapdriver.py:473 ../nova/auth/ldapdriver.py:484 +#, python-format +msgid "The group at dn %s doesn't exist" +msgstr "" + +#: ../nova/auth/ldapdriver.py:476 +#, python-format +msgid "User %(uid)s is already a member of the group %(group_dn)s" +msgstr "" + +#: ../nova/auth/ldapdriver.py:487 +#, python-format +msgid "User %s can't be removed from the group because the user doesn't exist" +msgstr "" + +#: ../nova/auth/ldapdriver.py:491 +#, python-format +msgid "User %s is not a member of the group" +msgstr "" + +#: ../nova/auth/ldapdriver.py:505 +#, python-format +msgid "" +"Attempted to remove the last member of a group. Deleting the group at %s " +"instead." +msgstr "" + +#: ../nova/auth/ldapdriver.py:512 +#, python-format +msgid "User %s can't be removed from all because the user doesn't exist" +msgstr "" + +#: ../nova/auth/ldapdriver.py:527 +#, python-format +msgid "Group at dn %s doesn't exist" +msgstr "" + +#: ../nova/virt/xenapi/network_utils.py:40 +#, python-format +msgid "Found non-unique network for bridge %s" +msgstr "" + +#: ../nova/virt/xenapi/network_utils.py:43 +#, python-format +msgid "Found no network for bridge %s" +msgstr "" + +#: ../nova/api/ec2/admin.py:97 +#, python-format +msgid "Creating new user: %s" +msgstr "" + +#: ../nova/api/ec2/admin.py:105 +#, python-format +msgid "Deleting user: %s" +msgstr "" + +#: ../nova/api/ec2/admin.py:127 +#, python-format +msgid "Adding role %(role)s to user %(user)s for project %(project)s" +msgstr "" + +#: ../nova/api/ec2/admin.py:131 +#, python-format +msgid "Adding sitewide role %(role)s to user %(user)s" +msgstr "" + +#: ../nova/api/ec2/admin.py:137 +#, python-format +msgid "Removing role %(role)s from user %(user)s for project %(project)s" +msgstr "" + +#: ../nova/api/ec2/admin.py:141 +#, python-format +msgid "Removing sitewide role %(role)s from user %(user)s" +msgstr "" + +#: ../nova/api/ec2/admin.py:146 ../nova/api/ec2/admin.py:223 +msgid "operation must be add or remove" +msgstr "" + +#: ../nova/api/ec2/admin.py:159 +#, python-format +msgid "Getting x509 for user: %(name)s on project: %(project)s" +msgstr "" + +#: ../nova/api/ec2/admin.py:177 +#, python-format +msgid "Create project %(name)s managed by %(manager_user)s" +msgstr "" + +#: ../nova/api/ec2/admin.py:190 +#, python-format +msgid "Modify project: %(name)s managed by %(manager_user)s" +msgstr "" + +#: ../nova/api/ec2/admin.py:200 +#, python-format +msgid "Delete project: %s" +msgstr "" + +#: ../nova/api/ec2/admin.py:214 +#, python-format +msgid "Adding user %(user)s to project %(project)s" +msgstr "" + +#: ../nova/api/ec2/admin.py:218 +#, python-format +msgid "Removing user %(user)s from project %(project)s" +msgstr "" diff --git a/setup.py b/setup.py index e3c45ce3e..c9393c508 100644 --- a/setup.py +++ b/setup.py @@ -19,9 +19,17 @@ import os import subprocess -from setuptools import setup, find_packages +from setuptools import find_packages from setuptools.command.sdist import sdist +try: + import DistUtilsExtra.auto +except ImportError: + print >> sys.stderr, 'To build nova you need https://launchpad.net/python-distutils-extra' + sys.exit(1) +assert DistUtilsExtra.auto.__version__ >= '2.18', 'needs DistUtilsExtra.auto >= 2.18' + + from nova.utils import parse_mailmap, str_dict_replace from nova import version @@ -75,7 +83,7 @@ try: except: pass -setup(name='nova', +DistUtilsExtra.auto.setup(name='nova', version=version.canonical_version_string(), description='cloud computing fabric controller', author='OpenStack', -- cgit From 9c0862b5f84cdb09b7ab0aafca669d30f261a666 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Mon, 14 Feb 2011 10:21:16 -0600 Subject: support for multiple IPs per network --- nova/virt/xenapi/vmops.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 575e53f80..db05a24ff 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -96,22 +96,36 @@ class VMOps(object): # write network info admin_context = context.get_admin_context() - #network = db.network_get_by_instance(admin_context, - # instance['id']) + # TODO(tr3buchet) - remove comment in multi-nic + # I've decided to go ahead and consider multiple IPs and networks + # at this stage even though they aren't implemented because these will + # be needed for multi-nic and there was no sense writing it for single + # network/single IP and then having to turn around and re-write it + IPs = db.fixed_ip_get_all_by_instance(admin_context, instance['id']) for network in db.network_get_all_by_instance(admin_context, instance['id']): + network_IPs = [ip for ip in IPs if ip.network_id == network.id] + + def ip_dict(ip): + return {'netmask': network['netmask'], + 'enabled': '1', + 'ip': ip.address} + mac_id = instance.mac_address.replace(':', '') location = 'vm-data/networking/%s' % mac_id mapping = {'label': network['label'], 'gateway': network['gateway'], 'mac': instance.mac_address, 'dns': [network['dns']], - 'ips': [{'netmask': network['netmask'], - 'enabled': '1', - 'ip': '192.168.3.3'}]} # <===== CHANGE!!!! + 'ips': [ip_dict(ip) for ip in network_IPs]} self.write_to_param_xenstore(vm_ref, {location: mapping}) + # TODO(tr3buchet) - remove comment in multi-nic + # this bit here about creating the vifs will be updated + # in multi-nic to handle multiple IPs on the same network + # and multiple networks + # for now it works as there is only one of each bridge = network['bridge'] network_ref = \ NetworkHelper.find_network_with_bridge(self._session, bridge) -- cgit From ee26d0827b7ad3e4d7869614835fe58abe32dfc8 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz Date: Mon, 14 Feb 2011 17:43:39 +0100 Subject: Got rid of BadParameter, just using standard python ValueError --- nova/network/manager.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nova/network/manager.py b/nova/network/manager.py index 8eb9f041b..d911844a1 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -505,6 +505,12 @@ class VlanManager(NetworkManager): def create_networks(self, context, cidr, num_networks, network_size, cidr_v6, vlan_start, vpn_start): """Create networks based on parameters.""" + # Check that num_networks + vlan_start is not > 4094, fixes lp708025 + if num_networks + vlan_start > 4094: + raise ValueError(_('The sum between the number of networks and' + ' the vlan start cannot be greater' + ' than 4094')) + fixed_net = IPy.IP(cidr) fixed_net_v6 = IPy.IP(cidr_v6) network_size_v6 = 1 << 64 -- cgit From 3f96e6dbf12533355aa6722eeb498814df076aea Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Mon, 14 Feb 2011 12:32:33 -0600 Subject: added call to reset_network from openstack api down to vmops --- nova/api/openstack/servers.py | 14 ++++++++++++++ nova/compute/api.py | 9 ++++++++- nova/compute/manager.py | 12 ++++++++++++ nova/virt/xenapi_conn.py | 4 ++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 8cbcebed2..c604bd215 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -242,6 +242,20 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + def reset_network(self, req, id): + """ + admin only operation which resets networking on an instance + + """ + context = req.environ['nova.context'] + try: + self.compute_api.reset_network(context, id) + except: + readable = traceback.format_exc() + LOG.exception(_("Compute.api::reset_network %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + def pause(self, req, id): """ Permit Admins to Pause the server. """ ctxt = req.environ['nova.context'] diff --git a/nova/compute/api.py b/nova/compute/api.py index 6a3fe08b6..43332ed27 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -1,4 +1,4 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# vim: tabstop=5 shiftwidth=4 softtabstop=4 # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. @@ -463,6 +463,13 @@ class API(base.Base): instance = self.get(context, instance_id) return instance['locked'] + def reset_network(self, context, instance_id): + """ + resets networking on the instance + + """ + self._cast_compute_message('reset_network', context, instance_id) + def attach_volume(self, context, instance_id, volume_id, device): if not re.match("^/dev/[a-z]d[a-z]+$", device): raise exception.ApiError(_("Invalid device specified: %s. " diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6f09ce674..b03f58693 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -494,6 +494,18 @@ class ComputeManager(manager.Manager): instance_ref = self.db.instance_get(context, instance_id) return instance_ref['locked'] + @checks_instance_lock + def reset_network(self, context, instance_id): + """ + resets the networking on the instance + + """ + context = context.elevated() + instance_ref = self.db.instance_get(context, instance_id) + LOG.debug(_('instance %s: reset network'), instance_id, + context=context) + self.driver.reset_network(instance_ref) + @exception.wrap_exception def get_console_output(self, context, instance_id): """Send the console output for an instance.""" diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 927f5905b..4e5442aa6 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -188,6 +188,10 @@ class XenAPIConnection(object): """resume the specified instance""" self._vmops.resume(instance, callback) + def reset_network(self, instance): + """reset networking for specified instance""" + self._vmops.reset_network(instance) + def get_info(self, instance_id): """Return data about VM instance""" return self._vmops.get_info(instance_id) -- cgit From a7f796c0a3dbccb74ba3fe51e3885c716bc88592 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Mon, 14 Feb 2011 10:43:22 -0800 Subject: fix for bug #716847 - if a volume has not been assigned to a host, then delete from db and skip rpc --- bin/nova-manage | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index 7835ca551..e4c0684c4 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -579,6 +579,13 @@ class VolumeCommands(object): ctxt = context.get_admin_context() volume = db.volume_get(ctxt, param2id(volume_id)) host = volume['host'] + + if not host: + print "Volume not yet assigned to host." + print "Deleting volume from database and skipping rpc." + db.volume_destroy(ctxt, param2id(volume_id)) + return + if volume['status'] == 'in-use': print "Volume is in-use." print "Detach volume from instance and then try again." -- cgit From 875c4e1bab5e364a23695e46df69f1b21d9a8200 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 12:44:07 -0600 Subject: Derp --- nova/db/sqlalchemy/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 8566bb91f..861d13716 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1933,7 +1933,7 @@ def migration_create(context, values): def migration_update(context, id, values): session = get_session() with session.begin(): - migration = migration_get(context, id, session=session) + migration = migration_get(context, id) migration.update(values) return migration -- cgit From 4f23e417bb5ac3db8ac28dfb4b032a3e233c9821 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 12:50:54 -0600 Subject: More fixes --- nova/compute/manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 7c9e918fb..3fd37d831 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -425,10 +425,11 @@ class ComputeManager(manager.Manager): def resize_instance(self, context, instance_id, migration_id): """Starts the migration of a running instance to another host""" migration_ref = self.db.migration_get(context, migration_id) + instance_ref = self.db.instance_get(context, instance_id) self.db.migration_update(context, migration_id, { 'status': 'migrating', }) - self.driver.migrate_disk_and_power_off(context, instance, + self.driver.migrate_disk_and_power_off(instance_ref, migration_ref['dest_host']) self.db.migration_update(context, migration_id, -- cgit From ee4cba7779daa5b2e7415fb69cabc698b7dd60da Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Mon, 14 Feb 2011 12:59:46 -0600 Subject: corrected model for table lookup --- nova/db/sqlalchemy/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f20f4e266..827f81ae2 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -609,7 +609,7 @@ def fixed_ip_get_instance(context, address): @require_context def fixed_ip_get_all_by_instance(context, instance_id): session = get_session() - rv = session.query(models.Network.fixed_ips).\ + rv = session.query(models.FixedIp).\ filter_by(instance_id=instance_id).\ filter_by(deleted=False) if not rv: -- cgit From 1631196f3f277608fb0569c7242a7d8391605d0d Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 13:38:05 -0600 Subject: wharrgarbl --- nova/virt/xenapi/vmops.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3b14390b4..d5b2c821c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -230,7 +230,7 @@ class VMOps(object): label = "%s-snapshot" % instance.name try: - _, template_vdi_uuids = VMHelper.create_snapshot( + vdi_ref, template_vdi_uuids = VMHelper.create_snapshot( self._session, instance.id, vm_ref, label) return Snapshot(self, instance, template_vdi_uuids) except self.XenAPI.Failure, exc: @@ -262,22 +262,21 @@ class VMOps(object): # Now power down the instance and transfer the COW VHD self._shutdown(instance, method='clean') - _, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], 'dest_name': 'cow.vhd'} self._session.async_call_plugin('data_transfer', 'transfer_vhd', {'params': pickle.dumps(params)}) return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] - def attach_disk(self, instance): + def attach_disk(self, instance):hh vm_ref = VMHelper.lookup(self._session, instance.name) params = { 'instance_id': instance.id } self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) - - _, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) VMHelper.scan_sr(self._session, instance.id, vm_vdi_rec['SR']) def resize(self, instance, flavor): -- cgit From 9f22390532332b955cb8d78ebfd8cf9670a63ac8 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 13:50:06 -0600 Subject: Snapshot correctly --- nova/virt/xenapi/vmops.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index d5b2c821c..fc5cd84e1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -211,10 +211,11 @@ class VMOps(object): def _get_snapshot(self, instance): class Snapshot(object): - def __init__(self, virt, instance, vdis): + def __init__(self, virt, instance, vm_ref, vdis): self.instance = instance self.vdi_uuids = vdis self.virt = virt + self.vm_ref = vm_ref def __enter__(self): return self @@ -230,9 +231,10 @@ class VMOps(object): label = "%s-snapshot" % instance.name try: - vdi_ref, template_vdi_uuids = VMHelper.create_snapshot( + template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( self._session, instance.id, vm_ref, label) - return Snapshot(self, instance, template_vdi_uuids) + return Snapshot(self, instance, template_vm_ref, + template_vdi_uuids) except self.XenAPI.Failure, exc: logging.error(_("Unable to Snapshot %(vm_ref)s: %(exc)s") % locals()) @@ -269,7 +271,7 @@ class VMOps(object): {'params': pickle.dumps(params)}) return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] - def attach_disk(self, instance):hh + def attach_disk(self, instance): vm_ref = VMHelper.lookup(self._session, instance.name) params = { 'instance_id': instance.id } -- cgit From 89ca05c457cb951e91060359493e5f830fe5eeda Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 14 Feb 2011 21:06:16 +0100 Subject: Use eventlet.green.subprocess instead of standard subprocess Eventlet's monkey patching causes the os.wait call in the standard subprocess module to be non-blocking. This means that if it happens to call self.wait on a Popen object that hasn't completely terminated it'll be left as a zombie and its fd's are also leaked. --- nova/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/utils.py b/nova/utils.py index 8d7ff1f64..ba71ebf39 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -25,7 +25,6 @@ import inspect import json import os import random -import subprocess import socket import struct import sys @@ -36,6 +35,7 @@ import netaddr from eventlet import event from eventlet import greenthread +from eventlet.green import subprocess from nova import exception from nova.exception import ProcessExecutionError -- cgit From fc8394a80b28f94561aa9ebf94c067ce2d1efd3b Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 14:25:00 -0600 Subject: Snapshot correctly --- nova/virt/xenapi/vmops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index fc5cd84e1..a327f1d36 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -22,6 +22,7 @@ Management class for VM-related functions (spawn, reboot, etc). import json import M2Crypto import os +import pickle import subprocess import tempfile import uuid @@ -262,7 +263,7 @@ class VMOps(object): {'params': pickle.dumps(params)}) # Now power down the instance and transfer the COW VHD - self._shutdown(instance, method='clean') + self._shutdown(instance, snapshot.vm_ref, method='clean') vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], -- cgit From 411d828fc3511a09420e579ceee65a9470242509 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 14:40:58 -0600 Subject: hurr --- nova/virt/xenapi/vm_utils.py | 38 +++++++++++++++++++++----------------- nova/virt/xenapi/vmops.py | 8 +++++--- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index e16662aad..2c4ae6aa2 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -238,6 +238,24 @@ class VMHelper(HelperBase): % locals()) return vdi_ref + @classmethod + def get_vdi_for_vm_safely(cls, session, vm_ref): + vdi_refs = VMHelper.lookup_vm_vdis(session, vm_ref) + if vdi_refs is None: + raise Exception(_("No VDIs found for VM %s") % vm_ref) + else: + num_vdis = len(vdi_refs) + if num_vdis != 1: + raise Exception(_("Unexpected number of VDIs (%(num_vdis)s) found" + " for VM %(vm_ref)s") % locals()) + + vdi_ref = vdi_refs[0] + vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) + return vdi_ref, vdi_rec + + + + @classmethod def create_snapshot(cls, session, instance_id, vm_ref, label): """ Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, @@ -248,7 +266,7 @@ class VMHelper(HelperBase): LOG.debug(_("Snapshotting VM %(vm_ref)s with label '%(label)s'...") % locals()) - vm_vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + vm_vdi_ref, vm_vdi_rec = self.get_vdi_for_vm_safely(session, vm_ref) vm_vdi_uuid = vm_vdi_rec["uuid"] sr_ref = vm_vdi_rec["SR"] @@ -256,7 +274,8 @@ class VMHelper(HelperBase): task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) template_vm_ref = session.wait_for_task(instance_id, task) - template_vdi_rec = get_vdi_for_vm_safely(session, template_vm_ref)[1] + template_vdi_rec = self.get_vdi_for_vm_safely(session, + template_vm_ref)[1] template_vdi_uuid = template_vdi_rec["uuid"] LOG.debug(_('Created snapshot %(template_vm_ref)s from' @@ -568,21 +587,6 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, return parent_uuid -def get_vdi_for_vm_safely(session, vm_ref): - vdi_refs = VMHelper.lookup_vm_vdis(session, vm_ref) - if vdi_refs is None: - raise Exception(_("No VDIs found for VM %s") % vm_ref) - else: - num_vdis = len(vdi_refs) - if num_vdis != 1: - raise Exception(_("Unexpected number of VDIs (%(num_vdis)s) found" - " for VM %(vm_ref)s") % locals()) - - vdi_ref = vdi_refs[0] - vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) - return vdi_ref, vdi_rec - - def find_sr(session): host = session.get_xenapi_host() srs = session.get_xenapi().SR.get_all() diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a327f1d36..6a7308c74 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -263,9 +263,10 @@ class VMOps(object): {'params': pickle.dumps(params)}) # Now power down the instance and transfer the COW VHD - self._shutdown(instance, snapshot.vm_ref, method='clean') + self._shutdown(instance, vm_ref, method='clean') - vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + vdi_ref, vm_vdi_rec = \ + VMHelper.get_vdi_for_vm_safely(session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], 'dest_name': 'cow.vhd'} self._session.async_call_plugin('data_transfer', 'transfer_vhd', @@ -279,7 +280,8 @@ class VMOps(object): self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) - vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + vdi_ref, vm_vdi_rec = \ + VMHelper.get_vdi_for_vm_safely(session, vm_ref) VMHelper.scan_sr(self._session, instance.id, vm_vdi_rec['SR']) def resize(self, instance, flavor): -- cgit From 7bb6122549ad5ac549465f0012020f8e5dc9d506 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 15:26:08 -0600 Subject: Some refactoring --- nova/compute/manager.py | 4 ++-- nova/virt/xenapi/vm_utils.py | 12 ++++++++---- nova/virt/xenapi/vmops.py | 13 ++++++++----- nova/virt/xenapi_conn.py | 2 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 8 ++++++-- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 3fd37d831..0b0966324 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -405,7 +405,7 @@ class ComputeManager(manager.Manager): migration_ref = self.db.migration_create(context, { 'instance_id': instance_id, 'source_host': instance_ref['host'], - 'dest_host': socket.gethostbyname(socket.gethostname()), + 'dest_host': socket.gethostname(), 'status': 'pre-migrating' }) LOG.audit(_('instance %s: migrating to '), instance_id, context=context) service = self.db.service_get_by_host_and_topic(context, @@ -459,7 +459,7 @@ class ComputeManager(manager.Manager): migration_ref['instance_id']) # this may get passed into the following spawn instead - disk_info = self.driver.attach_disk(context, instance_ref) + disk_info = self.driver.attach_disk(instance_ref) self.driver.spawn(context, instance_ref, disk=disk_info) self.db.migration_update(context, migration_id, diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 2c4ae6aa2..eeb5502ed 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -266,7 +266,7 @@ class VMHelper(HelperBase): LOG.debug(_("Snapshotting VM %(vm_ref)s with label '%(label)s'...") % locals()) - vm_vdi_ref, vm_vdi_rec = self.get_vdi_for_vm_safely(session, vm_ref) + vm_vdi_ref, vm_vdi_rec = cls.get_vdi_for_vm_safely(session, vm_ref) vm_vdi_uuid = vm_vdi_rec["uuid"] sr_ref = vm_vdi_rec["SR"] @@ -274,7 +274,7 @@ class VMHelper(HelperBase): task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) template_vm_ref = session.wait_for_task(instance_id, task) - template_vdi_rec = self.get_vdi_for_vm_safely(session, + template_vdi_rec = cls.get_vdi_for_vm_safely(session, template_vm_ref)[1] template_vdi_uuid = template_vdi_rec["uuid"] @@ -287,6 +287,12 @@ class VMHelper(HelperBase): #TODO(sirp): we need to assert only one parent, not parents two deep return template_vm_ref, [template_vdi_uuid, parent_uuid] + @classmethod + def get_sr(cls, session, sr_label='slices'): + """ Finds the SR named by the given name label and returns + the UUID """ + return session.call_xenapi('SR.get_by_name_label', sr_label)[0] + @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_id): """ Requests that the Glance plugin bundle the specified VDIs and @@ -504,8 +510,6 @@ class VMHelper(HelperBase): session.wait_for_task(instance_id, task) - - def get_rrd(host, uuid): """Return the VM RRD XML as a string""" try: diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6a7308c74..470b6ea8c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -266,7 +266,7 @@ class VMOps(object): self._shutdown(instance, vm_ref, method='clean') vdi_ref, vm_vdi_rec = \ - VMHelper.get_vdi_for_vm_safely(session, vm_ref) + VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], 'dest_name': 'cow.vhd'} self._session.async_call_plugin('data_transfer', 'transfer_vhd', @@ -277,12 +277,15 @@ class VMOps(object): vm_ref = VMHelper.lookup(self._session, instance.name) params = { 'instance_id': instance.id } - self._session.async_call_plugin('migration', 'move_vhds_into_sr', + new_base_copy_uuid, new_cow_uuid = self._session.async_call_plugin( + 'migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) - vdi_ref, vm_vdi_rec = \ - VMHelper.get_vdi_for_vm_safely(session, vm_ref) - VMHelper.scan_sr(self._session, instance.id, vm_vdi_rec['SR']) + # Now we rescan the SR so we find the VHDs + sr_ref = VMHelper.get_sr(self._session) + VMHelper.scan_sr(self._session, instance.id, sr_ref) + + return new_base_copy_uuid def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 2fddb8c7f..6869ce8d8 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -199,7 +199,7 @@ class XenAPIConnection(object): def attach_disk(self, instance): """Moves the copied VDIs into the SR""" - self._vmops.attach_disk(instance) + return self._vmops.attach_disk(instance) def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 0fb7b5806..71d4473c5 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -78,9 +78,12 @@ def move_vhds_into_sr(session, args): source_base_copy_path = "%s/base_copy.vhd" % source_image_path source_cow_path = "%s/cow.vhd" % source_image_path + new_base_copy_uuid = str(uuid.uuid4()) + new_cow_uuid = str(uuid.uuid4()) + temp_vhd_path = "%s/instance%d/" % (sr_temp_path, instance_id) - new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) - new_cow_path = "%s/%s.vhd" % (temp_vhd_path, str(uuid.uuid4())) + new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid) + new_cow_path = "%s/%s.vhd" % (temp_vhd_path, new_cow_uuid) os.mkdir(temp_vhd_path) shutil.move(source_base_copy_path, new_base_copy_path) @@ -94,6 +97,7 @@ def move_vhds_into_sr(session, args): shutil.move("%s/*.vhd" % temp_vhd_path, sr_path) os.rmdir(temp_vhd_path) + return (new_base_copy_uuid, new_cow_uuid) def transfer_vhd(session, args): -- cgit From fad5baf307b74a92fd5b9d8e2d1479f558e180aa Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 15:55:52 -0600 Subject: hurr --- nova/virt/xenapi/vm_utils.py | 12 ++++++++---- nova/virt/xenapi/vmops.py | 13 ++++++++----- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 7 ++++--- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index eeb5502ed..23f9547d7 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -504,10 +504,14 @@ class VMHelper(HelperBase): return {"Unable to retrieve diagnostics": e} @classmethod - def scan_sr(cls, session, instance_id, sr_ref): - LOG.debug(_("Re-scanning SR %s"), sr_ref) - task = session.call_xenapi('Async.SR.scan', sr_ref) - session.wait_for_task(instance_id, task) + def scan_sr(cls, session, instance_id=None, sr_ref=None): + if sr_ref: + LOG.debug(_("Re-scanning SR %s"), sr_ref) + task = session.call_xenapi('Async.SR.scan', sr_ref) + session.wait_for_task(instance_id, task) + else: + sr_ref = cls.get_sr(session) + session.call_xen_api('SR.scan', sr_ref) def get_rrd(host, uuid): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 470b6ea8c..17d42d542 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -276,14 +276,17 @@ class VMOps(object): def attach_disk(self, instance): vm_ref = VMHelper.lookup(self._session, instance.name) - params = { 'instance_id': instance.id } - new_base_copy_uuid, new_cow_uuid = self._session.async_call_plugin( - 'migration', 'move_vhds_into_sr', + new_base_copy_uuid = str(uuid.uuid4()) + + params = { 'instance_id': instance.id, + 'new_base_copy_uuid': new_base_copy_uuid, + 'new_cow_uuid': str(uuid.uuid4() } + + self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) # Now we rescan the SR so we find the VHDs - sr_ref = VMHelper.get_sr(self._session) - VMHelper.scan_sr(self._session, instance.id, sr_ref) + VMHelper.scan_sr(self._session) return new_base_copy_uuid diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 71d4473c5..e73480445 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -69,6 +69,9 @@ def move_vhds_into_sr(session, args): params = pickle.dumps(args) instance_id = params['instance_id'] + new_base_copy_uuid = params['new_base_copy_uuid'] + new_cow_uuid = params['new_cow_uuid'] + sr_path = get_sr_path(session) sr_temp_path = "%s/images/" % sr_path @@ -78,8 +81,6 @@ def move_vhds_into_sr(session, args): source_base_copy_path = "%s/base_copy.vhd" % source_image_path source_cow_path = "%s/cow.vhd" % source_image_path - new_base_copy_uuid = str(uuid.uuid4()) - new_cow_uuid = str(uuid.uuid4()) temp_vhd_path = "%s/instance%d/" % (sr_temp_path, instance_id) new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid) @@ -97,7 +98,7 @@ def move_vhds_into_sr(session, args): shutil.move("%s/*.vhd" % temp_vhd_path, sr_path) os.rmdir(temp_vhd_path) - return (new_base_copy_uuid, new_cow_uuid) + return None def transfer_vhd(session, args): -- cgit From 96d0edff2348040362b77491892e525217a17562 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 14 Feb 2011 23:19:15 +0100 Subject: Resurrect logdir option. --- nova/flags.py | 2 ++ nova/log.py | 11 +++++++++-- nova/twistd.py | 2 -- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index 3ba3fe6fa..f64a62da9 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -282,6 +282,8 @@ DEFINE_integer('auth_token_ttl', 3600, 'Seconds for auth tokens to linger') DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../'), "Top-level directory for maintaining nova's state") +DEFINE_string('logdir', None, 'output to a per-service log file in named ' + 'directory') DEFINE_string('sql_connection', 'sqlite:///$state_path/nova.sqlite', diff --git a/nova/log.py b/nova/log.py index b541488bd..a5b4828d5 100644 --- a/nova/log.py +++ b/nova/log.py @@ -28,9 +28,11 @@ It also allows setting of formatting information through flags. import cStringIO +import inspect import json import logging import logging.handlers +import os import sys import traceback @@ -123,8 +125,13 @@ def basicConfig(): syslog = SysLogHandler(address='/dev/log') syslog.setFormatter(_formatter) logging.root.addHandler(syslog) - if FLAGS.logfile: - logfile = FileHandler(FLAGS.logfile) + if FLAGS.logfile or FLAGS.logdir: + if FLAGS.logfile: + logfile = FLAGS.logfile + else: + binary = os.path.basename(inspect.stack()[-1][1]) + logpath = '%s.log' % (os.path.join(FLAGS.logdir, binary),) + logfile = FileHandler(logpath) logfile.setFormatter(_formatter) logging.root.addHandler(logfile) diff --git a/nova/twistd.py b/nova/twistd.py index 6390a8144..60ff7879a 100644 --- a/nova/twistd.py +++ b/nova/twistd.py @@ -43,8 +43,6 @@ else: FLAGS = flags.FLAGS -flags.DEFINE_string('logdir', None, 'directory to keep log files in ' - '(will be prepended to $logfile)') class TwistdServerOptions(ServerOptions): -- cgit From 9a71c79dc3beb554c86a1b1b5d03ab66c6e96edc Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 16:24:51 -0600 Subject: Typo fixes --- nova/compute/manager.py | 2 +- nova/virt/xenapi/vm_utils.py | 2 +- nova/virt/xenapi/vmops.py | 2 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 0b0966324..23d2b80ac 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -460,7 +460,7 @@ class ComputeManager(manager.Manager): # this may get passed into the following spawn instead disk_info = self.driver.attach_disk(instance_ref) - self.driver.spawn(context, instance_ref, disk=disk_info) + self.driver.spawn(instance_ref, disk=disk_info) self.db.migration_update(context, migration_id, {'status': 'finished', }) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 23f9547d7..08064b786 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -511,7 +511,7 @@ class VMHelper(HelperBase): session.wait_for_task(instance_id, task) else: sr_ref = cls.get_sr(session) - session.call_xen_api('SR.scan', sr_ref) + session.call_xenapi('SR.scan', sr_ref) def get_rrd(host, uuid): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 17d42d542..6c6d04dbf 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -280,7 +280,7 @@ class VMOps(object): params = { 'instance_id': instance.id, 'new_base_copy_uuid': new_base_copy_uuid, - 'new_cow_uuid': str(uuid.uuid4() } + 'new_cow_uuid': str(uuid.uuid4()) } self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index e73480445..cf7f378fa 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -25,7 +25,6 @@ import os.path import pickle import shutil import subprocess -import uuid import XenAPIPlugin @@ -114,7 +113,7 @@ def transfer_vhd(session, args): source_path = "%s/%s" % (sr_path, vhd_path) dest_path = '%s:%sinstance%d/%s' % (host, IMAGE_PATH, instance_id, dest_name) - rsync_args = [['nohup', RSYNC, '-av', '--progress', + rsync_args = ['nohup', RSYNC, '-av', '--progress', '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] if subprocess.call(rsync_args) != 0: -- cgit From 2816ca39281bf7c1994791d969bc63d22c86f911 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 14 Feb 2011 23:27:31 +0100 Subject: Use RotatingFileHandler instead of FileHandler. --- nova/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/log.py b/nova/log.py index b541488bd..413dd946b 100644 --- a/nova/log.py +++ b/nova/log.py @@ -92,7 +92,7 @@ critical = logging.critical log = logging.log # handlers StreamHandler = logging.StreamHandler -FileHandler = logging.FileHandler +FileHandler = logging.RotatingFileHandler # logging.SysLogHandler is nicer than logging.logging.handler.SysLogHandler. SysLogHandler = logging.handlers.SysLogHandler -- cgit From 6147a606cbe6b7e764865d2471d86f503437051b Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 14 Feb 2011 14:52:58 -0800 Subject: fixed template and added migration --- nova/auth/novarc.template | 6 +-- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 62 ++++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py diff --git a/nova/auth/novarc.template b/nova/auth/novarc.template index 702df3bb0..cda2ecc28 100644 --- a/nova/auth/novarc.template +++ b/nova/auth/novarc.template @@ -10,6 +10,6 @@ export NOVA_CERT=${NOVA_KEY_DIR}/%(nova)s export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this set alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user 42 --ec2cert ${NOVA_CERT}" alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${NOVA_CERT}" -export NOVA_TOOLS_API_KEY="%(access)s" -export NOVA_TOOLS_USERNAME="%(user)s" -export NOVA_TOOLS_URL="%(os)s" +export NOVA_API_KEY="%(access)s" +export NOVA_USERNAME="%(user)s" +export NOVA_URL="%(os)s" diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py new file mode 100644 index 000000000..eb3287077 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -0,0 +1,62 @@ +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# +# New Tables +# +child_zones = Table('child_zones', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('api_url', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('username', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('password', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + ) + + +# +# Tables to alter +# + +# (none currently) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (child_zones, ): + try: + table.create() + except Exception: + logging.info(repr(table)) -- cgit From 1da5dcc0644a13cfb99852f3438649f710feb2bc Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 14 Feb 2011 14:54:04 -0800 Subject: removed debugging --- nova/api/openstack/auth.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index a383ef086..473071738 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -79,7 +79,6 @@ class AuthMiddleware(wsgi.Middleware): except KeyError: return faults.Fault(webob.exc.HTTPUnauthorized()) - logging.debug("**** USERNAME %s, PASSWORD %s" % (username, key)) token, user = self._authorize_user(username, key, req) if user and token: res = webob.Response() -- cgit From 41e4e18a0324593c0076c3936d63bb6dcca487cb Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Mon, 14 Feb 2011 23:12:34 +0000 Subject: First cut on XenServer unified-images --- nova/api/openstack/servers.py | 6 +- nova/virt/xenapi/vm_utils.py | 47 ++++- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 204 +++++++++++++++++---- 3 files changed, 217 insertions(+), 40 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 17c5519a1..1c9dcd9a6 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -140,7 +140,11 @@ class Controller(wsgi.Controller): image_id = str(image_id) image = self._image_service.show(req.environ['nova.context'], image_id) - return lookup('kernel_id'), lookup('ramdisk_id') + disk_format = image['properties'].get('disk_format', None) + if disk_format == "vhd": + return None, None + else: + return lookup('kernel_id'), lookup('ramdisk_id') def create(self, req): """ Creates a new server for a given user """ diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4bbd522c1..6d6067da5 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -266,7 +266,9 @@ class VMHelper(HelperBase): session, instance_id, sr_ref, vm_vdi_ref, original_parent_uuid) #TODO(sirp): we need to assert only one parent, not parents two deep - return template_vm_ref, [template_vdi_uuid, parent_uuid] + template_vdi_uuids = {'image': parent_uuid, + 'snap': template_vdi_uuid} + return template_vm_ref, template_vdi_uuids @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_id): @@ -303,15 +305,36 @@ class VMHelper(HelperBase): return cls._fetch_image_objectstore(session, instance_id, image, access, user.secret, type) + @classmethod - def _fetch_image_glance(cls, session, instance_id, image, access, type): + def _fetch_image_glance_vhd(cls, session, instance_id, image, access, type): + LOG.debug(_("Asking xapi to fetch vhd image %(image)s") + % locals()) + + sr_ref = find_sr(session) + if sr_ref is None: + raise exception.NotFound('Cannot find SR to write VDI to') + + params = {'image_id': image, + 'glance_host': FLAGS.glance_host, + 'glance_port': FLAGS.glance_port} + + kwargs = {'params': pickle.dumps(params)} + task = session.async_call_plugin('glance', 'get_vdi', kwargs) + vdi_uuid = session.wait_for_task(instance_id, task) + scan_sr(session, instance_id, sr_ref) + LOG.debug(_("Xapi 'get_vdi' returned VDI UUID %(vdi_uuid)s") % locals()) + return vdi_uuid + + @classmethod + def _fetch_image_glance_disk(cls, session, instance_id, image, access, type): sr = find_sr(session) if sr is None: raise exception.NotFound('Cannot find SR to write VDI to') - c = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) + client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) - meta, image_file = c.get_image(image) + meta, image_file = client.get_image(image) virtual_size = int(meta['size']) vdi_size = virtual_size LOG.debug(_("Size for image %(image)s:%(virtual_size)d") % locals()) @@ -344,6 +367,22 @@ class VMHelper(HelperBase): else: return session.get_xenapi().VDI.get_uuid(vdi) + @classmethod + def _fetch_image_glance(cls, session, instance_id, image, access, type): + client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) + meta = client.get_image_meta(image) + properties = meta['properties'] + disk_format = properties.get('disk_format', None) + + # TODO(sirp): When Glance treats disk_format as a first class + # attribute, we should start using that rather than an image-property + if disk_format == 'vhd': + return cls._fetch_image_glance_vhd( + session, instance_id, image, access, type) + else: + return cls._fetch_image_glance_disk( + session, instance_id, image, access, type) + @classmethod def _fetch_image_objectstore(cls, session, instance_id, image, access, secret, type): diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index aadacce57..8352d7ee6 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -29,7 +29,10 @@ import os import os.path import pickle import sha +import shlex +import shutil import subprocess +import tempfile import time import urlparse @@ -66,65 +69,190 @@ def _copy_kernel_vdi(dest,copy_args): data=f.read(vdi_size) of.write(data) f.close() - of.close() + of.close() logging.debug("Done. Filename: %s",filename) - return filename + return filename + + +def execute(cmd): + args = shlex.split(cmd) + proc = subprocess.Popen( + args, stdout=subprocess.PIPE, stdin=subprocess.PIPE) + return proc + + +def get_vdi(session, args): + """ + """ + params = pickle.loads(exists(args, 'params')) + image_id = params["image_id"] + glance_host = params["glance_host"] + glance_port = params["glance_port"] + + def unbundle_xfer(sr_path, staging_path): + """ + + """ + conn = httplib.HTTPConnection(glance_host, glance_port) + conn.request('GET', '/images/%s' % image_id) + resp = conn.getresponse() + if resp.status == httplib.NOT_FOUND: + raise Exception("Image '%s' not found in Glance" % image_id) + elif resp.status != httplib.OK: + raise Exception("Unexpected response from Glance %i" % res.status) + + tar_proc = execute("tar -zx --directory=%(staging_path)s" % locals()) + chunk = resp.read(CHUNK_SIZE) + while chunk: + tar_proc.stdin.write(chunk) + chunk = resp.read(CHUNK_SIZE) + out, err = tar_proc.communicate() + # TODO(sirp): write assert_process_success + ret = tar_proc.returncode + if ret != 0: + raise Exception( + "tar returned non-zero exit code (%i): '%s'" % (ret, err)) + conn.close() + + def fixup_vhds(sr_path, staging_path): + """ + """ + def rename_with_uuid(orig_path): + """Generate a uuid and rename the file with the uuid""" + orig_dirname = os.path.dirname(orig_path) + uuid = generate_uuid() + new_path = os.path.join(orig_dirname, "%s.vhd" % uuid) + os.rename(orig_path, new_path) + return new_path, uuid + + def move_into_sr(orig_path): + """Move a file into the SR""" + filename = os.path.basename(orig_path) + new_path = os.path.join(sr_path, filename) + #os.rename(orig_path, new_path) + # FIXME(sirp) : testing + shutil.copyfile(orig_path, new_path) + return new_path + + def link_vhds(child_path, parent_path): + proc = execute("vhd-util modify -n %(child_path)s -p %(parent_path)s" + % locals()) + out, err = proc.communicate() + if proc.returncode != 0: + raise Exception("Failed to link vhds: '%s'" % err) + + image_path = os.path.join(staging_path, 'image') + + orig_base_copy_path = os.path.join(image_path, 'image.vhd') + if not os.path.exists(orig_base_copy_path): + raise Exception("Invalid image: image.vhd not present") + + base_copy_path, base_copy_uuid = rename_with_uuid(orig_base_copy_path) + + vdi_uuid = base_copy_uuid + orig_snap_path = os.path.join(image_path, 'snap.vhd') + if os.path.exists(orig_snap_path): + snap_path, snap_uuid = rename_with_uuid(orig_snap_path) + vdi_uuid = snap_uuid + # NOTE(sirp): this step is necessary so that an SR scan won't + # delete the base_copy out from under us (since it would be + # orphaned) + link_vhds(snap_path, base_copy_path) + move_into_sr(snap_path) + else: + raise Exception("path '%s' not found!!!" % orig_snap_path) + move_into_sr(base_copy_path) + return vdi_uuid + + sr_path = get_sr_path(session) + staging_path = make_staging_area(sr_path) + try: + unbundle_xfer(sr_path, staging_path) + vdi_uuid = fixup_vhds(sr_path, staging_path) + return vdi_uuid + finally: + # FIXME(sirp) : testing + pass + #cleanup_staging_area(staging_path) + def put_vdis(session, args): + """ + """ params = pickle.loads(exists(args, 'params')) vdi_uuids = params["vdi_uuids"] image_id = params["image_id"] glance_host = params["glance_host"] glance_port = params["glance_port"] - - sr_path = get_sr_path(session) - #FIXME(sirp): writing to a temp file until Glance supports chunked-PUTs - tmp_file = "%s.tar.gz" % os.path.join('/tmp', str(image_id)) - tar_cmd = ['tar', '-zcf', tmp_file, '--directory=%s' % sr_path] - paths = [ "%s.vhd" % vdi_uuid for vdi_uuid in vdi_uuids ] - tar_cmd.extend(paths) - logging.debug("Bundling image with cmd: %s", tar_cmd) - subprocess.call(tar_cmd) - logging.debug("Writing to test file %s", tmp_file) - put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port) - return "" # FIXME(sirp): return anything useful here? + def prepare_staging_area(sr_path, staging_path): + """ + Explain preparing staging area here... + """ + image_path = os.path.join(staging_path, 'image') + os.mkdir(image_path) + for name, uuid in vdi_uuids.items(): + source = os.path.join(sr_path, "%s.vhd" % uuid) + link_name = os.path.join(image_path, "%s.vhd" % name) + os.link(source, link_name) -def put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port): - size = os.path.getsize(tmp_file) - basename = os.path.basename(tmp_file) + def bundle_xfer(staging_path): + conn = httplib.HTTPConnection(glance_host, glance_port) + #NOTE(sirp): httplib under python2.4 won't accept a file-like object + # to request + conn.putrequest('PUT', '/images/%s' % image_id) - bundle = open(tmp_file, 'r') - try: headers = { 'x-image-meta-store': 'file', 'x-image-meta-is_public': 'True', 'x-image-meta-type': 'raw', - 'x-image-meta-size': size, - 'content-length': size, + 'x-image-meta-property-disk-format': 'vhd', + 'x-image-meta-property-container-format': 'tarball', + 'transfer-encoding': "chunked", 'content-type': 'application/octet-stream', } - conn = httplib.HTTPConnection(glance_host, glance_port) - #NOTE(sirp): httplib under python2.4 won't accept a file-like object - # to request - conn.putrequest('PUT', '/images/%s' % image_id) - for header, value in headers.iteritems(): conn.putheader(header, value) conn.endheaders() - - chunk = bundle.read(CHUNK_SIZE) + + tar_proc = execute("tar -zc --directory=%(staging_path)s ." % locals()) + + chunk = tar_proc.stdout.read(CHUNK_SIZE) while chunk: - conn.send(chunk) - chunk = bundle.read(CHUNK_SIZE) - + conn.send("%x\r\n%s\r\n" % (len(chunk), chunk)) + chunk = tar_proc.stdout.read(CHUNK_SIZE) + conn.send("0\r\n\r\n") - res = conn.getresponse() + resp = conn.getresponse() #FIXME(sirp): should this be 201 Created? - if res.status != httplib.OK: + if resp.status != httplib.OK: raise Exception("Unexpected response from Glance %i" % res.status) + + conn.close() + + sr_path = get_sr_path(session) + staging_path = make_staging_area(sr_path) + try: + prepare_staging_area(sr_path, staging_path) + bundle_xfer(staging_path) finally: - bundle.close() + cleanup_staging_area(staging_path) + + return "" # FIXME(sirp): return anything useful here? + + +def make_staging_area(sr_path): + """ + Explain staging area here... + """ + # NOTE(sirp): staging area is created in SR to allow hard-linking + staging_path = tempfile.mkdtemp(dir=sr_path) + return staging_path + + +def cleanup_staging_area(staging_path): + shutil.rmtree(staging_path) + def get_sr_path(session): sr_ref = find_sr(session) @@ -154,7 +282,13 @@ def find_sr(session): return sr return None +def generate_uuid(): + # NOTE(sirp): Python2.4 does not include the uuid module + proc = execute("uuidgen") + uuid = proc.stdout.read().strip() + return uuid if __name__ == '__main__': - XenAPIPlugin.dispatch({'put_vdis': put_vdis, + XenAPIPlugin.dispatch({'put_vdis': put_vdis, + 'get_vdi': get_vdi, 'copy_kernel_vdi': copy_kernel_vdi}) -- cgit From 0bd48e3d53c6fce04b0c5e483537b3fd31c7364a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 17:24:33 -0600 Subject: bad plugin --- nova/virt/xenapi/vmops.py | 2 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6c6d04dbf..5ab73d562 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -269,7 +269,7 @@ class VMOps(object): VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], 'dest_name': 'cow.vhd'} - self._session.async_call_plugin('data_transfer', 'transfer_vhd', + self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index cf7f378fa..c68fc93c5 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -120,4 +120,5 @@ def transfer_vhd(session, args): raise Exception("Unexpected VHD transfer failure") if __name__ == '__main__': - XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd}) + XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd, + 'move_vhd_into_sr':move_vhd_into_sr, }) -- cgit From e44a91ced3d19a3bca10457239592307bf6f829b Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 17:31:20 -0600 Subject: bad plugin --- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index c68fc93c5..c1f5b7528 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -65,7 +65,7 @@ def find_sr(session): def move_vhds_into_sr(session, args): """Moves the VHDs from their copied location to the SR""" - params = pickle.dumps(args) + params = pickle.loads(exists(args, 'params')) instance_id = params['instance_id'] new_base_copy_uuid = params['new_base_copy_uuid'] @@ -102,7 +102,7 @@ def move_vhds_into_sr(session, args): def transfer_vhd(session, args): """Rsyncs a VHD to an adjacent host""" - params = pickle.dumps(args) + params = pickle.loads(exists(args, 'params')) instance_id = params['instance_id'] host = params['host'] vdi_uuid = params['vdi_uuid'] @@ -121,4 +121,4 @@ def transfer_vhd(session, args): if __name__ == '__main__': XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd, - 'move_vhd_into_sr':move_vhd_into_sr, }) + 'move_vhds_into_sr':move_vhds_into_sr, }) -- cgit From b7cf8f233a585043f0aa85f4d26dc2fb5a6701c7 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 17:34:54 -0600 Subject: bad plugin --- nova/virt/xenapi/vmops.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 5ab73d562..ba0db22f1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -258,7 +258,8 @@ class VMOps(object): #pretty fugly with self._get_snapshot(instance) as snapshot: params = {'host':dest, 'vdi_uuid':snapshot.vdi_uuids[1], - 'dest_name':'base_copy.vhd'} + 'dest_name': 'base_copy.vhd', + 'instance_id': instance.id, } self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) @@ -268,7 +269,8 @@ class VMOps(object): vdi_ref, vm_vdi_rec = \ VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], - 'dest_name': 'cow.vhd'} + 'dest_name': 'cow.vhd', + 'instance_id': instance.id, } self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] -- cgit From 238ae2b5a8c559acc362a3b44160404771f1259f Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 00:00:56 +0000 Subject: Removing testing statements --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 8352d7ee6..0cb3b52db 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -129,9 +129,7 @@ def get_vdi(session, args): """Move a file into the SR""" filename = os.path.basename(orig_path) new_path = os.path.join(sr_path, filename) - #os.rename(orig_path, new_path) - # FIXME(sirp) : testing - shutil.copyfile(orig_path, new_path) + os.rename(orig_path, new_path) return new_path def link_vhds(child_path, parent_path): @@ -159,8 +157,7 @@ def get_vdi(session, args): # orphaned) link_vhds(snap_path, base_copy_path) move_into_sr(snap_path) - else: - raise Exception("path '%s' not found!!!" % orig_snap_path) + move_into_sr(base_copy_path) return vdi_uuid @@ -171,9 +168,7 @@ def get_vdi(session, args): vdi_uuid = fixup_vhds(sr_path, staging_path) return vdi_uuid finally: - # FIXME(sirp) : testing - pass - #cleanup_staging_area(staging_path) + cleanup_staging_area(staging_path) def put_vdis(session, args): -- cgit From 3014c0896202b592858fc1a7fc9c29b92a6f5d1b Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 18:04:07 -0600 Subject: plugin --- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index c1f5b7528..9c56cb379 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -111,14 +111,18 @@ def transfer_vhd(session, args): vhd_path = "%s.vhd" % vdi_uuid source_path = "%s/%s" % (sr_path, vhd_path) - dest_path = '%s:%sinstance%d/%s' % (host, IMAGE_PATH, instance_id, - dest_name) - rsync_args = ['nohup', RSYNC, '-av', '--progress', - '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path] + dest_path = '%sinstance%d/' % (IMAGE_PATH, instance_id) + + dest_path_with_vhd="$s:%s/%s" % (host, dest_path, dest_name) + ssh_cmd = '-e "ssh -o StrictHostKeyChecking=no \'mkdir -p %s\' " ' % dest_path + + rsync_args = ['nohup', RSYNC, '-av', '--progress', ssh_cmd, source_path, + dest_path_with_vhd] if subprocess.call(rsync_args) != 0: raise Exception("Unexpected VHD transfer failure") + if __name__ == '__main__': XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd, 'move_vhds_into_sr':move_vhds_into_sr, }) -- cgit From e65291cf34894322bd0f3f6661907e48e7a6a0b5 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 14 Feb 2011 20:11:29 -0400 Subject: fixed nova-combined debug hack and renamed ChildZone to Zone --- bin/nova-combined | 4 ++-- nova/db/api.py | 10 +++++----- nova/db/sqlalchemy/api.py | 10 +++++----- nova/db/sqlalchemy/migration.py | 2 +- nova/db/sqlalchemy/models.py | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bin/nova-combined b/bin/nova-combined index a0f552d64..913c866bf 100755 --- a/bin/nova-combined +++ b/bin/nova-combined @@ -53,11 +53,11 @@ if __name__ == '__main__': compute = service.Service.create(binary='nova-compute') network = service.Service.create(binary='nova-network') - #volume = service.Service.create(binary='nova-volume') + volume = service.Service.create(binary='nova-volume') scheduler = service.Service.create(binary='nova-scheduler') #objectstore = service.Service.create(binary='nova-objectstore') - service.serve(compute, network, scheduler) + service.serve(compute, network, volume, scheduler) apps = [] paste_config_file = wsgi.paste_config_file('nova-api.conf') diff --git a/nova/db/api.py b/nova/db/api.py index fa73d86ad..939f1a069 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -986,25 +986,25 @@ def console_get(context, console_id, instance_id=None): def zone_create(context, values): - """Create a new ChildZone entry in this Zone.""" + """Create a new child Zone entry.""" return IMPL.zone_create(context, values) def zone_update(context, zone_id, values): - """Update a ChildZone entry in this Zone.""" + """Update a child Zone entry.""" return IMPL.zone_update(context, values) def zone_delete(context, zone_id): - """Delete a ChildZone.""" + """Delete a child Zone.""" return IMPL.zone_delete(context, zone_id) def zone_get(context, zone_id): - """Get a specific ChildZone.""" + """Get a specific child Zone.""" return IMPL.zone_get(context, zone_id) def zone_get_all(context): - """Get all ChildZone's.""" + """Get all child Zones.""" return IMPL.zone_get_all(context) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index b3320c819..abd65b67b 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2021,7 +2021,7 @@ def console_get(context, console_id, instance_id=None): @require_admin_context def zone_create(context, values): - zone = models.ChildZone() + zone = models.Zone() zone.update(values) zone.save() return zone @@ -2029,7 +2029,7 @@ def zone_create(context, values): @require_admin_context def zone_update(context, zone_id, values): - zone = session.query(models.ChildZone).filter_by(id=zone_id).first() + zone = session.query(models.Zone).filter_by(id=zone_id).first() if not zone: raise exception.NotFound(_("No zone with id %(zone_id)s") % locals()) zone.update(values) @@ -2041,14 +2041,14 @@ def zone_update(context, zone_id, values): def zone_delete(context, zone_id): session = get_session() with session.begin(): - session.execute('delete from child_zones ' + session.execute('delete from zones ' 'where id=:id', {'id': zone_id}) @require_admin_context def zone_get(context, zone_id): session = get_session() - result = session.query(models.ChildZone).filter_by(id=zone_id).first() + result = session.query(models.Zone).filter_by(id=zone_id).first() if not result: raise exception.NotFound(_("No zone with id %(zone_id)s") % locals()) return result @@ -2057,4 +2057,4 @@ def zone_get(context, zone_id): @require_admin_context def zone_get_all(context): session = get_session() - return session.query(models.ChildZone).all() + return session.query(models.Zone).all() diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py index 194ecc627..1d9c041f5 100644 --- a/nova/db/sqlalchemy/migration.py +++ b/nova/db/sqlalchemy/migration.py @@ -45,7 +45,7 @@ def db_version(): engine = sqlalchemy.create_engine(FLAGS.sql_connection, echo=False) meta.reflect(bind=engine) try: - for table in ('auth_tokens', 'child_zones', 'export_devices', + for table in ('auth_tokens', 'zones', 'export_devices', 'fixed_ips', 'floating_ips', 'instances', 'key_pairs', 'networks', 'projects', 'quotas', 'security_group_instance_association', diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 071d57fca..4930c7a2a 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -535,9 +535,9 @@ class Console(BASE, NovaBase): pool = relationship(ConsolePool, backref=backref('consoles')) -class ChildZone(BASE, NovaBase): +class Zone(BASE, NovaBase): """Represents a child zone of this zone.""" - __tablename__ = 'child_zones' + __tablename__ = 'zones' id = Column(Integer, primary_key=True) api_url = Column(String(255)) username = Column(String(255)) @@ -556,7 +556,7 @@ def register_models(): Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, - Project, Certificate, ConsolePool, Console, ChildZone) + Project, Certificate, ConsolePool, Console, Zone) engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: model.metadata.create_all(engine) -- cgit From f1e536fb296c927a9fc953b1dfe24b9060a0387a Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Mon, 14 Feb 2011 19:51:51 -0500 Subject: Updates to that S3ImageService kernel_id and ramdisk_id mappings work with EC2 API. --- nova/api/ec2/cloud.py | 3 +++ nova/compute/api.py | 4 ++-- nova/image/s3.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 16a3a4521..6919cd8d2 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -883,6 +883,9 @@ class CloudController(object): % attribute) try: image = self.image_service.show(context, image_id) + image = self._format_image(context, + self.image_service.show(context, + image_id)) except IndexError: raise exception.ApiError(_('invalid id: %s') % image_id) result = {'image_id': image_id, 'launchPermission': []} diff --git a/nova/compute/api.py b/nova/compute/api.py index ac02dbcfa..740dd3935 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -103,9 +103,9 @@ class API(base.Base): if not is_vpn: image = self.image_service.show(context, image_id) if kernel_id is None: - kernel_id = image.get('kernelId', None) + kernel_id = image.get('kernel_id', None) if ramdisk_id is None: - ramdisk_id = image.get('ramdiskId', None) + ramdisk_id = image.get('ramdisk_id', None) # No kernel and ramdisk for raw images if kernel_id == str(FLAGS.null_kernel): kernel_id = None diff --git a/nova/image/s3.py b/nova/image/s3.py index 71304cdd6..14135a1ee 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -94,7 +94,7 @@ class S3ImageService(service.BaseImageService): if FLAGS.connection_type == 'fake': return {'imageId': 'bar'} result = self.index(context) - result = [i for i in result if i['imageId'] == image_id] + result = [i for i in result if i['id'] == image_id] if not result: raise exception.NotFound(_('Image %s could not be found') % image_id) -- cgit From fe6efb38ee30c5a3e532cd19faef0fec063b7446 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 01:37:54 +0000 Subject: Adding DISK_VHD to ImageTypes --- nova/virt/xenapi/vm_utils.py | 38 ++++++++++++++++++++++++++++++-------- nova/virt/xenapi/vmops.py | 22 +++++++++++++++------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 6d6067da5..a6312d00c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -63,11 +63,14 @@ class ImageType: 0 - kernel/ramdisk image (goes on dom0's filesystem) 1 - disk image (local SR, partitioned by objectstore plugin) 2 - raw disk image (local SR, NOT partitioned by plugin) + 3 - vhd disk image (local SR, NOT inspected by XS, PV assumed for + linux, HVM assumed for Windows) """ KERNEL_RAMDISK = 0 DISK = 1 DISK_RAW = 2 + DISK_VHD = 3 class VMHelper(HelperBase): @@ -368,15 +371,34 @@ class VMHelper(HelperBase): return session.get_xenapi().VDI.get_uuid(vdi) @classmethod - def _fetch_image_glance(cls, session, instance_id, image, access, type): - client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) - meta = client.get_image_meta(image) - properties = meta['properties'] - disk_format = properties.get('disk_format', None) + def determine_disk_image_type(cls, instance): + instance_id = instance.id + if instance.kernel_id: + #if kernel is not present we must download a raw disk + LOG.debug(_("Instance %(instance_id)s will use DISK format") % + locals()) + return ImageType.DISK - # TODO(sirp): When Glance treats disk_format as a first class - # attribute, we should start using that rather than an image-property - if disk_format == 'vhd': + if FLAGS.xenapi_image_service == 'glance': + # if using glance, then we could be VHD format + client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) + meta = client.get_image_meta(instance.image_id) + properties = meta['properties'] + disk_format = properties.get('disk_format', None) + # TODO(sirp): When Glance treats disk_format as a first class + # attribute, we should start using that rather than an image-property + if disk_format == 'vhd': + LOG.debug(_("Instance %(instance_id)s will use DISK_VHD format") % + locals()) + return ImageType.DISK_VHD + + LOG.debug(_("Instance %(instance_id)s will use DISK_RAW format") % + locals()) + return ImageType.DISK_RAW + + @classmethod + def _fetch_image_glance(cls, session, instance_id, image, access, type): + if type == ImageType.DISK_VHD: return cls._fetch_image_glance_vhd( session, instance_id, image, access, type) else: diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index e84ce20c4..34ceea358 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -74,27 +74,34 @@ class VMOps(object): user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) - #if kernel is not present we must download a raw disk - if instance.kernel_id: - disk_image_type = ImageType.DISK - else: - disk_image_type = ImageType.DISK_RAW + + disk_image_type = VMHelper.determine_disk_image_type(instance) + vdi_uuid = VMHelper.fetch_image(self._session, instance.id, instance.image_id, user, project, disk_image_type) + vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) - #Have a look at the VDI and see if it has a PV kernel + pv_kernel = False - if not instance.kernel_id: + if disk_image_type == ImageType.DISK_RAW: + #Have a look at the VDI and see if it has a PV kernel pv_kernel = VMHelper.lookup_image(self._session, instance.id, vdi_ref) + elif disk_image_type == ImageType.DISK_VHD: + # TODO(sirp): Assuming PV for now; this will need to be + # configurable as Windows will use HVM. + pv_kernel = True + kernel = None if instance.kernel_id: kernel = VMHelper.fetch_image(self._session, instance.id, instance.kernel_id, user, project, ImageType.KERNEL_RAMDISK) + ramdisk = None if instance.ramdisk_id: ramdisk = VMHelper.fetch_image(self._session, instance.id, instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK) + vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk, pv_kernel) VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True) @@ -102,6 +109,7 @@ class VMOps(object): if network_ref: VMHelper.create_vif(self._session, vm_ref, network_ref, instance.mac_address) + LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) instance_name = instance.name -- cgit From c15289a63c90218a573d5e75833985ec2ad8691e Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 14 Feb 2011 23:02:26 -0400 Subject: better filtering --- nova/api/openstack/zones.py | 13 ++++++++----- nova/tests/api/openstack/test_zones.py | 10 +++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index 2c93c0c7b..830464ffd 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -33,6 +33,10 @@ def _filter_keys(item, keys): return dict((k, v) for k, v in item.iteritems() if k in keys) +def _scrub_zone(zone): + return _filter_keys(zone, ('id', 'api_url')) + + class Controller(wsgi.Controller): _serialization_metadata = { @@ -44,7 +48,7 @@ class Controller(wsgi.Controller): """Return all zones in brief""" items = db.zone_get_all(req.environ['nova.context']) items = common.limited(items, req) - items = [_filter_keys(item, ('id', 'api_url')) for item in items] + items = [_scrub_zone(item) for item in items] return dict(zones=items) def detail(self, req): @@ -55,8 +59,7 @@ class Controller(wsgi.Controller): """Return data about the given zone id""" zone_id = int(id) zone = db.zone_get(req.environ['nova.context'], zone_id) - zone = _filter_keys(zone, ('id', 'api_url')) - return dict(zone=zone) + return dict(zone=_scrub_zone(zone)) def delete(self, req, id): zone_id = int(id) @@ -67,11 +70,11 @@ class Controller(wsgi.Controller): context = req.environ['nova.context'] env = self._deserialize(req.body, req) zone = db.zone_create(context, env["zone"]) - return dict(zone=zone) + return dict(zone=_scrub_zone(zone)) def update(self, req, id): context = req.environ['nova.context'] env = self._deserialize(req.body, req) zone_id = int(id) zone = db.zone_update(context, zone_id, env["zone"]) - return dict(zone=zone) + return dict(zone=_scrub_zone(zone)) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 8dbdffa41..5542a1cf3 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -98,9 +98,7 @@ class ZonesTest(unittest.TestCase): self.assertEqual(res_dict['zone']['id'], 1) self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') - self.assertEqual(res_dict['zone']['username'], 'bob') - self.assertEqual(res_dict['zone']['password'], 'xxx') - + self.assertFalse('password' in res_dict['zone']) self.assertEqual(res.status_int, 200) def test_zone_delete(self): @@ -122,8 +120,7 @@ class ZonesTest(unittest.TestCase): self.assertEqual(res.status_int, 200) self.assertEqual(res_dict['zone']['id'], 1) self.assertEqual(res_dict['zone']['api_url'], 'http://blah.zoo') - self.assertEqual(res_dict['zone']['username'], 'fred') - self.assertEqual(res_dict['zone']['password'], 'fubar') + self.assertFalse('username' in res_dict['zone']) def test_zone_update(self): body = dict(zone=dict(username='zeb', password='sneaky')) @@ -137,8 +134,7 @@ class ZonesTest(unittest.TestCase): self.assertEqual(res.status_int, 200) self.assertEqual(res_dict['zone']['id'], 1) self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') - self.assertEqual(res_dict['zone']['username'], 'zeb') - self.assertEqual(res_dict['zone']['password'], 'sneaky') + self.assertFalse('username' in res_dict['zone']) if __name__ == '__main__': -- cgit From e90e0355b6edcd381ea4cf1977fdcf1481fdf703 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of nova-core <> Date: Tue, 15 Feb 2011 05:12:01 +0000 Subject: Launchpad automatic translations update. --- locale/zh_CN.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locale/zh_CN.po b/locale/zh_CN.po index 01b8dc378..a39383497 100644 --- a/locale/zh_CN.po +++ b/locale/zh_CN.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-02-14 05:22+0000\n" +"X-Launchpad-Export-Date: 2011-02-15 05:12+0000\n" "X-Generator: Launchpad (build 12351)\n" #: nova/twistd.py:268 -- cgit From e7fe96453760320ef897b9edfc39e057d565e6c0 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 23:22:37 -0600 Subject: Refactored --- nova/compute/manager.py | 9 ++-- nova/virt/xenapi/vmops.py | 50 ++++++++++++---------- nova/virt/xenapi_conn.py | 2 +- .../xenserver/xenapi/etc/xapi.d/plugins/migration | 17 ++++---- 4 files changed, 43 insertions(+), 35 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 23d2b80ac..2308c8315 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -429,14 +429,15 @@ class ComputeManager(manager.Manager): self.db.migration_update(context, migration_id, { 'status': 'migrating', }) - self.driver.migrate_disk_and_power_off(instance_ref, + disk_info = self.driver.migrate_disk_and_power_off(instance_ref, migration_ref['dest_host']) self.db.migration_update(context, migration_id, { 'status': 'post-migrating', }) + #TODO(mdietz): This is where we would update the VM record #after resizing - + service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_host'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, @@ -451,7 +452,7 @@ class ComputeManager(manager.Manager): @exception.wrap_exception @checks_instance_lock - def finish_resize(self, context, instance_id, migration_id): + def finish_resize(self, context, instance_id, migration_id, disk_info): """Completes the migration process by setting up the newly transferred disk and turning on the instance on its new host machine""" migration_ref = self.db.migration_get(context, migration_id) @@ -459,7 +460,7 @@ class ComputeManager(manager.Manager): migration_ref['instance_id']) # this may get passed into the following spawn instead - disk_info = self.driver.attach_disk(instance_ref) + new_disk_info = self.driver.attach_disk(instance_ref, disk_info) self.driver.spawn(instance_ref, disk=disk_info) self.db.migration_update(context, migration_id, diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ba0db22f1..127a09ad1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -103,17 +103,19 @@ class VMOps(object): vdi_ref) if instance.kernel_id: kernel = VMHelper.fetch_image(self._session, instance.id, - instance.kernel_id, user, project, ImageType.KERNEL_RAMDISK) + instance.kernel_id, user, project, + ImageType.KERNEL_RAMDISK) if instance.ramdisk_id: ramdisk = VMHelper.fetch_image(self._session, instance.id, - instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK) + instance.ramdisk_id, user, project, + ImageType.KERNEL_RAMDISK) else: vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', disk) vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk, pv_kernel) - VMHelper.create_vbd(session=self._session, vm_ref=vm_ref, vdi_ref=vdi_ref, - userdevice=0, bootable=True) + VMHelper.create_vbd(session=self._session, vm_ref=vm_ref, + vdi_ref=vdi_ref, userdevice=0, bootable=True) if network_ref: VMHelper.create_vif(self._session, vm_ref, @@ -234,7 +236,7 @@ class VMOps(object): try: template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( self._session, instance.id, vm_ref, label) - return Snapshot(self, instance, template_vm_ref, + return Snapshot(self, instance, template_vm_ref, template_vdi_uuids) except self.XenAPI.Failure, exc: logging.error(_("Unable to Snapshot %(vm_ref)s: %(exc)s") @@ -254,35 +256,40 @@ class VMOps(object): # identify it via the VBD. The base copy is the parent_uuid returned # from the snapshot creation - #TODO(mdietz): explicitly forcing the base_copy and cow names is - #pretty fugly + base_copy_uuid = cow_uuid = None with self._get_snapshot(instance) as snapshot: - params = {'host':dest, 'vdi_uuid':snapshot.vdi_uuids[1], - 'dest_name': 'base_copy.vhd', + # transfer the base copy + base_copy_uuid = snapshot.vdi_uuids[1] + vdi_ref, vm_vdi_rec = \ + VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) + cow_uuid = vm_vdi_rec['uuid'] + + params = {'host': dest, 'vdi_uuid': base_copy_uuid, 'instance_id': instance.id, } + self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) # Now power down the instance and transfer the COW VHD self._shutdown(instance, vm_ref, method='clean') - vdi_ref, vm_vdi_rec = \ - VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) - params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid'], - 'dest_name': 'cow.vhd', + params = {'host': dest, 'vdi_uuid': cow_uuid, 'instance_id': instance.id, } self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) - return snapshot.vdi_uuids[1], vm_vdi_rec['uuid'] - def attach_disk(self, instance): - vm_ref = VMHelper.lookup(self._session, instance.name) + # TODO(mdietz): we could also consider renaming these to something + # sensible so we don't need to blindly pass around dictionaries + return {'base_copy': base_copy_uuid, 'cow': cow_uuid} + def attach_disk(self, instance, disk_info): + vm_ref = VMHelper.lookup(self._session, instance.name) new_base_copy_uuid = str(uuid.uuid4()) - - params = { 'instance_id': instance.id, - 'new_base_copy_uuid': new_base_copy_uuid, - 'new_cow_uuid': str(uuid.uuid4()) } + params = {'instance_id': instance.id, + 'old_base_copy_uuid': disk_info['base_copy'], + 'old_cow_uuid': disk_info['cow'], + 'new_base_copy_uuid': new_base_copy_uuid, + 'new_cow_uuid': str(uuid.uuid4())} self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) @@ -290,7 +297,7 @@ class VMOps(object): # Now we rescan the SR so we find the VHDs VMHelper.scan_sr(self._session) - return new_base_copy_uuid + return new_base_copy_uuid def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ @@ -340,7 +347,6 @@ class VMOps(object): raise RuntimeError(resp_dict['message']) return resp_dict['message'] - def _shutdown(self, instance, vm, method='hard'): """Shutdown an instance """ state = self.get_info(instance['name'])['state'] diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 6869ce8d8..21892ca37 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -197,7 +197,7 @@ class XenAPIConnection(object): off the instance copies over the COW disk""" self._vmops.migrate_disk_and_power_off(instance, dest) - def attach_disk(self, instance): + def attach_disk(self, instance, disk_info): """Moves the copied VDIs into the SR""" return self._vmops.attach_disk(instance) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 9c56cb379..3d3ad4e67 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -68,6 +68,9 @@ def move_vhds_into_sr(session, args): params = pickle.loads(exists(args, 'params')) instance_id = params['instance_id'] + old_base_copy_uuid = params['old_base_copy_uuid'] + old_cow_uuid = params['old_cow_uuid'] + new_base_copy_uuid = params['new_base_copy_uuid'] new_cow_uuid = params['new_cow_uuid'] @@ -77,9 +80,9 @@ def move_vhds_into_sr(session, args): # Discover the copied VHDs locally, and then set up paths to copy # them to under the SR source_image_path = "%s/instance%d" % (IMAGE_PATH, instance_id) - source_base_copy_path = "%s/base_copy.vhd" % source_image_path - source_cow_path = "%s/cow.vhd" % source_image_path - + source_base_copy_path = "%s/%s.vhd" % (source_image_path, + old_base_copy_uuid) + source_cow_path = "%s/%s.vhd" % (source_image_path, old_cow_uuid) temp_vhd_path = "%s/instance%d/" % (sr_temp_path, instance_id) new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid) @@ -106,18 +109,16 @@ def transfer_vhd(session, args): instance_id = params['instance_id'] host = params['host'] vdi_uuid = params['vdi_uuid'] - dest_name = params['dest_name'] sr_path = get_sr_path(session) vhd_path = "%s.vhd" % vdi_uuid source_path = "%s/%s" % (sr_path, vhd_path) - dest_path = '%sinstance%d/' % (IMAGE_PATH, instance_id) + dest_path = '%s:%sinstance%d/' % (host, IMAGE_PATH, instance_id) - dest_path_with_vhd="$s:%s/%s" % (host, dest_path, dest_name) - ssh_cmd = '-e "ssh -o StrictHostKeyChecking=no \'mkdir -p %s\' " ' % dest_path + ssh_cmd = '-e "ssh -o StrictHostKeyChecking=no " ' rsync_args = ['nohup', RSYNC, '-av', '--progress', ssh_cmd, source_path, - dest_path_with_vhd] + dest_path] if subprocess.call(rsync_args) != 0: raise Exception("Unexpected VHD transfer failure") -- cgit From 4574bcdfe303a76a46eb7579a5a70de4e54cc926 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Mon, 14 Feb 2011 23:58:21 -0600 Subject: Tons o loggin --- nova/compute/manager.py | 1 + nova/virt/xenapi_conn.py | 4 ++-- .../xenserver/xenapi/etc/xapi.d/plugins/migration | 20 +++++++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 2308c8315..6a87bb6f1 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -447,6 +447,7 @@ class ComputeManager(manager.Manager): 'args': { 'migration_id': migration_id, 'instance_id': instance_id, + 'disk_info': disk_info, }, }) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 21892ca37..6d40d4615 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -195,11 +195,11 @@ class XenAPIConnection(object): 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""" - self._vmops.migrate_disk_and_power_off(instance, dest) + return self._vmops.migrate_disk_and_power_off(instance, dest) def attach_disk(self, instance, disk_info): """Moves the copied VDIs into the SR""" - return self._vmops.attach_disk(instance) + return self._vmops.attach_disk(instance, disk_info) def suspend(self, instance, callback): """suspend the specified instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 3d3ad4e67..63de5bfba 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -29,6 +29,7 @@ import subprocess import XenAPIPlugin from pluginlib_nova import * +configure_logging('migration') SSH_HOSTS = '/root/.ssh/known_hosts' DEVNULL = '/dev/null' @@ -88,17 +89,27 @@ def move_vhds_into_sr(session, args): new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid) new_cow_path = "%s/%s.vhd" % (temp_vhd_path, new_cow_uuid) + logging.debug('Creating temporary SR path' % temp_vhd_path) os.mkdir(temp_vhd_path) + + logging.debug('Moving %s into %s' % (source_base_copy_path, temp_vhd_path)) shutil.move(source_base_copy_path, new_base_copy_path) + + logging.debug('Moving %s into %s' % (source_cow_path, temp_vhd_path)) shutil.move(source_cow_path, new_cow_path) + logging.debug('Cleaning up %s' % source_image_path) os.rmdir(source_image_path) # Link the COW to the base copy + logging.debug('Attaching COW to the base copy...') subprocess.call([VHD_UTIL, 'modify', '-n', new_cow_path, '-p', new_base_copy_path]) + logging.debug('Moving VHDs into SR %s' % sr_path) shutil.move("%s/*.vhd" % temp_vhd_path, sr_path) + + loggin.debug('Cleaning up temporary SR path %s' % temp_vhd_path) os.rmdir(temp_vhd_path) return None @@ -115,12 +126,19 @@ def transfer_vhd(session, args): source_path = "%s/%s" % (sr_path, vhd_path) dest_path = '%s:%sinstance%d/' % (host, IMAGE_PATH, instance_id) + logging.debug("Preparing to transmit %s to %s" % (source_path, + dest_path)) + ssh_cmd = '-e "ssh -o StrictHostKeyChecking=no " ' rsync_args = ['nohup', RSYNC, '-av', '--progress', ssh_cmd, source_path, dest_path] - if subprocess.call(rsync_args) != 0: + logging.debug('rsync %s' % (' '.join(rsync_args, ))) + + rsync_proc = subprocess.POpen(rsync_args) + logging.debug('Rsync output: \n %s' % rsync_proc.communicate()[0]) + if rsync_proc.returncode != 0 raise Exception("Unexpected VHD transfer failure") -- cgit From 9ab97a9b0860f912be2a085327eea6b9a7fa7674 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 15 Feb 2011 12:33:31 +0100 Subject: Break out of the "for group in rv" loop in security group unit tests so that we are use we are dealing with the correct group. --- nova/tests/test_api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index 2569e262b..efd6d94a8 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -258,6 +258,7 @@ class ApiEc2TestCase(test.TestCase): self.assertEquals(int(group.rules[0].to_port), 81) self.assertEquals(len(group.rules[0].grants), 1) self.assertEquals(str(group.rules[0].grants[0]), '0.0.0.0/0') + break self.expect_http() self.mox.ReplayAll() @@ -324,6 +325,7 @@ class ApiEc2TestCase(test.TestCase): self.assertEquals(int(group.rules[0].to_port), 81) self.assertEquals(len(group.rules[0].grants), 1) self.assertEquals(str(group.rules[0].grants[0]), '::/0') + break self.expect_http() self.mox.ReplayAll() -- cgit From bf8d9d3adfcb5e5cd97ae0d7451e6253892622b1 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 15 Feb 2011 13:18:27 +0100 Subject: Beautify it a little bit, thanks to dabo. --- nova/tests/test_api.py | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index efd6d94a8..fa27825cd 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -248,17 +248,14 @@ class ApiEc2TestCase(test.TestCase): self.mox.ReplayAll() rv = self.ec2.get_all_security_groups() - # I don't bother checkng that we actually find it here, - # because the create/delete unit test further up should - # be good enough for that. - for group in rv: - if group.name == security_group_name: - self.assertEquals(len(group.rules), 1) - self.assertEquals(int(group.rules[0].from_port), 80) - self.assertEquals(int(group.rules[0].to_port), 81) - self.assertEquals(len(group.rules[0].grants), 1) - self.assertEquals(str(group.rules[0].grants[0]), '0.0.0.0/0') - break + + group = [grp for grp in rv if grp.name == security_group_name][0] + + self.assertEquals(len(group.rules), 1) + self.assertEquals(int(group.rules[0].from_port), 80) + self.assertEquals(int(group.rules[0].to_port), 81) + self.assertEquals(len(group.rules[0].grants), 1) + self.assertEquals(str(group.rules[0].grants[0]), '0.0.0.0/0') self.expect_http() self.mox.ReplayAll() @@ -315,17 +312,13 @@ class ApiEc2TestCase(test.TestCase): self.mox.ReplayAll() rv = self.ec2.get_all_security_groups() - # I don't bother checkng that we actually find it here, - # because the create/delete unit test further up should - # be good enough for that. - for group in rv: - if group.name == security_group_name: - self.assertEquals(len(group.rules), 1) - self.assertEquals(int(group.rules[0].from_port), 80) - self.assertEquals(int(group.rules[0].to_port), 81) - self.assertEquals(len(group.rules[0].grants), 1) - self.assertEquals(str(group.rules[0].grants[0]), '::/0') - break + + group = [grp for grp in rv if grp.name == security_group_name][0] + self.assertEquals(len(group.rules), 1) + self.assertEquals(int(group.rules[0].from_port), 80) + self.assertEquals(int(group.rules[0].to_port), 81) + self.assertEquals(len(group.rules[0].grants), 1) + self.assertEquals(str(group.rules[0].grants[0]), '::/0') self.expect_http() self.mox.ReplayAll() -- cgit From dfcf07192cf40d0451c7dfa3802994e4cef8d116 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 15 Feb 2011 14:02:19 +0100 Subject: Naïve attempt at threading rpc requests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nova/rpc.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nova/rpc.py b/nova/rpc.py index 2b1f7298b..efe6164ad 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -29,6 +29,7 @@ import uuid from carrot import connection as carrot_connection from carrot import messaging +from eventlet import greenpool from eventlet import greenthread from nova import context @@ -155,11 +156,15 @@ class AdapterConsumer(TopicConsumer): def __init__(self, connection=None, topic="broadcast", proxy=None): LOG.debug(_('Initing the Adapter Consumer for %s') % topic) self.proxy = proxy + self.pool = greenpool.GreenPool(1024) super(AdapterConsumer, self).__init__(connection=connection, topic=topic) + def receive(self, *args, **kwargs): + self.pool.spawn_n(self._receive, *args, **kwargs) + @exception.wrap_exception - def receive(self, message_data, message): + def _receive(self, message_data, message): """Magically looks for a method on the proxy object and calls it Message data should be a dictionary with two keys: -- cgit -- cgit From a6ec10460b60a6c0073ddcc4790b1fb18a675f1b Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Tue, 15 Feb 2011 15:02:50 +0100 Subject: Adding missing scripts and files to setup.py / MANIFEST.in --- MANIFEST.in | 9 +++++++++ setup.py | 7 ++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 3908830d7..510f1d688 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,14 +6,23 @@ graft doc graft smoketests graft tools graft etc +graft bzrplugins +graft contrib +graft locale +graft plugins include nova/api/openstack/notes.txt +include nova/auth/*.schema include nova/auth/novarc.template +include nova/auth/opendj.sh include nova/auth/slap.sh include nova/cloudpipe/bootscript.sh include nova/cloudpipe/client.ovpn.template +include nova/cloudpipe/bootscript.template include nova/compute/fakevirtinstance.xml include nova/compute/interfaces.template +include nova/console/xvp.conf.template include nova/db/sqlalchemy/migrate_repo/migrate.cfg +include nova/db/sqlalchemy/migrate_repo/README include nova/virt/interfaces.template include nova/virt/libvirt*.xml.template include nova/tests/CA/ diff --git a/setup.py b/setup.py index e3c45ce3e..4e25f43ed 100644 --- a/setup.py +++ b/setup.py @@ -85,9 +85,13 @@ setup(name='nova', packages=find_packages(exclude=['bin', 'smoketests']), include_package_data=True, test_suite='nose.collector', - scripts=['bin/nova-api', + scripts=['bin/nova-ajax-console-proxy', + 'bin/nova-api', + 'bin/nova-combined', 'bin/nova-compute', + 'bin/nova-console', 'bin/nova-dhcpbridge', + 'bin/nova-direct-api', 'bin/nova-import-canonical-imagestore', 'bin/nova-instancemonitor', 'bin/nova-logspool', @@ -96,5 +100,6 @@ setup(name='nova', 'bin/nova-objectstore', 'bin/nova-scheduler', 'bin/nova-spoolsentry', + 'bin/stack', 'bin/nova-volume', 'tools/nova-debug']) -- cgit From 5dd9839c333e4c11de07ee0bad185252b3b4837c Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Tue, 15 Feb 2011 15:44:46 +0100 Subject: Translations will be shipped in po/, not locale/ --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 510f1d688..9b6a3b2da 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,7 +8,7 @@ graft tools graft etc graft bzrplugins graft contrib -graft locale +graft po graft plugins include nova/api/openstack/notes.txt include nova/auth/*.schema -- cgit From aaf5110fa6720d5b32db9b8a41b353a446163396 Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Tue, 15 Feb 2011 15:53:58 +0100 Subject: Missing nova/tests/db/nova.austin.sqlite file --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 9b6a3b2da..f0a9cffb3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -34,6 +34,7 @@ include nova/tests/bundle/1mb.manifest.xml include nova/tests/bundle/1mb.no_kernel_or_ramdisk.manifest.xml include nova/tests/bundle/1mb.part.0 include nova/tests/bundle/1mb.part.1 +include nova/tests/db/nova.austin.sqlite include plugins/xenapi/README include plugins/xenapi/etc/xapi.d/plugins/objectstore include plugins/xenapi/etc/xapi.d/plugins/pluginlib_nova.py -- cgit From bb2ab1211cbe1528e510fa45b189ba4bd04ac2f9 Mon Sep 17 00:00:00 2001 From: "jaypipes@gmail.com" <> Date: Tue, 15 Feb 2011 11:06:28 -0500 Subject: Update .pot file with source file and line numbers after running python setup.py build --- po/nova.pot | 264 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 141 insertions(+), 123 deletions(-) diff --git a/po/nova.pot b/po/nova.pot index 576621ce9..f747ae0f7 100644 --- a/po/nova.pot +++ b/po/nova.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-02-09 09:26-0800\n" +"POT-Creation-Date: 2011-02-15 11:05-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,9 +17,9 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: ../nova/scheduler/chance.py:37 ../nova/scheduler/simple.py:75 -#: ../nova/scheduler/simple.py:110 ../nova/scheduler/simple.py:122 -#: ../nova/scheduler/zone.py:55 +#: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55 +#: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110 +#: ../nova/scheduler/simple.py:122 msgid "No hosts found" msgstr "" @@ -68,11 +68,6 @@ msgstr "" msgid "Volume is already detached" msgstr "" -#: ../nova/virt/fake.py:224 -#, python-format -msgid "Instance %s Not Found" -msgstr "" - #: ../nova/api/openstack/servers.py:138 #, python-format msgid "%(param)s property not found for image %(_image_id)s" @@ -142,7 +137,7 @@ msgstr "" #: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101 #: ../nova/db/sqlalchemy/api.py:709 ../nova/virt/libvirt_conn.py:741 -#: ../nova/api/ec2/__init__.py:322 +#: ../nova/api/ec2/__init__.py:321 #, python-format msgid "Instance %s not found" msgstr "" @@ -383,7 +378,7 @@ msgstr "" msgid "instance %s: getting locked state" msgstr "" -#: ../nova/compute/manager.py:506 ../nova/api/ec2/cloud.py:513 +#: ../nova/compute/manager.py:506 ../nova/api/ec2/cloud.py:515 #, python-format msgid "Get console output for instance %s" msgstr "" @@ -540,40 +535,40 @@ msgstr "" msgid "Failed to open connection to the hypervisor" msgstr "" -#: ../nova/network/linux_net.py:181 +#: ../nova/network/linux_net.py:187 #, python-format msgid "Starting VLAN inteface %s" msgstr "" -#: ../nova/network/linux_net.py:202 +#: ../nova/network/linux_net.py:208 #, python-format msgid "Starting Bridge interface for %s" msgstr "" #. pylint: disable-msg=W0703 -#: ../nova/network/linux_net.py:308 +#: ../nova/network/linux_net.py:314 #, python-format msgid "Hupping dnsmasq threw %s" msgstr "" -#: ../nova/network/linux_net.py:310 +#: ../nova/network/linux_net.py:316 #, python-format msgid "Pid %d is stale, relaunching dnsmasq" msgstr "" #. pylint: disable-msg=W0703 -#: ../nova/network/linux_net.py:352 +#: ../nova/network/linux_net.py:358 #, python-format msgid "killing radvd threw %s" msgstr "" -#: ../nova/network/linux_net.py:354 +#: ../nova/network/linux_net.py:360 #, python-format msgid "Pid %d is stale, relaunching radvd" msgstr "" #. pylint: disable-msg=W0703 -#: ../nova/network/linux_net.py:443 +#: ../nova/network/linux_net.py:449 #, python-format msgid "Killing dnsmasq threw %s" msgstr "" @@ -598,37 +593,42 @@ msgstr "" msgid "Running cmd (subprocess): %s" msgstr "" -#: ../nova/utils.py:141 +#: ../nova/utils.py:141 ../nova/utils.py:181 #, python-format msgid "Result was %s" msgstr "" -#: ../nova/utils.py:179 +#: ../nova/utils.py:157 +#, python-format +msgid "Running cmd (SSH): %s" +msgstr "" + +#: ../nova/utils.py:215 #, python-format msgid "debug in callback: %s" msgstr "" -#: ../nova/utils.py:184 +#: ../nova/utils.py:220 #, python-format msgid "Running %s" msgstr "" -#: ../nova/utils.py:215 +#: ../nova/utils.py:251 #, python-format msgid "Link Local address is not found.:%s" msgstr "" -#: ../nova/utils.py:218 +#: ../nova/utils.py:254 #, python-format msgid "Couldn't get Link Local IP of %(interface)s :%(ex)s" msgstr "" -#: ../nova/utils.py:316 +#: ../nova/utils.py:352 #, python-format msgid "Invalid backend: %s" msgstr "" -#: ../nova/utils.py:327 +#: ../nova/utils.py:363 #, python-format msgid "backend %s" msgstr "" @@ -793,129 +793,129 @@ msgstr "" msgid "VDI %s is still available" msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:445 +#: ../nova/virt/xenapi/vm_utils.py:453 #, python-format msgid "(VM_UTILS) xenserver vm state -> |%s|" msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:447 +#: ../nova/virt/xenapi/vm_utils.py:455 #, python-format msgid "(VM_UTILS) xenapi power_state -> |%s|" msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:507 +#: ../nova/virt/xenapi/vm_utils.py:515 #, python-format msgid "VHD %(vdi_uuid)s has parent %(parent_ref)s" msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:524 +#: ../nova/virt/xenapi/vm_utils.py:532 #, python-format msgid "Re-scanning SR %s" msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:549 +#: ../nova/virt/xenapi/vm_utils.py:557 #, python-format msgid "" "VHD coalesce attempts exceeded (%(counter)d > %(max_attempts)d), giving up..." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:556 +#: ../nova/virt/xenapi/vm_utils.py:564 #, python-format msgid "" "Parent %(parent_uuid)s doesn't match original parent " "%(original_parent_uuid)s, waiting for coalesce..." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:572 +#: ../nova/virt/xenapi/vm_utils.py:580 #, python-format msgid "No VDIs found for VM %s" msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:576 +#: ../nova/virt/xenapi/vm_utils.py:584 #, python-format msgid "Unexpected number of VDIs (%(num_vdis)s) found for VM %(vm_ref)s" msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:635 +#: ../nova/virt/xenapi/vm_utils.py:643 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:188 #, python-format msgid "Creating VBD for VDI %s ... " msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:637 +#: ../nova/virt/xenapi/vm_utils.py:645 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:190 #, python-format msgid "Creating VBD for VDI %s done." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:639 +#: ../nova/virt/xenapi/vm_utils.py:647 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:192 #, python-format msgid "Plugging VBD %s ... " msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:641 +#: ../nova/virt/xenapi/vm_utils.py:649 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:194 #, python-format msgid "Plugging VBD %s done." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:643 +#: ../nova/virt/xenapi/vm_utils.py:651 #, python-format msgid "VBD %(vbd)s plugged as %(orig_dev)s" msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:646 +#: ../nova/virt/xenapi/vm_utils.py:654 #, python-format msgid "VBD %(vbd)s plugged into wrong dev, remapping to %(dev)s" msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:650 +#: ../nova/virt/xenapi/vm_utils.py:658 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:197 #, python-format msgid "Destroying VBD for VDI %s ... " msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:653 +#: ../nova/virt/xenapi/vm_utils.py:661 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:200 #, python-format msgid "Destroying VBD for VDI %s done." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:665 +#: ../nova/virt/xenapi/vm_utils.py:673 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:211 msgid "VBD.unplug successful first time." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:670 +#: ../nova/virt/xenapi/vm_utils.py:678 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:216 msgid "VBD.unplug rejected: retrying..." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:674 +#: ../nova/virt/xenapi/vm_utils.py:682 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:220 msgid "VBD.unplug successful eventually." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:677 +#: ../nova/virt/xenapi/vm_utils.py:685 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:223 #, python-format msgid "Ignoring XenAPI.Failure in VBD.unplug: %s" msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:686 +#: ../nova/virt/xenapi/vm_utils.py:694 #: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:66 #, python-format msgid "Ignoring XenAPI.Failure %s" msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:717 +#: ../nova/virt/xenapi/vm_utils.py:725 #, python-format msgid "" "Writing partition table %(primary_first)d %(primary_last)d to %(dest)s..." msgstr "" -#: ../nova/virt/xenapi/vm_utils.py:729 +#: ../nova/virt/xenapi/vm_utils.py:737 #, python-format msgid "Writing partition table %s done." msgstr "" @@ -954,105 +954,105 @@ msgstr "" msgid "No floating ip for address %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:933 +#: ../nova/db/sqlalchemy/api.py:939 #, python-format msgid "no keypair for user %(user_id)s, name %(name)s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1048 ../nova/db/sqlalchemy/api.py:1106 +#: ../nova/db/sqlalchemy/api.py:1054 ../nova/db/sqlalchemy/api.py:1112 #, python-format msgid "No network for id %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1078 +#: ../nova/db/sqlalchemy/api.py:1084 #, python-format msgid "No network for bridge %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1092 +#: ../nova/db/sqlalchemy/api.py:1098 #, python-format msgid "No network for instance %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1227 +#: ../nova/db/sqlalchemy/api.py:1233 #, python-format msgid "Token %s does not exist" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1252 +#: ../nova/db/sqlalchemy/api.py:1258 #, python-format msgid "No quota for project_id %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1405 ../nova/db/sqlalchemy/api.py:1451 -#: ../nova/api/ec2/__init__.py:328 +#: ../nova/db/sqlalchemy/api.py:1411 ../nova/db/sqlalchemy/api.py:1457 +#: ../nova/api/ec2/__init__.py:327 #, python-format msgid "Volume %s not found" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1464 +#: ../nova/db/sqlalchemy/api.py:1470 #, python-format msgid "No export device found for volume %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1477 +#: ../nova/db/sqlalchemy/api.py:1483 #, python-format msgid "No target id found for volume %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1522 +#: ../nova/db/sqlalchemy/api.py:1528 #, python-format msgid "No security group with id %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1539 +#: ../nova/db/sqlalchemy/api.py:1545 #, python-format msgid "No security group named %(group_name)s for project: %(project_id)s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1632 +#: ../nova/db/sqlalchemy/api.py:1638 #, python-format msgid "No secuity group rule with id %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1706 +#: ../nova/db/sqlalchemy/api.py:1712 #, python-format msgid "No user for id %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1722 +#: ../nova/db/sqlalchemy/api.py:1728 #, python-format msgid "No user for access key %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1784 +#: ../nova/db/sqlalchemy/api.py:1790 #, python-format msgid "No project with id %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1929 +#: ../nova/db/sqlalchemy/api.py:1935 #, python-format msgid "No console pool with id %(pool_id)s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1946 +#: ../nova/db/sqlalchemy/api.py:1952 #, python-format msgid "" "No console pool of type %(console_type)s for compute host %(compute_host)s " "on proxy host %(host)s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:1985 +#: ../nova/db/sqlalchemy/api.py:1991 #, python-format msgid "No console for instance %(instance_id)s in pool %(pool_id)s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:2007 +#: ../nova/db/sqlalchemy/api.py:2013 #, python-format msgid "on instance %s" msgstr "" -#: ../nova/db/sqlalchemy/api.py:2008 +#: ../nova/db/sqlalchemy/api.py:2014 #, python-format msgid "No console with id %(console_id)s %(idesc)s" msgstr "" @@ -1262,129 +1262,129 @@ msgstr "" msgid "Delete key pair %s" msgstr "" -#: ../nova/api/ec2/cloud.py:384 +#: ../nova/api/ec2/cloud.py:386 #, python-format msgid "%s is not a valid ipProtocol" msgstr "" -#: ../nova/api/ec2/cloud.py:388 +#: ../nova/api/ec2/cloud.py:390 msgid "Invalid port range" msgstr "" -#: ../nova/api/ec2/cloud.py:419 +#: ../nova/api/ec2/cloud.py:421 #, python-format msgid "Revoke security group ingress %s" msgstr "" -#: ../nova/api/ec2/cloud.py:428 ../nova/api/ec2/cloud.py:457 +#: ../nova/api/ec2/cloud.py:430 ../nova/api/ec2/cloud.py:459 msgid "Not enough parameters to build a valid rule." msgstr "" -#: ../nova/api/ec2/cloud.py:441 +#: ../nova/api/ec2/cloud.py:443 msgid "No rule for the specified parameters." msgstr "" -#: ../nova/api/ec2/cloud.py:448 +#: ../nova/api/ec2/cloud.py:450 #, python-format msgid "Authorize security group ingress %s" msgstr "" -#: ../nova/api/ec2/cloud.py:462 +#: ../nova/api/ec2/cloud.py:464 #, python-format msgid "This rule already exists in group %s" msgstr "" -#: ../nova/api/ec2/cloud.py:490 +#: ../nova/api/ec2/cloud.py:492 #, python-format msgid "Create Security Group %s" msgstr "" -#: ../nova/api/ec2/cloud.py:493 +#: ../nova/api/ec2/cloud.py:495 #, python-format msgid "group %s already exists" msgstr "" -#: ../nova/api/ec2/cloud.py:505 +#: ../nova/api/ec2/cloud.py:507 #, python-format msgid "Delete security group %s" msgstr "" -#: ../nova/api/ec2/cloud.py:582 +#: ../nova/api/ec2/cloud.py:584 #, python-format msgid "Create volume of %s GB" msgstr "" -#: ../nova/api/ec2/cloud.py:610 +#: ../nova/api/ec2/cloud.py:612 #, python-format msgid "Attach volume %(volume_id)s to instance %(instance_id)s at %(device)s" msgstr "" -#: ../nova/api/ec2/cloud.py:627 +#: ../nova/api/ec2/cloud.py:629 #, python-format msgid "Detach volume %s" msgstr "" -#: ../nova/api/ec2/cloud.py:759 +#: ../nova/api/ec2/cloud.py:761 msgid "Allocate address" msgstr "" -#: ../nova/api/ec2/cloud.py:764 +#: ../nova/api/ec2/cloud.py:766 #, python-format msgid "Release address %s" msgstr "" -#: ../nova/api/ec2/cloud.py:769 +#: ../nova/api/ec2/cloud.py:771 #, python-format msgid "Associate address %(public_ip)s to instance %(instance_id)s" msgstr "" -#: ../nova/api/ec2/cloud.py:778 +#: ../nova/api/ec2/cloud.py:780 #, python-format msgid "Disassociate address %s" msgstr "" -#: ../nova/api/ec2/cloud.py:805 +#: ../nova/api/ec2/cloud.py:807 msgid "Going to start terminating instances" msgstr "" -#: ../nova/api/ec2/cloud.py:813 +#: ../nova/api/ec2/cloud.py:815 #, python-format msgid "Reboot instance %r" msgstr "" -#: ../nova/api/ec2/cloud.py:850 +#: ../nova/api/ec2/cloud.py:867 #, python-format msgid "De-registering image %s" msgstr "" -#: ../nova/api/ec2/cloud.py:858 +#: ../nova/api/ec2/cloud.py:875 #, python-format msgid "Registered image %(image_location)s with id %(image_id)s" msgstr "" -#: ../nova/api/ec2/cloud.py:865 ../nova/api/ec2/cloud.py:880 +#: ../nova/api/ec2/cloud.py:882 ../nova/api/ec2/cloud.py:900 #, python-format msgid "attribute not supported: %s" msgstr "" -#: ../nova/api/ec2/cloud.py:870 +#: ../nova/api/ec2/cloud.py:890 #, python-format msgid "invalid id: %s" msgstr "" -#: ../nova/api/ec2/cloud.py:883 +#: ../nova/api/ec2/cloud.py:903 msgid "user or group not specified" msgstr "" -#: ../nova/api/ec2/cloud.py:885 +#: ../nova/api/ec2/cloud.py:905 msgid "only group \"all\" is supported" msgstr "" -#: ../nova/api/ec2/cloud.py:887 +#: ../nova/api/ec2/cloud.py:907 msgid "operation_type must be add or remove" msgstr "" -#: ../nova/api/ec2/cloud.py:888 +#: ../nova/api/ec2/cloud.py:908 #, python-format msgid "Updating image %s publicity" msgstr "" @@ -1507,21 +1507,29 @@ msgstr "" msgid "VM %(vm)s already halted, skipping shutdown..." msgstr "" -#: ../nova/virt/xenapi/vmops.py:444 +#: ../nova/virt/xenapi/vmops.py:295 +msgid "Removing kernel/ramdisk files" +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:305 +msgid "kernel/ramdisk files removed" +msgstr "" + +#: ../nova/virt/xenapi/vmops.py:459 #, python-format msgid "" "TIMEOUT: The call to %(method)s timed out. VM id=%(instance_id)s; args=" "%(strargs)s" msgstr "" -#: ../nova/virt/xenapi/vmops.py:447 +#: ../nova/virt/xenapi/vmops.py:462 #, python-format msgid "" "The call to %(method)s returned an error: %(e)s. VM id=%(instance_id)s; args=" "%(strargs)s" msgstr "" -#: ../nova/virt/xenapi/vmops.py:638 +#: ../nova/virt/xenapi/vmops.py:653 #, python-format msgid "OpenSSL error: %s" msgstr "" @@ -1553,74 +1561,74 @@ msgstr "" msgid "Launching VPN for %s" msgstr "" -#: ../nova/image/s3.py:89 +#: ../nova/image/s3.py:99 #, python-format msgid "Image %s could not be found" msgstr "" -#: ../nova/api/ec2/__init__.py:126 +#: ../nova/api/ec2/__init__.py:125 msgid "Too many failed authentications." msgstr "" -#: ../nova/api/ec2/__init__.py:136 +#: ../nova/api/ec2/__init__.py:135 #, python-format msgid "" "Access key %(access_key)s has had %(failures)d failed authentications and " "will be locked out for %(lock_mins)d minutes." msgstr "" -#: ../nova/api/ec2/__init__.py:174 ../nova/objectstore/handler.py:140 +#: ../nova/api/ec2/__init__.py:173 ../nova/objectstore/handler.py:140 #, python-format msgid "Authentication Failure: %s" msgstr "" -#: ../nova/api/ec2/__init__.py:187 +#: ../nova/api/ec2/__init__.py:186 #, python-format msgid "Authenticated Request For %(uname)s:%(pname)s)" msgstr "" -#: ../nova/api/ec2/__init__.py:212 +#: ../nova/api/ec2/__init__.py:211 #, python-format msgid "action: %s" msgstr "" -#: ../nova/api/ec2/__init__.py:214 +#: ../nova/api/ec2/__init__.py:213 #, python-format msgid "arg: %(key)s\t\tval: %(value)s" msgstr "" -#: ../nova/api/ec2/__init__.py:286 +#: ../nova/api/ec2/__init__.py:285 #, python-format msgid "" "Unauthorized request for controller=%(controller)s and action=%(action)s" msgstr "" -#: ../nova/api/ec2/__init__.py:319 +#: ../nova/api/ec2/__init__.py:318 #, python-format msgid "InstanceNotFound raised: %s" msgstr "" -#: ../nova/api/ec2/__init__.py:325 +#: ../nova/api/ec2/__init__.py:324 #, python-format msgid "VolumeNotFound raised: %s" msgstr "" -#: ../nova/api/ec2/__init__.py:331 +#: ../nova/api/ec2/__init__.py:330 #, python-format msgid "NotFound raised: %s" msgstr "" -#: ../nova/api/ec2/__init__.py:334 +#: ../nova/api/ec2/__init__.py:333 #, python-format msgid "ApiError raised: %s" msgstr "" -#: ../nova/api/ec2/__init__.py:343 +#: ../nova/api/ec2/__init__.py:342 #, python-format msgid "Unexpected error raised: %s" msgstr "" -#: ../nova/api/ec2/__init__.py:348 +#: ../nova/api/ec2/__init__.py:347 msgid "An unknown error has occurred. Please try your request again." msgstr "" @@ -1708,6 +1716,11 @@ msgstr "" msgid "Found instance: %s" msgstr "" +#: ../nova/volume/san.py:67 +#, python-format +msgid "Could not find iSCSI export for volume %s" +msgstr "" + #: ../nova/api/ec2/apirequest.py:99 #, python-format msgid "" @@ -2155,51 +2168,56 @@ msgid "" "on interface %(interface)s" msgstr "" -#: ../nova/network/manager.py:139 +#: ../nova/virt/fake.py:224 +#, python-format +msgid "Instance %s Not Found" +msgstr "" + +#: ../nova/network/manager.py:143 msgid "setting network host" msgstr "" -#: ../nova/network/manager.py:194 +#: ../nova/network/manager.py:198 #, python-format msgid "Leasing IP %s" msgstr "" -#: ../nova/network/manager.py:198 +#: ../nova/network/manager.py:202 #, python-format msgid "IP %s leased that isn't associated" msgstr "" -#: ../nova/network/manager.py:202 +#: ../nova/network/manager.py:206 #, python-format msgid "IP %(address)s leased to bad mac %(inst_addr)s vs %(mac)s" msgstr "" -#: ../nova/network/manager.py:210 +#: ../nova/network/manager.py:214 #, python-format msgid "IP %s leased that was already deallocated" msgstr "" -#: ../nova/network/manager.py:215 +#: ../nova/network/manager.py:219 #, python-format msgid "Releasing IP %s" msgstr "" -#: ../nova/network/manager.py:219 +#: ../nova/network/manager.py:223 #, python-format msgid "IP %s released that isn't associated" msgstr "" -#: ../nova/network/manager.py:223 +#: ../nova/network/manager.py:227 #, python-format msgid "IP %(address)s released from bad mac %(inst_addr)s vs %(mac)s" msgstr "" -#: ../nova/network/manager.py:226 +#: ../nova/network/manager.py:230 #, python-format msgid "IP %s released that was not leased" msgstr "" -#: ../nova/network/manager.py:461 +#: ../nova/network/manager.py:464 #, python-format msgid "Dissassociated %s stale fixed ip(s)" msgstr "" -- cgit From 5a988eb393c306097250a7f17ea65f0919fd9219 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Tue, 15 Feb 2011 12:43:29 -0400 Subject: fixed / renamed migration scripts --- .../migrate_repo/versions/003_add_zone_tables.py | 62 ++++++++++++++++++++++ .../sqlalchemy/migrate_repo/versions/003_cactus.py | 62 ---------------------- 2 files changed, 62 insertions(+), 62 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_add_zone_tables.py delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_add_zone_tables.py b/nova/db/sqlalchemy/migrate_repo/versions/003_add_zone_tables.py new file mode 100644 index 000000000..d2b6b9570 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_add_zone_tables.py @@ -0,0 +1,62 @@ +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# +# New Tables +# +zones = Table('zones', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('api_url', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('username', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('password', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + ) + + +# +# Tables to alter +# + +# (none currently) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (zones, ): + try: + table.create() + except Exception: + logging.info(repr(table)) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py deleted file mode 100644 index eb3287077..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ /dev/null @@ -1,62 +0,0 @@ -# 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. - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - - -# -# New Tables -# -child_zones = Table('child_zones', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('api_url', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('username', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('password', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - ) - - -# -# Tables to alter -# - -# (none currently) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (child_zones, ): - try: - table.create() - except Exception: - logging.info(repr(table)) -- cgit From bf82637cad867b0e8fb6ad868f60c6dcd66d7f97 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 11:05:20 -0600 Subject: Better host acquisition --- nova/compute/manager.py | 7 ++++--- nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 3 ++- nova/db/sqlalchemy/models.py | 3 ++- nova/virt/xenapi_conn.py | 4 ++++ plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6a87bb6f1..7e929d715 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -404,12 +404,13 @@ class ComputeManager(manager.Manager): instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_create(context, { 'instance_id': instance_id, - 'source_host': instance_ref['host'], - 'dest_host': socket.gethostname(), + 'source_compute': instance_ref['host'], + 'dest_compute': socket.gethostname(), + 'dest_host': self.driver.get_host_ip_addr(), 'status': 'pre-migrating' }) LOG.audit(_('instance %s: migrating to '), instance_id, context=context) service = self.db.service_get_by_host_and_topic(context, - instance_ref['host'], FLAGS.compute_topic) + migration_ref['source_compute'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, service['host']) rpc.cast(context, topic, diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py index 02d9177bd..4aab5bdc6 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -39,7 +39,8 @@ migrations = Table('migrations', meta, Column('deleted_at', DateTime(timezone=False)), Column('deleted', Boolean(create_constraint=True, name=None)), Column('id', Integer(), primary_key=True, nullable=False), - Column('source_host', String(255)), + Column('source_compute', String(255)), + Column('dest_compute', String(255)), Column('dest_host', String(255)), Column('instance_id', Integer, ForeignKey('instances.id'), nullable=True), diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index ebf3a382b..1c84e15dd 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -370,7 +370,8 @@ class Migration(BASE, NovaBase): """Represents a running host-to-host migration.""" __tablename__ = 'migrations' id = Column(Integer, primary_key=True, nullable=False) - source_host = Column(String(255)) + source_compute = Column(String(255)) + dest_compute = Column(String(255)) dest_host = Column(String(255)) instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True) status = Column(String(255)) #TODO(_cerberus_): enum diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 6d40d4615..19b5269f5 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -224,6 +224,10 @@ class XenAPIConnection(object): def get_ajax_console(self, instance): """Return link to instance's ajax console""" return self._vmops.get_ajax_console(instance) + + def get_host_ip_addr(self): + xs_url = urlparse.urlpase(FLAGS.xenapi_connection_url) + return xs_url.netloc def attach_volume(self, instance_name, device_path, mountpoint): """Attach volume storage to VM instance""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 63de5bfba..97c970da5 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -136,7 +136,7 @@ def transfer_vhd(session, args): logging.debug('rsync %s' % (' '.join(rsync_args, ))) - rsync_proc = subprocess.POpen(rsync_args) + rsync_proc = subprocess.Popen(rsync_args) logging.debug('Rsync output: \n %s' % rsync_proc.communicate()[0]) if rsync_proc.returncode != 0 raise Exception("Unexpected VHD transfer failure") -- cgit From 03a8d1baae00a4150a02ac2f0b04c413dd3b00e0 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 11:19:27 -0600 Subject: derp --- nova/compute/manager.py | 2 +- nova/virt/xenapi_conn.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 7e929d715..546d07a09 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -440,7 +440,7 @@ class ComputeManager(manager.Manager): #after resizing service = self.db.service_get_by_host_and_topic(context, - migration_ref['dest_host'], FLAGS.compute_topic) + migration_ref['dest_compute'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, service['host']) rpc.cast(context, topic, diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 19b5269f5..2671f1a7b 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -226,7 +226,7 @@ class XenAPIConnection(object): return self._vmops.get_ajax_console(instance) def get_host_ip_addr(self): - xs_url = urlparse.urlpase(FLAGS.xenapi_connection_url) + xs_url = urlparse.urlparse(FLAGS.xenapi_connection_url) return xs_url.netloc def attach_volume(self, instance_name, device_path, mountpoint): -- cgit From f02c41a7fe332b215421320d041a944e4b9ee9ee Mon Sep 17 00:00:00 2001 From: "jaypipes@gmail.com" <> Date: Tue, 15 Feb 2011 12:39:17 -0500 Subject: Replace placeholders in nova.pot with some actual values. --- po/nova.pot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/po/nova.pot b/po/nova.pot index f747ae0f7..6cbb2f129 100644 --- a/po/nova.pot +++ b/po/nova.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-02-15 11:05-0500\n" +"POT-Creation-Date: 2011-02-15 12:38-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" -- cgit From 15cdeef7820aacd0b1ff95da48816cba9f2544ba Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 18:01:13 +0000 Subject: Adding more documentation, code-cleanup --- nova/virt/xenapi/vm_utils.py | 52 ++++++++++++++----- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 60 +++++++++++----------- 2 files changed, 70 insertions(+), 42 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index a6312d00c..5eab2a38f 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -24,6 +24,7 @@ import pickle import re import time import urllib +import uuid from xml.dom import minidom from eventlet import event @@ -318,9 +319,15 @@ class VMHelper(HelperBase): if sr_ref is None: raise exception.NotFound('Cannot find SR to write VDI to') + # NOTE(sirp): The Glance plugin runs under Python 2.4 which does not + # have the `uuid` module. To work around this, we generate the uuids + # here (under Python 2.6+) and pass them as arguments + uuid_stack = [str(uuid.uuid4()) for i in xrange(2)] + params = {'image_id': image, 'glance_host': FLAGS.glance_host, - 'glance_port': FLAGS.glance_port} + 'glance_port': FLAGS.glance_port, + 'uuid_stack': uuid_stack} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'get_vdi', kwargs) @@ -372,14 +379,35 @@ class VMHelper(HelperBase): @classmethod def determine_disk_image_type(cls, instance): - instance_id = instance.id + """Disk Image Types are used to determine where the kernel will reside + within an image. To figure out which type we're dealing with, we use + the following rules: + + 1. If the instance is specifying a kernel explicitly, we must be using + a 'disk' image (kernel outside of the image) + + 2. If the kernel isn't specified, then we have two different + scenarios: + + a) If the image is in Glance, then we can use the 'disk_format' + property to determine if the image is really a VHD-style image + or if it's a RAW image + + b) If the image is not in Glance, then it must be a RAW image + (since we don't have a way of identifying VHD images...yet) + """ + def log_disk_format(disk_format): + disk_format = disk_format.upper() + image_id = instance.image_id + instance_id = instance.id + LOG.debug(_("Detected %(disk_format)s format for image " + "%(image_id)s, instance %(instance_id)s") % locals()) + if instance.kernel_id: - #if kernel is not present we must download a raw disk - LOG.debug(_("Instance %(instance_id)s will use DISK format") % - locals()) + # 1. DISK + log_disk_format('disk') return ImageType.DISK - - if FLAGS.xenapi_image_service == 'glance': + elif FLAGS.xenapi_image_service == 'glance': # if using glance, then we could be VHD format client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) meta = client.get_image_meta(instance.image_id) @@ -388,12 +416,12 @@ class VMHelper(HelperBase): # TODO(sirp): When Glance treats disk_format as a first class # attribute, we should start using that rather than an image-property if disk_format == 'vhd': - LOG.debug(_("Instance %(instance_id)s will use DISK_VHD format") % - locals()) + # 2a. DISK_VHD + log_disk_format('disk_vhd') return ImageType.DISK_VHD - - LOG.debug(_("Instance %(instance_id)s will use DISK_RAW format") % - locals()) + + # 2b. DISK_RAW + log_disk_format('disk_raw') return ImageType.DISK_RAW @classmethod diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 0cb3b52db..2018dca5f 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -21,20 +21,14 @@ # XenAPI plugin for managing glance images # -import base64 -import errno -import hmac import httplib import os import os.path import pickle -import sha import shlex import shutil import subprocess import tempfile -import time -import urlparse import XenAPIPlugin @@ -74,11 +68,14 @@ def _copy_kernel_vdi(dest,copy_args): return filename -def execute(cmd): - args = shlex.split(cmd) - proc = subprocess.Popen( - args, stdout=subprocess.PIPE, stdin=subprocess.PIPE) - return proc +def assert_process_success(proc, cmd): + """Ensure that the process returned a zero exit code indicating success + """ + out, err = proc.communicate() + ret = proc.returncode + if ret != 0: + msg = "%(cmd)s returned non-zero exit code (%i): '%s'" % (ret, err) + raise Exception(msg) def get_vdi(session, args): @@ -88,6 +85,7 @@ def get_vdi(session, args): image_id = params["image_id"] glance_host = params["glance_host"] glance_port = params["glance_port"] + uuid_stack = params["uuid_stack"] def unbundle_xfer(sr_path, staging_path): """ @@ -101,17 +99,17 @@ def get_vdi(session, args): elif resp.status != httplib.OK: raise Exception("Unexpected response from Glance %i" % res.status) - tar_proc = execute("tar -zx --directory=%(staging_path)s" % locals()) + tar_args = shlex.split( + "tar -zx --directory=%(staging_path)s" % locals()) + tar_proc = subprocess.Popen( + tar_args, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + chunk = resp.read(CHUNK_SIZE) while chunk: tar_proc.stdin.write(chunk) chunk = resp.read(CHUNK_SIZE) - out, err = tar_proc.communicate() - # TODO(sirp): write assert_process_success - ret = tar_proc.returncode - if ret != 0: - raise Exception( - "tar returned non-zero exit code (%i): '%s'" % (ret, err)) + + assert_process_success(tar_proc, "tar") conn.close() def fixup_vhds(sr_path, staging_path): @@ -120,7 +118,7 @@ def get_vdi(session, args): def rename_with_uuid(orig_path): """Generate a uuid and rename the file with the uuid""" orig_dirname = os.path.dirname(orig_path) - uuid = generate_uuid() + uuid = uuid_stack.pop() new_path = os.path.join(orig_dirname, "%s.vhd" % uuid) os.rename(orig_path, new_path) return new_path, uuid @@ -133,11 +131,13 @@ def get_vdi(session, args): return new_path def link_vhds(child_path, parent_path): - proc = execute("vhd-util modify -n %(child_path)s -p %(parent_path)s" - % locals()) - out, err = proc.communicate() - if proc.returncode != 0: - raise Exception("Failed to link vhds: '%s'" % err) + modify_args = shlex.split( + "vhd-util modify -n %(child_path)s -p %(parent_path)s" + % locals()) + modify_proc = subprocess.Popen( + modify_args, stderr=subprocess.PIPE) + assert_process_success(modify_proc, "vhd-util") + image_path = os.path.join(staging_path, 'image') @@ -210,7 +210,10 @@ def put_vdis(session, args): conn.putheader(header, value) conn.endheaders() - tar_proc = execute("tar -zc --directory=%(staging_path)s ." % locals()) + tar_args = shlex.split( + "tar -zc --directory=%(staging_path)s ." % locals()) + tar_proc = subprocess.Popen( + tar_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) chunk = tar_proc.stdout.read(CHUNK_SIZE) while chunk: @@ -218,6 +221,8 @@ def put_vdis(session, args): chunk = tar_proc.stdout.read(CHUNK_SIZE) conn.send("0\r\n\r\n") + assert_process_success(tar_proc, "tar") + resp = conn.getresponse() #FIXME(sirp): should this be 201 Created? if resp.status != httplib.OK: @@ -277,11 +282,6 @@ def find_sr(session): return sr return None -def generate_uuid(): - # NOTE(sirp): Python2.4 does not include the uuid module - proc = execute("uuidgen") - uuid = proc.stdout.read().strip() - return uuid if __name__ == '__main__': XenAPIPlugin.dispatch({'put_vdis': put_vdis, -- cgit From 0fc3a184230c479254b9f713ea61de2f24f680ab Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 18:23:14 +0000 Subject: Moving SR path code outside of glance plugin --- nova/virt/xenapi/vm_utils.py | 21 +++++++++++-- nova/virt/xenapi_conn.py | 2 ++ plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 34 ++-------------------- 3 files changed, 23 insertions(+), 34 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 5eab2a38f..e73bc7f2b 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -285,7 +285,8 @@ class VMHelper(HelperBase): params = {'vdi_uuids': vdi_uuids, 'image_id': image_id, 'glance_host': FLAGS.glance_host, - 'glance_port': FLAGS.glance_port} + 'glance_port': FLAGS.glance_port, + 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'put_vdis', kwargs) @@ -327,7 +328,8 @@ class VMHelper(HelperBase): params = {'image_id': image, 'glance_host': FLAGS.glance_host, 'glance_port': FLAGS.glance_port, - 'uuid_stack': uuid_stack} + 'uuid_stack': uuid_stack, + 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'get_vdi', kwargs) @@ -685,6 +687,21 @@ def find_sr(session): return None +def get_sr_path(session): + """Return the path to our Storage Repository + + This is used when we're dealing with VHDs directly, either by taking + snapshots or by restoring an image in the DISK_VHD format. + """ + # TODO(sirp): add safe_find_sr + sr_ref = find_sr(session) + if sr_ref is None: + raise Exception('Cannot find SR to read VDI from') + sr_rec = session.get_xenapi().SR.get_record(sr_ref) + sr_uuid = sr_rec["uuid"] + return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid) + + def remap_vbd_dev(dev): """Return the appropriate location for a plugged-in VBD device diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index a0b0499b8..8ae5684b0 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -100,6 +100,8 @@ flags.DEFINE_integer('xenapi_vhd_coalesce_max_attempts', 5, 'Max number of times to poll for VHD to coalesce.' ' Used only if connection_type=xenapi.') +flags.DEFINE_string('xenapi_sr_base_path', '/var/run/sr-mount', + 'Base path to the storage repository') flags.DEFINE_string('target_host', None, 'iSCSI Target Host') diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 2018dca5f..0b270f5f9 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -38,7 +38,6 @@ configure_logging('glance') CHUNK_SIZE = 8192 KERNEL_DIR = '/boot/guest' -FILE_SR_PATH = '/var/run/sr-mount' def copy_kernel_vdi(session,args): vdi = exists(args, 'vdi-ref') @@ -86,6 +85,7 @@ def get_vdi(session, args): glance_host = params["glance_host"] glance_port = params["glance_port"] uuid_stack = params["uuid_stack"] + sr_path = params["sr_path"] def unbundle_xfer(sr_path, staging_path): """ @@ -161,7 +161,6 @@ def get_vdi(session, args): move_into_sr(base_copy_path) return vdi_uuid - sr_path = get_sr_path(session) staging_path = make_staging_area(sr_path) try: unbundle_xfer(sr_path, staging_path) @@ -179,6 +178,7 @@ def put_vdis(session, args): image_id = params["image_id"] glance_host = params["glance_host"] glance_port = params["glance_port"] + sr_path = params["sr_path"] def prepare_staging_area(sr_path, staging_path): """ @@ -230,7 +230,6 @@ def put_vdis(session, args): conn.close() - sr_path = get_sr_path(session) staging_path = make_staging_area(sr_path) try: prepare_staging_area(sr_path, staging_path) @@ -254,35 +253,6 @@ def cleanup_staging_area(staging_path): shutil.rmtree(staging_path) -def get_sr_path(session): - sr_ref = find_sr(session) - - if sr_ref is None: - raise Exception('Cannot find SR to read VDI from') - - sr_rec = session.xenapi.SR.get_record(sr_ref) - sr_uuid = sr_rec["uuid"] - sr_path = os.path.join(FILE_SR_PATH, sr_uuid) - return sr_path - - -#TODO(sirp): both objectstore and glance need this, should this be refactored -#into common lib -def find_sr(session): - host = get_this_host(session) - srs = session.xenapi.SR.get_all() - for sr in srs: - sr_rec = session.xenapi.SR.get_record(sr) - if not ('i18n-key' in sr_rec['other_config'] and - sr_rec['other_config']['i18n-key'] == 'local-storage'): - continue - for pbd in sr_rec['PBDs']: - pbd_rec = session.xenapi.PBD.get_record(pbd) - if pbd_rec['host'] == host: - return sr - return None - - if __name__ == '__main__': XenAPIPlugin.dispatch({'put_vdis': put_vdis, 'get_vdi': get_vdi, -- cgit From b4b1a7fbd55784157b3084016d4dfe2bd0120e51 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 18:36:17 +0000 Subject: Adding safe_find_sr --- nova/virt/xenapi/vm_utils.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index e73bc7f2b..37ce86885 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -316,9 +316,7 @@ class VMHelper(HelperBase): LOG.debug(_("Asking xapi to fetch vhd image %(image)s") % locals()) - sr_ref = find_sr(session) - if sr_ref is None: - raise exception.NotFound('Cannot find SR to write VDI to') + sr_ref = safe_find_sr(session) # NOTE(sirp): The Glance plugin runs under Python 2.4 which does not # have the `uuid` module. To work around this, we generate the uuids @@ -340,16 +338,14 @@ class VMHelper(HelperBase): @classmethod def _fetch_image_glance_disk(cls, session, instance_id, image, access, type): - sr = find_sr(session) - if sr is None: - raise exception.NotFound('Cannot find SR to write VDI to') + sr_ref = safe_find_sr(session) client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) - meta, image_file = client.get_image(image) virtual_size = int(meta['size']) vdi_size = virtual_size LOG.debug(_("Size for image %(image)s:%(virtual_size)d") % locals()) + if type == ImageType.DISK: # Make room for MBR. vdi_size += MBR_SIZE_BYTES @@ -672,7 +668,18 @@ def get_vdi_for_vm_safely(session, vm_ref): return vdi_ref, vdi_rec +def safe_find_sr(session): + """Same as find_sr except raises a NotFound exception if SR cannot be + determined + """ + sr_ref = find_sr(session) + if sr_ref is None: + raise exception.NotFound(_('Cannot find SR to read/write VDI')) + return sr_ref + + def find_sr(session): + """Return the storage repository to hold VM images""" host = session.get_xenapi_host() srs = session.get_xenapi().SR.get_all() for sr in srs: @@ -688,15 +695,12 @@ def find_sr(session): def get_sr_path(session): - """Return the path to our Storage Repository + """Return the path to our storage repository This is used when we're dealing with VHDs directly, either by taking snapshots or by restoring an image in the DISK_VHD format. """ - # TODO(sirp): add safe_find_sr - sr_ref = find_sr(session) - if sr_ref is None: - raise Exception('Cannot find SR to read VDI from') + sr_ref = safe_find_sr(session) sr_rec = session.get_xenapi().SR.get_record(sr_ref) sr_uuid = sr_rec["uuid"] return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid) -- cgit From eb603b5ec3d54b2b6c893f8d41e7d12bbaa49e57 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 18:50:40 +0000 Subject: Refactoring put_vdis --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 136 +++++++++++---------- 1 file changed, 69 insertions(+), 67 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 0b270f5f9..3fe2d4059 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -67,16 +67,6 @@ def _copy_kernel_vdi(dest,copy_args): return filename -def assert_process_success(proc, cmd): - """Ensure that the process returned a zero exit code indicating success - """ - out, err = proc.communicate() - ret = proc.returncode - if ret != 0: - msg = "%(cmd)s returned non-zero exit code (%i): '%s'" % (ret, err) - raise Exception(msg) - - def get_vdi(session, args): """ """ @@ -109,7 +99,7 @@ def get_vdi(session, args): tar_proc.stdin.write(chunk) chunk = resp.read(CHUNK_SIZE) - assert_process_success(tar_proc, "tar") + _assert_process_success(tar_proc, "tar") conn.close() def fixup_vhds(sr_path, staging_path): @@ -136,7 +126,7 @@ def get_vdi(session, args): % locals()) modify_proc = subprocess.Popen( modify_args, stderr=subprocess.PIPE) - assert_process_success(modify_proc, "vhd-util") + _assert_process_success(modify_proc, "vhd-util") image_path = os.path.join(staging_path, 'image') @@ -161,13 +151,13 @@ def get_vdi(session, args): move_into_sr(base_copy_path) return vdi_uuid - staging_path = make_staging_area(sr_path) + staging_path = _make_staging_area(sr_path) try: unbundle_xfer(sr_path, staging_path) vdi_uuid = fixup_vhds(sr_path, staging_path) return vdi_uuid finally: - cleanup_staging_area(staging_path) + _cleanup_staging_area(staging_path) def put_vdis(session, args): @@ -180,67 +170,69 @@ def put_vdis(session, args): glance_port = params["glance_port"] sr_path = params["sr_path"] - def prepare_staging_area(sr_path, staging_path): - """ - Explain preparing staging area here... - """ - image_path = os.path.join(staging_path, 'image') - os.mkdir(image_path) - for name, uuid in vdi_uuids.items(): - source = os.path.join(sr_path, "%s.vhd" % uuid) - link_name = os.path.join(image_path, "%s.vhd" % name) - os.link(source, link_name) - - def bundle_xfer(staging_path): - conn = httplib.HTTPConnection(glance_host, glance_port) - #NOTE(sirp): httplib under python2.4 won't accept a file-like object - # to request - conn.putrequest('PUT', '/images/%s' % image_id) - - headers = { - 'x-image-meta-store': 'file', - 'x-image-meta-is_public': 'True', - 'x-image-meta-type': 'raw', - 'x-image-meta-property-disk-format': 'vhd', - 'x-image-meta-property-container-format': 'tarball', - 'transfer-encoding': "chunked", - 'content-type': 'application/octet-stream', - } - for header, value in headers.iteritems(): - conn.putheader(header, value) - conn.endheaders() + staging_path = _make_staging_area(sr_path) + try: + _prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids) + _bundle_xfer(staging_path, image_id, glance_host, glance_port) + finally: + _cleanup_staging_area(staging_path) - tar_args = shlex.split( - "tar -zc --directory=%(staging_path)s ." % locals()) - tar_proc = subprocess.Popen( - tar_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return "" - chunk = tar_proc.stdout.read(CHUNK_SIZE) - while chunk: - conn.send("%x\r\n%s\r\n" % (len(chunk), chunk)) - chunk = tar_proc.stdout.read(CHUNK_SIZE) - conn.send("0\r\n\r\n") - assert_process_success(tar_proc, "tar") +def _prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids): + """ + Explain preparing staging area here... + """ + image_path = os.path.join(staging_path, 'image') + os.mkdir(image_path) + for name, uuid in vdi_uuids.items(): + source = os.path.join(sr_path, "%s.vhd" % uuid) + link_name = os.path.join(image_path, "%s.vhd" % name) + os.link(source, link_name) - resp = conn.getresponse() - #FIXME(sirp): should this be 201 Created? - if resp.status != httplib.OK: - raise Exception("Unexpected response from Glance %i" % res.status) - conn.close() +def _bundle_xfer(staging_path, image_id, glance_host, glance_port): + """ + """ + conn = httplib.HTTPConnection(glance_host, glance_port) + #NOTE(sirp): httplib under python2.4 won't accept a file-like object + # to request + conn.putrequest('PUT', '/images/%s' % image_id) + + headers = { + 'x-image-meta-store': 'file', + 'x-image-meta-is_public': 'True', + 'x-image-meta-type': 'raw', + 'x-image-meta-property-disk-format': 'vhd', + 'x-image-meta-property-container-format': 'tarball', + 'transfer-encoding': "chunked", + 'content-type': 'application/octet-stream', + } + for header, value in headers.iteritems(): + conn.putheader(header, value) + conn.endheaders() + + tar_args = shlex.split( + "tar -zc --directory=%(staging_path)s ." % locals()) + tar_proc = subprocess.Popen( + tar_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + chunk = tar_proc.stdout.read(CHUNK_SIZE) + while chunk: + conn.send("%x\r\n%s\r\n" % (len(chunk), chunk)) + chunk = tar_proc.stdout.read(CHUNK_SIZE) + conn.send("0\r\n\r\n") - staging_path = make_staging_area(sr_path) - try: - prepare_staging_area(sr_path, staging_path) - bundle_xfer(staging_path) - finally: - cleanup_staging_area(staging_path) + _assert_process_success(tar_proc, "tar") - return "" # FIXME(sirp): return anything useful here? + resp = conn.getresponse() + if resp.status != httplib.OK: + raise Exception("Unexpected response from Glance %i" % res.status) + conn.close() -def make_staging_area(sr_path): +def _make_staging_area(sr_path): """ Explain staging area here... """ @@ -249,10 +241,20 @@ def make_staging_area(sr_path): return staging_path -def cleanup_staging_area(staging_path): +def _cleanup_staging_area(staging_path): shutil.rmtree(staging_path) +def _assert_process_success(proc, cmd): + """Ensure that the process returned a zero exit code indicating success + """ + out, err = proc.communicate() + ret = proc.returncode + if ret != 0: + msg = "%(cmd)s returned non-zero exit code (%i): '%s'" % (ret, err) + raise Exception(msg) + + if __name__ == '__main__': XenAPIPlugin.dispatch({'put_vdis': put_vdis, 'get_vdi': get_vdi, -- cgit From 0020f14f43aa6f024d9aab7dc67c79caaaeb8257 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Tue, 15 Feb 2011 11:15:59 -0800 Subject: zone/info works --- bin/nova-combined | 4 ++-- nova/api/openstack/__init__.py | 6 +++--- nova/api/openstack/zones.py | 7 ++++++- nova/flags.py | 5 +++++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/bin/nova-combined b/bin/nova-combined index 913c866bf..a0f552d64 100755 --- a/bin/nova-combined +++ b/bin/nova-combined @@ -53,11 +53,11 @@ if __name__ == '__main__': compute = service.Service.create(binary='nova-compute') network = service.Service.create(binary='nova-network') - volume = service.Service.create(binary='nova-volume') + #volume = service.Service.create(binary='nova-volume') scheduler = service.Service.create(binary='nova-scheduler') #objectstore = service.Service.create(binary='nova-objectstore') - service.serve(compute, network, volume, scheduler) + service.serve(compute, network, scheduler) apps = [] paste_config_file = wsgi.paste_config_file('nova-api.conf') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 33d040ab3..95fce7f84 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -76,13 +76,13 @@ class APIRouter(wsgi.Router): LOG.debug(_("Including admin operations in API.")) server_members['pause'] = 'POST' server_members['unpause'] = 'POST' - server_members["diagnostics"] = "GET" - server_members["actions"] = "GET" + server_members['diagnostics'] = 'GET' + server_members['actions'] = 'GET' server_members['suspend'] = 'POST' server_members['resume'] = 'POST' mapper.resource("zone", "zones", controller=zones.Controller(), - collection={'detail': 'GET'}) + collection={'detail': 'GET', 'info': 'GET'}), mapper.resource("server", "servers", controller=servers.Controller(), collection={'detail': 'GET'}, diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index 830464ffd..16e5e366b 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -42,7 +42,7 @@ class Controller(wsgi.Controller): _serialization_metadata = { 'application/xml': { "attributes": { - "zone": ["id", "api_url"]}}} + "zone": ["id", "api_url", "name", "capabilities"]}}} def index(self, req): """Return all zones in brief""" @@ -55,6 +55,11 @@ class Controller(wsgi.Controller): """Return all zones in detail""" return self.index(req) + def info(self, req): + """Return name and capabilities for this zone.""" + return dict(zone=dict(name=FLAGS.zone_name, + capabilities=FLAGS.zone_capabilities)) + def show(self, req, id): """Return data about the given zone id""" zone_id = int(id) diff --git a/nova/flags.py b/nova/flags.py index 3ba3fe6fa..0a45499f3 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -312,3 +312,8 @@ DEFINE_string('host', socket.gethostname(), DEFINE_string('node_availability_zone', 'nova', 'availability zone of this node') + +DEFINE_string('zone_name', 'nova', 'name of this zone') +DEFINE_string('zone_capabilities', 'xen, linux', + 'comma-delimited list of tags which represent boolean' + ' capabilities of this zone') -- cgit From acf95a640cfeb0812a55577b6a08bff972ad523b Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 19:17:27 +0000 Subject: Regrouping methods so they make sense --- nova/virt/xenapi/vm_utils.py | 7 +- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 233 +++++++++++---------- 2 files changed, 123 insertions(+), 117 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 37ce86885..ba3e5e423 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -289,7 +289,7 @@ class VMHelper(HelperBase): 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} - task = session.async_call_plugin('glance', 'put_vdis', kwargs) + task = session.async_call_plugin('glance', 'upload_image', kwargs) session.wait_for_task(instance_id, task) @classmethod @@ -310,7 +310,6 @@ class VMHelper(HelperBase): return cls._fetch_image_objectstore(session, instance_id, image, access, user.secret, type) - @classmethod def _fetch_image_glance_vhd(cls, session, instance_id, image, access, type): LOG.debug(_("Asking xapi to fetch vhd image %(image)s") @@ -330,10 +329,10 @@ class VMHelper(HelperBase): 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} - task = session.async_call_plugin('glance', 'get_vdi', kwargs) + task = session.async_call_plugin('glance', 'download_image', kwargs) vdi_uuid = session.wait_for_task(instance_id, task) scan_sr(session, instance_id, sr_ref) - LOG.debug(_("Xapi 'get_vdi' returned VDI UUID %(vdi_uuid)s") % locals()) + LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") % locals()) return vdi_uuid @classmethod diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 3fe2d4059..7bbab4f52 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -39,17 +39,6 @@ configure_logging('glance') CHUNK_SIZE = 8192 KERNEL_DIR = '/boot/guest' -def copy_kernel_vdi(session,args): - vdi = exists(args, 'vdi-ref') - size = exists(args,'image-size') - #Use the uuid as a filename - vdi_uuid=session.xenapi.VDI.get_uuid(vdi) - copy_args={'vdi_uuid':vdi_uuid,'vdi_size':int(size)} - filename=with_vdi_in_dom0(session, vdi, False, - lambda dev: - _copy_kernel_vdi('/dev/%s' % dev,copy_args)) - return filename - def _copy_kernel_vdi(dest,copy_args): vdi_uuid=copy_args['vdi_uuid'] vdi_size=copy_args['vdi_size'] @@ -67,117 +56,83 @@ def _copy_kernel_vdi(dest,copy_args): return filename -def get_vdi(session, args): - """ +def _download_tarball(sr_path, staging_path, image_id, glance_host, + glance_port): """ - params = pickle.loads(exists(args, 'params')) - image_id = params["image_id"] - glance_host = params["glance_host"] - glance_port = params["glance_port"] - uuid_stack = params["uuid_stack"] - sr_path = params["sr_path"] - - def unbundle_xfer(sr_path, staging_path): - """ - """ - conn = httplib.HTTPConnection(glance_host, glance_port) - conn.request('GET', '/images/%s' % image_id) - resp = conn.getresponse() - if resp.status == httplib.NOT_FOUND: - raise Exception("Image '%s' not found in Glance" % image_id) - elif resp.status != httplib.OK: - raise Exception("Unexpected response from Glance %i" % res.status) + """ + conn = httplib.HTTPConnection(glance_host, glance_port) + conn.request('GET', '/images/%s' % image_id) + resp = conn.getresponse() + if resp.status == httplib.NOT_FOUND: + raise Exception("Image '%s' not found in Glance" % image_id) + elif resp.status != httplib.OK: + raise Exception("Unexpected response from Glance %i" % res.status) - tar_args = shlex.split( - "tar -zx --directory=%(staging_path)s" % locals()) - tar_proc = subprocess.Popen( - tar_args, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + tar_args = shlex.split( + "tar -zx --directory=%(staging_path)s" % locals()) + tar_proc = subprocess.Popen( + tar_args, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + chunk = resp.read(CHUNK_SIZE) + while chunk: + tar_proc.stdin.write(chunk) chunk = resp.read(CHUNK_SIZE) - while chunk: - tar_proc.stdin.write(chunk) - chunk = resp.read(CHUNK_SIZE) - - _assert_process_success(tar_proc, "tar") - conn.close() - - def fixup_vhds(sr_path, staging_path): - """ - """ - def rename_with_uuid(orig_path): - """Generate a uuid and rename the file with the uuid""" - orig_dirname = os.path.dirname(orig_path) - uuid = uuid_stack.pop() - new_path = os.path.join(orig_dirname, "%s.vhd" % uuid) - os.rename(orig_path, new_path) - return new_path, uuid - - def move_into_sr(orig_path): - """Move a file into the SR""" - filename = os.path.basename(orig_path) - new_path = os.path.join(sr_path, filename) - os.rename(orig_path, new_path) - return new_path - - def link_vhds(child_path, parent_path): - modify_args = shlex.split( - "vhd-util modify -n %(child_path)s -p %(parent_path)s" - % locals()) - modify_proc = subprocess.Popen( - modify_args, stderr=subprocess.PIPE) - _assert_process_success(modify_proc, "vhd-util") - - - image_path = os.path.join(staging_path, 'image') - - orig_base_copy_path = os.path.join(image_path, 'image.vhd') - if not os.path.exists(orig_base_copy_path): - raise Exception("Invalid image: image.vhd not present") - - base_copy_path, base_copy_uuid = rename_with_uuid(orig_base_copy_path) - - vdi_uuid = base_copy_uuid - orig_snap_path = os.path.join(image_path, 'snap.vhd') - if os.path.exists(orig_snap_path): - snap_path, snap_uuid = rename_with_uuid(orig_snap_path) - vdi_uuid = snap_uuid - # NOTE(sirp): this step is necessary so that an SR scan won't - # delete the base_copy out from under us (since it would be - # orphaned) - link_vhds(snap_path, base_copy_path) - move_into_sr(snap_path) - - move_into_sr(base_copy_path) - return vdi_uuid - staging_path = _make_staging_area(sr_path) - try: - unbundle_xfer(sr_path, staging_path) - vdi_uuid = fixup_vhds(sr_path, staging_path) - return vdi_uuid - finally: - _cleanup_staging_area(staging_path) + _assert_process_success(tar_proc, "tar") + conn.close() -def put_vdis(session, args): +def fixup_vhds(sr_path, staging_path, uuid_stack): """ """ - params = pickle.loads(exists(args, 'params')) - vdi_uuids = params["vdi_uuids"] - image_id = params["image_id"] - glance_host = params["glance_host"] - glance_port = params["glance_port"] - sr_path = params["sr_path"] + def rename_with_uuid(orig_path): + """Generate a uuid and rename the file with the uuid""" + orig_dirname = os.path.dirname(orig_path) + uuid = uuid_stack.pop() + new_path = os.path.join(orig_dirname, "%s.vhd" % uuid) + os.rename(orig_path, new_path) + return new_path, uuid - staging_path = _make_staging_area(sr_path) - try: - _prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids) - _bundle_xfer(staging_path, image_id, glance_host, glance_port) - finally: - _cleanup_staging_area(staging_path) - return "" + def link_vhds(child_path, parent_path): + modify_args = shlex.split( + "vhd-util modify -n %(child_path)s -p %(parent_path)s" + % locals()) + modify_proc = subprocess.Popen( + modify_args, stderr=subprocess.PIPE) + _assert_process_success(modify_proc, "vhd-util") + + + def move_into_sr(orig_path): + """Move a file into the SR""" + filename = os.path.basename(orig_path) + new_path = os.path.join(sr_path, filename) + os.rename(orig_path, new_path) + return new_path + + + image_path = os.path.join(staging_path, 'image') + + orig_base_copy_path = os.path.join(image_path, 'image.vhd') + if not os.path.exists(orig_base_copy_path): + raise Exception("Invalid image: image.vhd not present") + + base_copy_path, base_copy_uuid = rename_with_uuid(orig_base_copy_path) + + vdi_uuid = base_copy_uuid + orig_snap_path = os.path.join(image_path, 'snap.vhd') + if os.path.exists(orig_snap_path): + snap_path, snap_uuid = rename_with_uuid(orig_snap_path) + vdi_uuid = snap_uuid + # NOTE(sirp): this step is necessary so that an SR scan won't + # delete the base_copy out from under us (since it would be + # orphaned) + link_vhds(snap_path, base_copy_path) + move_into_sr(snap_path) + + move_into_sr(base_copy_path) + return vdi_uuid def _prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids): @@ -192,7 +147,7 @@ def _prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids): os.link(source, link_name) -def _bundle_xfer(staging_path, image_id, glance_host, glance_port): +def _upload_tarball(staging_path, image_id, glance_host, glance_port): """ """ conn = httplib.HTTPConnection(glance_host, glance_port) @@ -255,7 +210,59 @@ def _assert_process_success(proc, cmd): raise Exception(msg) +def download_image(session, args): + """ + """ + params = pickle.loads(exists(args, 'params')) + image_id = params["image_id"] + glance_host = params["glance_host"] + glance_port = params["glance_port"] + uuid_stack = params["uuid_stack"] + sr_path = params["sr_path"] + + staging_path = _make_staging_area(sr_path) + try: + _download_tarball(sr_path, staging_path, image_id, glance_host, + glance_port) + vdi_uuid = fixup_vhds(sr_path, staging_path, uuid_stack) + return vdi_uuid + finally: + _cleanup_staging_area(staging_path) + + +def upload_image(session, args): + """ + """ + params = pickle.loads(exists(args, 'params')) + vdi_uuids = params["vdi_uuids"] + image_id = params["image_id"] + glance_host = params["glance_host"] + glance_port = params["glance_port"] + sr_path = params["sr_path"] + + staging_path = _make_staging_area(sr_path) + try: + _prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids) + _upload_tarball(staging_path, image_id, glance_host, glance_port) + finally: + _cleanup_staging_area(staging_path) + + return "" + + +def copy_kernel_vdi(session,args): + vdi = exists(args, 'vdi-ref') + size = exists(args,'image-size') + #Use the uuid as a filename + vdi_uuid=session.xenapi.VDI.get_uuid(vdi) + copy_args={'vdi_uuid':vdi_uuid,'vdi_size':int(size)} + filename=with_vdi_in_dom0(session, vdi, False, + lambda dev: + _copy_kernel_vdi('/dev/%s' % dev,copy_args)) + return filename + + if __name__ == '__main__': - XenAPIPlugin.dispatch({'put_vdis': put_vdis, - 'get_vdi': get_vdi, + XenAPIPlugin.dispatch({'upload_image': upload_image, + 'download_image': download_image, 'copy_kernel_vdi': copy_kernel_vdi}) -- cgit From 300657f298fbecf9a08792b6d15e462560a6cdf5 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 19:51:23 +0000 Subject: Adding documentation --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 85 +++++++++++++++++++--- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 7bbab4f52..dac773ff1 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -58,8 +58,8 @@ def _copy_kernel_vdi(dest,copy_args): def _download_tarball(sr_path, staging_path, image_id, glance_host, glance_port): - """ - + """Download the tarball image from Glance and extract it into the staging + area. """ conn = httplib.HTTPConnection(glance_host, glance_port) conn.request('GET', '/images/%s' % image_id) @@ -84,10 +84,34 @@ def _download_tarball(sr_path, staging_path, image_id, glance_host, def fixup_vhds(sr_path, staging_path, uuid_stack): - """ + """Fixup the downloaded VHDs before we move them into the SR. + + We cannot extract VHDs directly into the SR since they don't yet have + UUIDs, aren't properly associated with each other, and would be subject to + a race-condition of one-file being present and the other not being + downloaded yet. + + To avoid these we problems, we use a staging area to fixup the VHDs before + moving them into the SR. The steps involved are: + + 1. Extracting tarball into staging area + + 2. Renaming VHDs to use UUIDs ('snap.vhd' -> 'ffff-aaaa-...vhd') + + 3. Linking the two VHDs together + + 4. Pseudo-atomically moving the images into the SR. (It's not really + atomic because it takes place as two os.rename operations; however, the + chances of an SR.scan occuring between the two rename() invocations is so + small that we can safely ignore it) """ def rename_with_uuid(orig_path): - """Generate a uuid and rename the file with the uuid""" + """Rename VHD using UUID so that it will be recognized by SR on a + subsequent scan. + + Since Python2.4 doesn't have the `uuid` module, we pass a stack of + pre-computed UUIDs from the compute worker. + """ orig_dirname = os.path.dirname(orig_path) uuid = uuid_stack.pop() new_path = os.path.join(orig_dirname, "%s.vhd" % uuid) @@ -96,6 +120,11 @@ def fixup_vhds(sr_path, staging_path, uuid_stack): def link_vhds(child_path, parent_path): + """Use vhd-util to associate the snapshot VHD with its base_copy. + + This needs to be done before we move both VHDs into the SR to prevent + the base_copy from being DOA (deleted-on-arrival). + """ modify_args = shlex.split( "vhd-util modify -n %(child_path)s -p %(parent_path)s" % locals()) @@ -136,8 +165,8 @@ def fixup_vhds(sr_path, staging_path, uuid_stack): def _prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids): - """ - Explain preparing staging area here... + """Hard-link VHDs into staging area with appropriate filename + ('snap' or 'image.vhd') """ image_path = os.path.join(staging_path, 'image') os.mkdir(image_path) @@ -149,6 +178,8 @@ def _prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids): def _upload_tarball(staging_path, image_id, glance_host, glance_port): """ + Create a tarball of the image and then stream that into Glance + using chunked-transfer-encoded HTTP. """ conn = httplib.HTTPConnection(glance_host, glance_port) #NOTE(sirp): httplib under python2.4 won't accept a file-like object @@ -189,7 +220,36 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): def _make_staging_area(sr_path): """ - Explain staging area here... + The staging area is a place where we can temporarily store and + manipulate VHDs. The use of the staging area is different for upload and + download: + + Download + ======== + + When we download the tarball, the VHDs contained within will have names + like "snap.vhd" and "image.vhd". We need to assign UUIDs to them before + moving them into the SR. However, since 'image.vhd' may be a base_copy, we + need to link it to 'snap.vhd' (using vhd-util modify) before moving both + into the SR (otherwise the SR.scan will cause 'image.vhd' to be deleted). + The staging area gives us a place to perform these operations before they + are moved to the SR, scanned, and then registered with XenServer. + + Upload + ====== + + On upload, we want to rename the VHDs to reflect what they are, 'snap.vhd' + in the case of the snapshot VHD, and 'image.vhd' in the case of the + base_copy. The staging area provides a directory in which we can create + hard-links to rename the VHDs without affecting what's in the SR. + + + NOTE + ==== + + The staging area is created as a subdirectory within the SR in order to + guarantee that it resides within the same filesystem and therefore permit + hard-linking and cheap file moves. """ # NOTE(sirp): staging area is created in SR to allow hard-linking staging_path = tempfile.mkdtemp(dir=sr_path) @@ -197,6 +257,12 @@ def _make_staging_area(sr_path): def _cleanup_staging_area(staging_path): + """Remove staging area directory + + On upload, the staging area contains hard-links to the VHDs in the SR; + it's safe to remove the staging-area because the SR will keep the link + count > 0 (so the VHDs in the SR will not be deleted). + """ shutil.rmtree(staging_path) @@ -211,7 +277,8 @@ def _assert_process_success(proc, cmd): def download_image(session, args): - """ + """Download an image from Glance, unbundle it, and then deposit the VHDs + into the storage repository """ params = pickle.loads(exists(args, 'params')) image_id = params["image_id"] @@ -231,7 +298,7 @@ def download_image(session, args): def upload_image(session, args): - """ + """Bundle the VHDs comprising an image and then stream them into Glance. """ params = pickle.loads(exists(args, 'params')) vdi_uuids = params["vdi_uuids"] -- cgit From c97d408842a4a5a8e9d379acc13c9c1f5871827f Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 14:10:43 -0600 Subject: Plugin changes --- nova/virt/xenapi/vmops.py | 11 +++++++---- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 12 +++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 127a09ad1..882d52f38 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -267,16 +267,18 @@ class VMOps(object): params = {'host': dest, 'vdi_uuid': base_copy_uuid, 'instance_id': instance.id, } - self._session.async_call_plugin('migration', 'transfer_vhd', + task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) + self._session.wait_for_task(instance.id, task) # Now power down the instance and transfer the COW VHD self._shutdown(instance, vm_ref, method='clean') params = {'host': dest, 'vdi_uuid': cow_uuid, 'instance_id': instance.id, } - self._session.async_call_plugin('migration', 'transfer_vhd', + task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) + self._session.wait_for_task(instance.id, task) # TODO(mdietz): we could also consider renaming these to something # sensible so we don't need to blindly pass around dictionaries @@ -291,8 +293,9 @@ class VMOps(object): 'new_base_copy_uuid': new_base_copy_uuid, 'new_cow_uuid': str(uuid.uuid4())} - self._session.async_call_plugin('migration', 'move_vhds_into_sr', - {'params': pickle.dumps(params)}) + task = self._session.async_call_plugin('migration', + 'move_vhds_into_sr', {'params': pickle.dumps(params)}) + self._session.wait_for_task(instance.id, task) # Now we rescan the SR so we find the VHDs VMHelper.scan_sr(self._session) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 97c970da5..4a4ed0e73 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -129,17 +129,19 @@ def transfer_vhd(session, args): logging.debug("Preparing to transmit %s to %s" % (source_path, dest_path)) - ssh_cmd = '-e "ssh -o StrictHostKeyChecking=no " ' + ssh_cmd = '-e \'ssh -o StrictHostKeyChecking=no\' ' - rsync_args = ['nohup', RSYNC, '-av', '--progress', ssh_cmd, source_path, - dest_path] + rsync_args = ['nohup > /root/instance%d_progress' % instance_id, RSYNC, + '-av', '--progress', ssh_cmd, source_path, dest_path] logging.debug('rsync %s' % (' '.join(rsync_args, ))) - rsync_proc = subprocess.Popen(rsync_args) + rsync_proc = subprocess.Popen(rsync_args, stdout=subprocess.PIPE) logging.debug('Rsync output: \n %s' % rsync_proc.communicate()[0]) - if rsync_proc.returncode != 0 + logging.debug('Rsync return: %d' % rsync_proc.returncode) + if rsync_proc.returncode != 0: raise Exception("Unexpected VHD transfer failure") + return "" if __name__ == '__main__': -- cgit From 00f2905a5debc5835b742dab8dce003f53e33fc2 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 14:29:31 -0600 Subject: plugin lol --- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 4a4ed0e73..bfc2a2ed4 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -89,7 +89,7 @@ def move_vhds_into_sr(session, args): new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid) new_cow_path = "%s/%s.vhd" % (temp_vhd_path, new_cow_uuid) - logging.debug('Creating temporary SR path' % temp_vhd_path) + logging.debug('Creating temporary SR path %s' % temp_vhd_path) os.mkdir(temp_vhd_path) logging.debug('Moving %s into %s' % (source_base_copy_path, temp_vhd_path)) @@ -129,10 +129,10 @@ def transfer_vhd(session, args): logging.debug("Preparing to transmit %s to %s" % (source_path, dest_path)) - ssh_cmd = '-e \'ssh -o StrictHostKeyChecking=no\' ' + ssh_cmd = 'ssh -o StrictHostKeyChecking=no' - rsync_args = ['nohup > /root/instance%d_progress' % instance_id, RSYNC, - '-av', '--progress', ssh_cmd, source_path, dest_path] + rsync_args = ['nohup', RSYNC, '-av', '--progress', '-e', ssh_cmd, + source_path, dest_path] logging.debug('rsync %s' % (' '.join(rsync_args, ))) -- cgit From cbe4ee9f3a6bf207d475ca230032ced9325c3b7a Mon Sep 17 00:00:00 2001 From: brian-lamar Date: Tue, 15 Feb 2011 15:33:17 -0500 Subject: Added teammate Naveed to authors file for his help. --- Authors | 1 + 1 file changed, 1 insertion(+) diff --git a/Authors b/Authors index e8bb58456..03f30084d 100644 --- a/Authors +++ b/Authors @@ -43,6 +43,7 @@ Monty Taylor MORITA Kazutaka Muneyuki Noguchi Nachi Ueno +Naveed Massjouni Paul Voccio Ricardo Carrillo Cruz Rick Clark -- cgit From 745b7b22f7b22a09e6c3bbc1cd8591eb3aa7f554 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Tue, 15 Feb 2011 21:38:47 +0100 Subject: removed flag --pidfile from nova/services.py --- nova/service.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/nova/service.py b/nova/service.py index 59648adf2..8b1b91e90 100644 --- a/nova/service.py +++ b/nova/service.py @@ -50,10 +50,6 @@ flags.DEFINE_integer('periodic_interval', 60, 'seconds between running periodic tasks', lower_bound=1) -flags.DEFINE_string('pidfile', None, - 'pidfile to use for this service') - - flags.DEFINE_flag(flags.HelpFlag()) flags.DEFINE_flag(flags.HelpshortFlag()) flags.DEFINE_flag(flags.HelpXMLFlag()) -- cgit From 93df4098bf4a2b1f161eb5e6675d3a4fd820d1ed Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 15 Feb 2011 22:13:08 +0100 Subject: Don't hid RotatingFileHandler behind FileHandler's name. --- nova/log.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/log.py b/nova/log.py index 413dd946b..82a13f6a8 100644 --- a/nova/log.py +++ b/nova/log.py @@ -92,7 +92,7 @@ critical = logging.critical log = logging.log # handlers StreamHandler = logging.StreamHandler -FileHandler = logging.RotatingFileHandler +RotatingFileHandler = logging.RotatingFileHandler # logging.SysLogHandler is nicer than logging.logging.handler.SysLogHandler. SysLogHandler = logging.handlers.SysLogHandler @@ -124,7 +124,7 @@ def basicConfig(): syslog.setFormatter(_formatter) logging.root.addHandler(syslog) if FLAGS.logfile: - logfile = FileHandler(FLAGS.logfile) + logfile = RotatingFileHandler(FLAGS.logfile) logfile.setFormatter(_formatter) logging.root.addHandler(logfile) -- cgit From 503749849df73df1732583bc9452e7952bf78ac2 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Tue, 15 Feb 2011 15:25:48 -0600 Subject: moved reset network to after boot durrrrr... --- nova/virt/xenapi/vmops.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 00028cdaa..dd9f48ddf 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -134,9 +134,6 @@ class VMOps(object): VMHelper.create_vif(self._session, vm_ref, network_ref, instance.mac_address) - # call reset networking - self.reset_network(instance) - LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) instance_name = instance.name @@ -164,6 +161,10 @@ class VMOps(object): timer.stop() timer.f = _wait_for_boot + + # call reset networking + self.reset_network(instance) + return timer.start(interval=0.5, now=True) def _get_vm_opaque_ref(self, instance_or_vm): -- cgit From 9f77e0a46cac2ebaf9a18c4a175099b208db1adb Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 15:27:23 -0600 Subject: More plugin lol --- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index bfc2a2ed4..cc72b5d26 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -90,7 +90,7 @@ def move_vhds_into_sr(session, args): new_cow_path = "%s/%s.vhd" % (temp_vhd_path, new_cow_uuid) logging.debug('Creating temporary SR path %s' % temp_vhd_path) - os.mkdir(temp_vhd_path) + os.makedirs(temp_vhd_path) logging.debug('Moving %s into %s' % (source_base_copy_path, temp_vhd_path)) shutil.move(source_base_copy_path, new_base_copy_path) @@ -107,11 +107,12 @@ def move_vhds_into_sr(session, args): new_base_copy_path]) logging.debug('Moving VHDs into SR %s' % sr_path) - shutil.move("%s/*.vhd" % temp_vhd_path, sr_path) + shutil.move("%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid), sr_path) + shutil.move("%s/%s.vhd" % (temp_vhd_path, new_cow_uuid), sr_path) - loggin.debug('Cleaning up temporary SR path %s' % temp_vhd_path) + logginig.debug('Cleaning up temporary SR path %s' % temp_vhd_path) os.rmdir(temp_vhd_path) - return None + return "" def transfer_vhd(session, args): -- cgit From 9b4150ad66abcdeb5dd46927fa320f9f2c6c99f6 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Tue, 15 Feb 2011 15:30:44 -0600 Subject: changed d to s --- locale/nova.pot | 2 +- nova/virt/xenapi/vm_utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/locale/nova.pot b/locale/nova.pot index a96411e33..53e38c619 100644 --- a/locale/nova.pot +++ b/locale/nova.pot @@ -1826,7 +1826,7 @@ msgstr "" #: nova/virt/xenapi/vm_utils.py:290 #, python-format -msgid "PV Kernel in VDI:%d" +msgid "PV Kernel in VDI:%s" msgstr "" #: nova/virt/xenapi/vm_utils.py:318 diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 574ef0944..80cc3035d 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -394,7 +394,7 @@ class VMHelper(HelperBase): pv = True elif pv_str.lower() == 'false': pv = False - LOG.debug(_("PV Kernel in VDI:%d"), pv) + LOG.debug(_("PV Kernel in VDI:%s"), pv) return pv @classmethod -- cgit From 1d72b9d3ddc835d788ba1fec1a937c2788e94b38 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 21:36:13 +0000 Subject: Using Nova style nokernel --- nova/api/openstack/servers.py | 8 +------- nova/compute/api.py | 2 ++ plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 9 ++++++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 1c9dcd9a6..cd0cea235 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -138,13 +138,7 @@ class Controller(wsgi.Controller): _("%(param)s property not found for image %(_image_id)s") % locals()) - image_id = str(image_id) - image = self._image_service.show(req.environ['nova.context'], image_id) - disk_format = image['properties'].get('disk_format', None) - if disk_format == "vhd": - return None, None - else: - return lookup('kernel_id'), lookup('ramdisk_id') + return lookup('kernel_id'), lookup('ramdisk_id') def create(self, req): """ Creates a new server for a given user """ diff --git a/nova/compute/api.py b/nova/compute/api.py index ac02dbcfa..6d28e376c 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -106,6 +106,8 @@ class API(base.Base): kernel_id = image.get('kernelId', None) if ramdisk_id is None: ramdisk_id = image.get('ramdiskId', None) + + # FIXME(sirp): is there a way we can remove null_kernel? # No kernel and ramdisk for raw images if kernel_id == str(FLAGS.null_kernel): kernel_id = None diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index dac773ff1..75bdda2c6 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -182,15 +182,19 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): using chunked-transfer-encoded HTTP. """ conn = httplib.HTTPConnection(glance_host, glance_port) - #NOTE(sirp): httplib under python2.4 won't accept a file-like object + # NOTE(sirp): httplib under python2.4 won't accept a file-like object # to request conn.putrequest('PUT', '/images/%s' % image_id) + # FIXME(sirp): nokernel signals Nova to use a raw image. This is defined by + # FLAGS.null_kernel. Is there a way to get rid of this? headers = { 'x-image-meta-store': 'file', 'x-image-meta-is_public': 'True', 'x-image-meta-type': 'raw', 'x-image-meta-property-disk-format': 'vhd', + 'x-image-meta-property-kernel-id': 'nokernel', + 'x-image-meta-property-ramdisk-id': 'noramdisk', 'x-image-meta-property-container-format': 'tarball', 'transfer-encoding': "chunked", 'content-type': 'application/octet-stream', @@ -251,7 +255,6 @@ def _make_staging_area(sr_path): guarantee that it resides within the same filesystem and therefore permit hard-linking and cheap file moves. """ - # NOTE(sirp): staging area is created in SR to allow hard-linking staging_path = tempfile.mkdtemp(dir=sr_path) return staging_path @@ -314,7 +317,7 @@ def upload_image(session, args): finally: _cleanup_staging_area(staging_path) - return "" + return "" # Nothing useful to return on an upload def copy_kernel_vdi(session,args): -- cgit From 5afc1f05d303cd58fb0bf94e5d35e5bc28b9d75c Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Tue, 15 Feb 2011 13:40:54 -0800 Subject: polling working --- nova/scheduler/manager.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index e9b47512e..00cab60cf 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -22,6 +22,9 @@ Scheduler Service """ import functools +import novatools + +from datetime import datetime from nova import db from nova import flags @@ -35,20 +38,72 @@ FLAGS = flags.FLAGS flags.DEFINE_string('scheduler_driver', 'nova.scheduler.chance.ChanceScheduler', 'Driver to use for the scheduler') +flags.DEFINE_integer('zone_db_check_interval', + 60, + 'Seconds between getting fresh zone info from db.') + + +class ZoneState(object): + """Holds the state of all connected child zones.""" + def __init__(self): + self.is_active = True + self.name = None + self.capabilities = None + self.retry = 0 + self.last_seen = datetime.min + + def update(self, zone): + self.zone_id = zone.id + self.api_url = zone.api_url + self.username = zone.username + self.password = zone.password +class ZoneManager(object): + """Keeps the zone states updated.""" + def __init__(self): + self.last_zone_db_check = datetime.min + self.zone_states = {} + + def _refresh_from_db(self, context): + zones = db.zone_get_all(context) + existing = self.zone_states.keys() + for zone in zones: + if zone.id not in existing: + self.zone_state[zone.id] = ZoneState() + self.zone_state[zones.id].update(zone) + + def _poll_zones(self, context): + pass + + def ping(self, context=None): + """Ping should be called periodically to update zone status.""" + logging.debug("ZoneManager PING") + diff = datetime.now() - self.last_zone_db_check + if diff.seconds >= FLAGS.zone_db_check_interval: + logging.debug("ZoneManager RECHECKING DB ") + self.last_zone_db_check = datetime.now() + self._refresh_from_db(context) + self._poll_zones(context) + + class SchedulerManager(manager.Manager): """Chooses a host to run instances on.""" def __init__(self, scheduler_driver=None, *args, **kwargs): if not scheduler_driver: scheduler_driver = FLAGS.scheduler_driver self.driver = utils.import_object(scheduler_driver) + self.zone_manager = ZoneManager() super(SchedulerManager, self).__init__(*args, **kwargs) def __getattr__(self, key): """Converts all method calls to use the schedule method""" return functools.partial(self._schedule, key) + def periodic_tasks(self, context=None): + """Poll child zones periodically to get status.""" + self.zone_manager.ping(context) + def _schedule(self, method, context, topic, *args, **kwargs): """Tries to call schedule_* method on the driver to retrieve host. -- cgit From c33378fbbe0fd76e807530522715ba4175af18d8 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 21:54:42 +0000 Subject: Fixing typo --- nova/api/openstack/servers.py | 11 ++++++++--- nova/virt/xenapi/vm_utils.py | 14 +++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index cd0cea235..c15e499a0 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -129,8 +129,12 @@ class Controller(wsgi.Controller): Machine images are associated with Kernels and Ramdisk images via metadata stored in Glance as 'image_properties' """ - def lookup(param): - _image_id = image_id + # FIXME(sirp): Currently Nova requires us to specify the `null_kernel` + # identifier ('nokernel') to indicate a RAW (or VHD) image. It would + # be better if we could omit the kernel_id and ramdisk_id properties + # on the image + def lookup(image, param): + _image_id = image.id try: return image['properties'][param] except KeyError: @@ -138,7 +142,8 @@ class Controller(wsgi.Controller): _("%(param)s property not found for image %(_image_id)s") % locals()) - return lookup('kernel_id'), lookup('ramdisk_id') + image = self._image_service.show(req.environ['nova.context'], image_id) + return lookup(image, 'kernel_id'), lookup(image, 'ramdisk_id') def create(self, req): """ Creates a new server for a given user """ diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index ba3e5e423..6fa03ee68 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -331,12 +331,24 @@ class VMHelper(HelperBase): kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'download_image', kwargs) vdi_uuid = session.wait_for_task(instance_id, task) + # TODO(sirp): set name-label on VDI scan_sr(session, instance_id, sr_ref) LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") % locals()) return vdi_uuid @classmethod def _fetch_image_glance_disk(cls, session, instance_id, image, access, type): + """Fetch the image from Glance + + NOTE: + Unlike _fetch_image_glance_vhd, this method does not use the Glance + plugin; instead, it streams the disks through domU to the VDI + directly. + + """ + # FIXME(sirp): Since the Glance plugin seems to be required for the VHD disk, + # it may be worth using the plugin for both VHD and RAW and DISK + # restores sr_ref = safe_find_sr(session) client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) @@ -349,7 +361,7 @@ class VMHelper(HelperBase): # Make room for MBR. vdi_size += MBR_SIZE_BYTES - vdi = cls.create_vdi(session, sr, _('Glance image %s') % image, + vdi = cls.create_vdi(session, sr_ref, _('Glance image %s') % image, vdi_size, False) with_vdi_attached_here(session, vdi, False, -- cgit From 52a443dfb53e8fa2226e7ae8b8dac0fa6e32a69d Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 15 Feb 2011 23:11:51 +0100 Subject: Refactor code that decides which logfile to use, if any. Adds unit tests. --- nova/log.py | 17 +++++++++++------ nova/tests/test_log.py | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/nova/log.py b/nova/log.py index a5b4828d5..ec6681edd 100644 --- a/nova/log.py +++ b/nova/log.py @@ -112,6 +112,15 @@ def _dictify_context(context): context = context.to_dict() return context +def _get_binary_name(): + return os.path.basename(inspect.stack()[-1][1]) + +def get_log_file_path(binary=None): + if FLAGS.logfile: + return FLAGS.logfile + if FLAGS.logdir: + binary = binary or _get_binary_name() + return '%s.log' % (os.path.join(FLAGS.logdir, binary),) def basicConfig(): logging.basicConfig() @@ -125,12 +134,8 @@ def basicConfig(): syslog = SysLogHandler(address='/dev/log') syslog.setFormatter(_formatter) logging.root.addHandler(syslog) - if FLAGS.logfile or FLAGS.logdir: - if FLAGS.logfile: - logfile = FLAGS.logfile - else: - binary = os.path.basename(inspect.stack()[-1][1]) - logpath = '%s.log' % (os.path.join(FLAGS.logdir, binary),) + logpath = get_log_file_path() + if logpath: logfile = FileHandler(logpath) logfile.setFormatter(_formatter) logging.root.addHandler(logfile) diff --git a/nova/tests/test_log.py b/nova/tests/test_log.py index 868a5ead3..7a5c52935 100644 --- a/nova/tests/test_log.py +++ b/nova/tests/test_log.py @@ -46,6 +46,27 @@ class RootLoggerTestCase(test.TestCase): self.assert_(True) # didn't raise exception +class LogHandlerTestCase(test.TestCase): + def test_log_path_logdir(self): + self.flags(logdir='/some/path') + self.assertEquals(log.get_log_file_path(binary='foo-bar'), + '/some/path/foo-bar.log') + + def test_log_path_logfile(self): + self.flags(logfile='/some/path/foo-bar.log') + self.assertEquals(log.get_log_file_path(binary='foo-bar'), + '/some/path/foo-bar.log') + + def test_log_path_none(self): + self.assertIsNone(log.get_log_file_path(binary='foo-bar')) + + def test_log_path_logfile_overrides_logdir(self): + self.flags(logdir='/some/other/path', + logfile='/some/path/foo-bar.log') + self.assertEquals(log.get_log_file_path(binary='foo-bar'), + '/some/path/foo-bar.log') + + class NovaFormatterTestCase(test.TestCase): def setUp(self): super(NovaFormatterTestCase, self).setUp() -- cgit From 297fdbfead55017340150412cd00b3aa7b962257 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 15 Feb 2011 23:13:25 +0100 Subject: Don't hide RotatingFileHandler behind FileHandler's name. --- nova/log.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/log.py b/nova/log.py index 413dd946b..32b6d4629 100644 --- a/nova/log.py +++ b/nova/log.py @@ -92,7 +92,7 @@ critical = logging.critical log = logging.log # handlers StreamHandler = logging.StreamHandler -FileHandler = logging.RotatingFileHandler +RotatingFileHandler = logging.handlers.RotatingFileHandler # logging.SysLogHandler is nicer than logging.logging.handler.SysLogHandler. SysLogHandler = logging.handlers.SysLogHandler @@ -124,7 +124,7 @@ def basicConfig(): syslog.setFormatter(_formatter) logging.root.addHandler(syslog) if FLAGS.logfile: - logfile = FileHandler(FLAGS.logfile) + logfile = RotatingFileHandler(FLAGS.logfile) logfile.setFormatter(_formatter) logging.root.addHandler(logfile) -- cgit From 24d988263324c9136a1cd9aa5a3a3c4fdf229651 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 22:19:58 +0000 Subject: Set name-label on VDI --- nova/virt/xenapi/vm_utils.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 6fa03ee68..738372da0 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -331,8 +331,14 @@ class VMHelper(HelperBase): kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'download_image', kwargs) vdi_uuid = session.wait_for_task(instance_id, task) - # TODO(sirp): set name-label on VDI + scan_sr(session, instance_id, sr_ref) + + # Set the name-label to ease debugging + vdi_ref = session.get_xenapi().VDI.get_by_uuid(vdi_uuid) + name_label = get_name_label_for_image(image) + session.get_xenapi().VDI.set_name_label(vdi_ref, name_label) + LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") % locals()) return vdi_uuid @@ -361,8 +367,8 @@ class VMHelper(HelperBase): # Make room for MBR. vdi_size += MBR_SIZE_BYTES - vdi = cls.create_vdi(session, sr_ref, _('Glance image %s') % image, - vdi_size, False) + name_label = get_name_label_for_image(image) + vdi = cls.create_vdi(session, sr_ref, name_label, vdi_size, False) with_vdi_attached_here(session, vdi, False, lambda dev: @@ -848,3 +854,8 @@ def _write_partition(virtual_size, dev): (dest, primary_first, primary_last)) LOG.debug(_('Writing partition table %s done.'), dest) + + +def get_name_label_for_image(image): + # TODO(sirp): This should eventually be the URI for the Glance image + return _('Glance image %s') % image -- cgit From 28ba475d9c32a384570ce6eb0e2f9cfc3dc79a08 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 22:24:48 +0000 Subject: Pep8 fixes --- nova/virt/xenapi/vm_utils.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 738372da0..93009b408 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -311,7 +311,8 @@ class VMHelper(HelperBase): access, user.secret, type) @classmethod - def _fetch_image_glance_vhd(cls, session, instance_id, image, access, type): + def _fetch_image_glance_vhd(cls, session, instance_id, image, access, + type): LOG.debug(_("Asking xapi to fetch vhd image %(image)s") % locals()) @@ -339,11 +340,13 @@ class VMHelper(HelperBase): name_label = get_name_label_for_image(image) session.get_xenapi().VDI.set_name_label(vdi_ref, name_label) - LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") % locals()) + LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") + % locals()) return vdi_uuid @classmethod - def _fetch_image_glance_disk(cls, session, instance_id, image, access, type): + def _fetch_image_glance_disk(cls, session, instance_id, image, access, + type): """Fetch the image from Glance NOTE: @@ -352,9 +355,9 @@ class VMHelper(HelperBase): directly. """ - # FIXME(sirp): Since the Glance plugin seems to be required for the VHD disk, - # it may be worth using the plugin for both VHD and RAW and DISK - # restores + # FIXME(sirp): Since the Glance plugin seems to be required for the + # VHD disk, it may be worth using the plugin for both VHD and RAW and + # DISK restores sr_ref = safe_find_sr(session) client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) @@ -429,12 +432,13 @@ class VMHelper(HelperBase): properties = meta['properties'] disk_format = properties.get('disk_format', None) # TODO(sirp): When Glance treats disk_format as a first class - # attribute, we should start using that rather than an image-property + # attribute, we should start using that rather than an + # image-property if disk_format == 'vhd': # 2a. DISK_VHD log_disk_format('disk_vhd') return ImageType.DISK_VHD - + # 2b. DISK_RAW log_disk_format('disk_raw') return ImageType.DISK_RAW -- cgit From c7cd7b755c86bd15e2b19f70a09f88f62361596c Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 22:31:51 +0000 Subject: More pep8 fixes --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 51 +++++++++++----------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 75bdda2c6..43c58dcff 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -39,20 +39,22 @@ configure_logging('glance') CHUNK_SIZE = 8192 KERNEL_DIR = '/boot/guest' -def _copy_kernel_vdi(dest,copy_args): - vdi_uuid=copy_args['vdi_uuid'] - vdi_size=copy_args['vdi_size'] - logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s",dest,vdi_uuid) - filename=KERNEL_DIR + '/' + vdi_uuid + +def _copy_kernel_vdi(dest, copy_args): + vdi_uuid = copy_args['vdi_uuid'] + vdi_size = copy_args['vdi_size'] + logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s", + dest, vdi_uuid) + filename = KERNEL_DIR + '/' + vdi_uuid #read data from /dev/ and write into a file on /boot/guest - of=open(filename,'wb') - f=open(dest,'rb') + of = open(filename, 'wb') + f = open(dest, 'rb') #copy only vdi_size bytes - data=f.read(vdi_size) + data = f.read(vdi_size) of.write(data) f.close() of.close() - logging.debug("Done. Filename: %s",filename) + logging.debug("Done. Filename: %s", filename) return filename @@ -101,9 +103,9 @@ def fixup_vhds(sr_path, staging_path, uuid_stack): 3. Linking the two VHDs together 4. Pseudo-atomically moving the images into the SR. (It's not really - atomic because it takes place as two os.rename operations; however, the - chances of an SR.scan occuring between the two rename() invocations is so - small that we can safely ignore it) + atomic because it takes place as two os.rename operations; however, + the chances of an SR.scan occuring between the two rename() + invocations is so small that we can safely ignore it) """ def rename_with_uuid(orig_path): """Rename VHD using UUID so that it will be recognized by SR on a @@ -118,7 +120,6 @@ def fixup_vhds(sr_path, staging_path, uuid_stack): os.rename(orig_path, new_path) return new_path, uuid - def link_vhds(child_path, parent_path): """Use vhd-util to associate the snapshot VHD with its base_copy. @@ -132,7 +133,6 @@ def fixup_vhds(sr_path, staging_path, uuid_stack): modify_args, stderr=subprocess.PIPE) _assert_process_success(modify_proc, "vhd-util") - def move_into_sr(orig_path): """Move a file into the SR""" filename = os.path.basename(orig_path) @@ -140,7 +140,6 @@ def fixup_vhds(sr_path, staging_path, uuid_stack): os.rename(orig_path, new_path) return new_path - image_path = os.path.join(staging_path, 'image') orig_base_copy_path = os.path.join(image_path, 'image.vhd') @@ -157,7 +156,7 @@ def fixup_vhds(sr_path, staging_path, uuid_stack): # NOTE(sirp): this step is necessary so that an SR scan won't # delete the base_copy out from under us (since it would be # orphaned) - link_vhds(snap_path, base_copy_path) + link_vhds(snap_path, base_copy_path) move_into_sr(snap_path) move_into_sr(base_copy_path) @@ -227,7 +226,7 @@ def _make_staging_area(sr_path): The staging area is a place where we can temporarily store and manipulate VHDs. The use of the staging area is different for upload and download: - + Download ======== @@ -241,7 +240,7 @@ def _make_staging_area(sr_path): Upload ====== - + On upload, we want to rename the VHDs to reflect what they are, 'snap.vhd' in the case of the snapshot VHD, and 'image.vhd' in the case of the base_copy. The staging area provides a directory in which we can create @@ -264,7 +263,7 @@ def _cleanup_staging_area(staging_path): On upload, the staging area contains hard-links to the VHDs in the SR; it's safe to remove the staging-area because the SR will keep the link - count > 0 (so the VHDs in the SR will not be deleted). + count > 0 (so the VHDs in the SR will not be deleted). """ shutil.rmtree(staging_path) @@ -320,15 +319,15 @@ def upload_image(session, args): return "" # Nothing useful to return on an upload -def copy_kernel_vdi(session,args): +def copy_kernel_vdi(session, args): vdi = exists(args, 'vdi-ref') - size = exists(args,'image-size') + size = exists(args, 'image-size') #Use the uuid as a filename - vdi_uuid=session.xenapi.VDI.get_uuid(vdi) - copy_args={'vdi_uuid':vdi_uuid,'vdi_size':int(size)} - filename=with_vdi_in_dom0(session, vdi, False, - lambda dev: - _copy_kernel_vdi('/dev/%s' % dev,copy_args)) + vdi_uuid = session.xenapi.VDI.get_uuid(vdi) + copy_args = {'vdi_uuid': vdi_uuid, 'vdi_size': int(size)} + filename = with_vdi_in_dom0(session, vdi, False, + lambda dev: + _copy_kernel_vdi('/dev/%s' % dev, copy_args)) return filename -- cgit From 21a3d77fee681d05c465c74e40177ae022bc24af Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 22:41:27 +0000 Subject: Fixing test by adding stub for get_image_meta --- nova/tests/glance/stubs.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index f182b857a..4cd5c357f 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -29,9 +29,10 @@ class FakeGlance(object): def __init__(self, host, port=None, use_ssl=False): pass - def get_image(self, image): - meta = { - 'size': 0, - } + def get_image_meta(self, image_id): + return {'size': 0, 'properties': {}} + + def get_image(self, image_id): + meta = self.get_image_meta(image_id) image_file = StringIO.StringIO('') return meta, image_file -- cgit From f6bf7e8c1e2481e870ed4baa9f2a6aa8001b5514 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 16:46:17 -0600 Subject: fail --- nova/compute/manager.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 546d07a09..19e1d9f46 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -402,10 +402,14 @@ class ComputeManager(manager.Manager): host, possibly changing the RAM and disk size in the process""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) + if instance_ref['host'] == FLAGS.host: + raise exception.Error(_( + 'Migration error: destination same as source!')) + migration_ref = self.db.migration_create(context, { 'instance_id': instance_id, 'source_compute': instance_ref['host'], - 'dest_compute': socket.gethostname(), + 'dest_compute': FLAGS.host, 'dest_host': self.driver.get_host_ip_addr(), 'status': 'pre-migrating' }) LOG.audit(_('instance %s: migrating to '), instance_id, context=context) @@ -463,7 +467,7 @@ class ComputeManager(manager.Manager): # this may get passed into the following spawn instead new_disk_info = self.driver.attach_disk(instance_ref, disk_info) - self.driver.spawn(instance_ref, disk=disk_info) + self.driver.spawn(instance_ref, disk=new_disk_info) self.db.migration_update(context, migration_id, {'status': 'finished', }) -- cgit From 00f785dab7d269b9baf403f51bc1d4b2ea1dc06a Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 16 Feb 2011 00:10:17 +0100 Subject: Make rpc thread pool size configurable. --- nova/rpc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nova/rpc.py b/nova/rpc.py index efe6164ad..067d954d0 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -43,6 +43,8 @@ from nova import utils FLAGS = flags.FLAGS LOG = logging.getLogger('nova.rpc') +FLAGS.DEFINE_integer('rpc_thread_pool_size', 1024, 'Size of RPC thread pool') + class Connection(carrot_connection.BrokerConnection): """Connection instance object""" @@ -156,7 +158,7 @@ class AdapterConsumer(TopicConsumer): def __init__(self, connection=None, topic="broadcast", proxy=None): LOG.debug(_('Initing the Adapter Consumer for %s') % topic) self.proxy = proxy - self.pool = greenpool.GreenPool(1024) + self.pool = greenpool.GreenPool(FLAGS.rpc_thread_pool_size) super(AdapterConsumer, self).__init__(connection=connection, topic=topic) -- cgit -- cgit From af920572f42b07c3ea491015d30eb5001d1f735d Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 15 Feb 2011 23:36:23 +0000 Subject: Adding vhd hidden sanity check --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 23 +++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 43c58dcff..ceb9a2185 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -140,6 +140,25 @@ def fixup_vhds(sr_path, staging_path, uuid_stack): os.rename(orig_path, new_path) return new_path + def assert_vhd_not_hidden(path): + """ + This is a sanity check on the image; if a snap.vhd isn't + present, then the image.vhd better not be marked 'hidden' or it will + be deleted when moved into the SR. + """ + vhd_query_args = shlex.split( + "vhd-util query -n %(path)s -f" % locals()) + vhd_query_proc = subprocess.Popen( + vhd_query_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = _assert_process_success(vhd_query_proc, "vhd-util") + for line in out.splitlines(): + if line.startswith('hidden'): + value = line.split(':')[1].strip() + if value == "1": + raise Exception( + "VHD %(path)s is marked as hidden without child" % + locals()) + image_path = os.path.join(staging_path, 'image') orig_base_copy_path = os.path.join(image_path, 'image.vhd') @@ -158,6 +177,8 @@ def fixup_vhds(sr_path, staging_path, uuid_stack): # orphaned) link_vhds(snap_path, base_copy_path) move_into_sr(snap_path) + else: + assert_vhd_not_hidden(base_copy_path) move_into_sr(base_copy_path) return vdi_uuid @@ -276,7 +297,7 @@ def _assert_process_success(proc, cmd): if ret != 0: msg = "%(cmd)s returned non-zero exit code (%i): '%s'" % (ret, err) raise Exception(msg) - + return out, err def download_image(session, args): """Download an image from Glance, unbundle it, and then deposit the VHDs -- cgit From a6ea6759450aab7eb021e202c68e5301667c74a9 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 17:58:57 -0600 Subject: foo --- nova/virt/xenapi/vmops.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 882d52f38..c2f5ddc41 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -97,21 +97,22 @@ class VMOps(object): vdi_uuid = VMHelper.fetch_image(self._session, instance.id, instance.image_id, user, project, disk_image_type) vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) - #Have a look at the VDI and see if it has a PV kernel - if not instance.kernel_id: - pv_kernel = VMHelper.lookup_image(self._session, instance.id, - vdi_ref) - if instance.kernel_id: - kernel = VMHelper.fetch_image(self._session, instance.id, - instance.kernel_id, user, project, - ImageType.KERNEL_RAMDISK) - if instance.ramdisk_id: - ramdisk = VMHelper.fetch_image(self._session, instance.id, - instance.ramdisk_id, user, project, - ImageType.KERNEL_RAMDISK) else: vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', disk) + #Have a look at the VDI and see if it has a PV kernel + if not instance.kernel_id: + pv_kernel = VMHelper.lookup_image(self._session, instance.id, + vdi_ref) + if instance.kernel_id: + kernel = VMHelper.fetch_image(self._session, instance.id, + instance.kernel_id, user, project, + ImageType.KERNEL_RAMDISK) + if instance.ramdisk_id: + ramdisk = VMHelper.fetch_image(self._session, instance.id, + instance.ramdisk_id, user, project, + ImageType.KERNEL_RAMDISK) + vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk, pv_kernel) VMHelper.create_vbd(session=self._session, vm_ref=vm_ref, -- cgit From cd5aba9d1d00d9daad87efd89f78e49079bee2c7 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 18:02:57 -0600 Subject: foo --- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index cc72b5d26..5bf0fe994 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -110,7 +110,7 @@ def move_vhds_into_sr(session, args): shutil.move("%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid), sr_path) shutil.move("%s/%s.vhd" % (temp_vhd_path, new_cow_uuid), sr_path) - logginig.debug('Cleaning up temporary SR path %s' % temp_vhd_path) + logging.debug('Cleaning up temporary SR path %s' % temp_vhd_path) os.rmdir(temp_vhd_path) return "" -- cgit From 1b9413e11ba1b4b49b50965e3f812e636f2319d5 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Tue, 15 Feb 2011 18:20:44 -0600 Subject: stubbed out reset networkin xenapi VM tests to solve domid problem --- nova/tests/test_xenapi.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index d5660c5d1..6b8efc9d8 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -32,6 +32,7 @@ from nova.virt import xenapi_conn from nova.virt.xenapi import fake as xenapi_fake from nova.virt.xenapi import volume_utils from nova.virt.xenapi.vmops import SimpleDH +from nova.virt.xenapi.vmops import VMOps from nova.tests.db import fakes as db_fakes from nova.tests.xenapi import stubs from nova.tests.glance import stubs as glance_stubs @@ -141,6 +142,10 @@ class XenAPIVolumeTestCase(test.TestCase): self.stubs.UnsetAll() +def reset_network(*args): + pass + + class XenAPIVMTestCase(test.TestCase): """ Unit tests for VM operations @@ -162,6 +167,7 @@ class XenAPIVMTestCase(test.TestCase): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) stubs.stubout_get_this_vm_uuid(self.stubs) stubs.stubout_stream_disk(self.stubs) + self.stubs.Set(VMOps, 'reset_network', reset_network) glance_stubs.stubout_glance_client(self.stubs, glance_stubs.FakeGlance) self.conn = xenapi_conn.get_connection(False) -- cgit From bb98e2055002ff3ed2099f60bbe4058d5f5c7b35 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 23:10:29 -0600 Subject: hurr durr --- nova/virt/xenapi/vmops.py | 5 +++-- plugins/xenserver/xenapi/etc/xapi.d/plugins/migration | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c2f5ddc41..7a176442a 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -288,11 +288,12 @@ class VMOps(object): def attach_disk(self, instance, disk_info): vm_ref = VMHelper.lookup(self._session, instance.name) new_base_copy_uuid = str(uuid.uuid4()) + new_cow_uuid = str(uuid.uuid4()) params = {'instance_id': instance.id, 'old_base_copy_uuid': disk_info['base_copy'], 'old_cow_uuid': disk_info['cow'], 'new_base_copy_uuid': new_base_copy_uuid, - 'new_cow_uuid': str(uuid.uuid4())} + 'new_cow_uuid': new_cow_uuid, } task = self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) @@ -301,7 +302,7 @@ class VMOps(object): # Now we rescan the SR so we find the VHDs VMHelper.scan_sr(self._session) - return new_base_copy_uuid + return new_cow_uuid def resize(self, instance, flavor): """Resize a running instance by changing it's RAM and disk size """ diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 5bf0fe994..7a6eefda2 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -102,7 +102,8 @@ def move_vhds_into_sr(session, args): os.rmdir(source_image_path) # Link the COW to the base copy - logging.debug('Attaching COW to the base copy...') + logging.debug('Attaching COW to the base copy %s -> %s' % + (new_cow_path, new_base_copy_path)) subprocess.call([VHD_UTIL, 'modify', '-n', new_cow_path, '-p', new_base_copy_path]) -- cgit From 98b038c6878772f6b272cb169b1c74bd7c9838b8 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Tue, 15 Feb 2011 23:56:00 -0600 Subject: Foo --- nova/api/openstack/servers.py | 12 ++++++++++-- nova/compute/api.py | 20 +++++++++++++++----- nova/compute/manager.py | 16 +++------------- nova/db/sqlalchemy/api.py | 1 + 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 06a40e92c..83b421127 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -207,10 +207,18 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotImplemented()) def _action_confirm_resize(self, input_dict, req, id): - return faults.Fault(exc.HTTPNotImplemented()) + try: + self.compute_api.confirm_resize(req.environ['nova.context'], id) + except: + return faults.Fault(exc.HTTPBadRequest()) + return exc.HTTPNoContent() def _action_revert_resize(self, input_dict, req, id): - return faults.Fault(exc.HTTPNotImplemented()) + try: + self.compute_api.confirm_resize(req.environ['nova.context'], id) + except: + return faults.Fault(exc.HTTPBadRequest()) + return exc.HTTPAccepted() def _action_rebuild(self, input_dict, req, id): return faults.Fault(exc.HTTPNotImplemented()) diff --git a/nova/compute/api.py b/nova/compute/api.py index 6b2628378..b8c4a8597 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -401,16 +401,26 @@ class API(base.Base): def revert_resize(self, context, instance_id): """Reverts a resize, deleting the 'new' instance in the process""" + context = context.elevated() instance_ref = self.db.instance_get(instance_id) - self._cast_compute_message('revert_resize', context, instance_id, - instance_ref['host']) + self._cast_compute_message('revert_resize', context, instance_id) def confirm_resize(self, context, instance_id): """Confirms a migration/resize, deleting the 'old' instance in the process.""" - migration_ref = self.db.get_migration_by_instance_id(instance_id) - self._cast_compute_message('confirm_resize', context, instance_id, - migration_ref['source_host']) + context = context.elevated() + migration_ref = self.db.migration_get_by_instance_id(context, + instance_id) + if migration_ref['status'] != 'finished': + raise exception.Error(_("Migration has incorrect status %s" % + migration_ref['status'])) + instance_ref = self.db.instance_get(context, instance_id) + + self._cast_compute_message('terminate_instance', context, instance_id, + migration_ref['source_compute']) + + self.db.instance_update(context, instance_id, + {'host': migration_ref['dest_compute'], }) def resize(self, context, instance_id, flavor): """Resize a running instance.""" diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 19e1d9f46..169509163 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -379,12 +379,7 @@ class ComputeManager(manager.Manager): def _update_state_callback(self, context, instance_id, result): """Update instance state when async task completes.""" self._update_state(context, instance_id) - - @exception.wrap_exception - @checks_instance_lock - def confirm_resize(self, context, instance_id): - """Destroys the old instance on the source machine""" - pass + @exception.wrap_exception @checks_instance_lock @@ -413,10 +408,8 @@ class ComputeManager(manager.Manager): 'dest_host': self.driver.get_host_ip_addr(), 'status': 'pre-migrating' }) LOG.audit(_('instance %s: migrating to '), instance_id, context=context) - service = self.db.service_get_by_host_and_topic(context, - migration_ref['source_compute'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, - service['host']) + instance_ref['host']) rpc.cast(context, topic, { 'method': 'resize_instance', 'args': { @@ -446,7 +439,7 @@ class ComputeManager(manager.Manager): service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_compute'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, - service['host']) + migration_ref['dest_compute']) rpc.cast(context, topic, { 'method': 'finish_resize', 'args': { @@ -472,9 +465,6 @@ class ComputeManager(manager.Manager): self.db.migration_update(context, migration_id, {'status': 'finished', }) - # Cleans up any transferred files and unmounts things - self.driver.cleanup_disk_transfer(context, instance_ref['id']) - @exception.wrap_exception @checks_instance_lock def pause_instance(self, context, instance_id): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 861d13716..1b6eaf138 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1935,6 +1935,7 @@ def migration_update(context, id, values): with session.begin(): migration = migration_get(context, id) migration.update(values) + migration.save() return migration -- cgit From 8e536500e83b311bf8d006ca23234c50962dc6aa Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 00:06:29 -0600 Subject: I fail at sessions --- nova/compute/manager.py | 1 - nova/db/sqlalchemy/api.py | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 169509163..b405e3763 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -435,7 +435,6 @@ class ComputeManager(manager.Manager): #TODO(mdietz): This is where we would update the VM record #after resizing - service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_compute'], FLAGS.compute_topic) topic = self.db.queue_get_for(context, FLAGS.compute_topic, diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 1b6eaf138..f96430e67 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1933,15 +1933,16 @@ def migration_create(context, values): def migration_update(context, id, values): session = get_session() with session.begin(): - migration = migration_get(context, id) + migration = migration_get(context, id, session=session) migration.update(values) - migration.save() + migration.save(session=session) return migration @require_admin_context -def migration_get(context, id): - session = get_session() +def migration_get(context, id, session=None): + if not session: + session = get_session() result = session.query(models.Migration).\ filter_by(id=id).first() if not result: -- cgit From c735796e0668b2bf7c45eeef6396a3fb33d22d6e Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 00:14:26 -0600 Subject: I fail at sessions --- nova/compute/api.py | 2 +- nova/db/api.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index b8c4a8597..3fb852ab0 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -409,7 +409,7 @@ class API(base.Base): """Confirms a migration/resize, deleting the 'old' instance in the process.""" context = context.elevated() - migration_ref = self.db.migration_get_by_instance_id(context, + migration_ref = self.db.migration_get_by_instance(context, instance_id) if migration_ref['status'] != 'finished': raise exception.Error(_("Migration has incorrect status %s" % diff --git a/nova/db/api.py b/nova/db/api.py index 887f57885..9ed5efedb 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -271,9 +271,9 @@ def migration_get(context, migration_id): """Finds a migration by the id""" return IMPL.migration_get(context, migration_id) -def migration_get_by_instance_id(context, instance_id): +def migration_get_by_instance(context, instance_id): """Finds a migration by the instance id its migrating""" - return IMPL.migration_get_by_instance_id(context, instance_id) + return IMPL.migration_get_by_instance(context, instance_id) #################### -- cgit From 333ee7dd74b187ec48e923f767267eb9bb29a4aa Mon Sep 17 00:00:00 2001 From: Vasiliy Shlykov Date: Wed, 16 Feb 2011 10:13:52 +0300 Subject: Added myself to the authors file. --- Authors | 1 + 1 file changed, 1 insertion(+) diff --git a/Authors b/Authors index b359fec22..c338a9b3e 100644 --- a/Authors +++ b/Authors @@ -56,6 +56,7 @@ Thierry Carrez Todd Willey Trey Morris Tushar Patil +Vasiliy Shlykov Vishvananda Ishaya Youcef Laribi Zhixue Wu -- cgit From a00b8b78ce16bbfe4368f0e5e383b7b8f49ba9ef Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 16 Feb 2011 10:02:17 +0100 Subject: assertIsNone is a 2.7-ism. --- nova/tests/test_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_log.py b/nova/tests/test_log.py index 7a5c52935..c2c9d7772 100644 --- a/nova/tests/test_log.py +++ b/nova/tests/test_log.py @@ -58,7 +58,7 @@ class LogHandlerTestCase(test.TestCase): '/some/path/foo-bar.log') def test_log_path_none(self): - self.assertIsNone(log.get_log_file_path(binary='foo-bar')) + self.assertTrue(log.get_log_file_path(binary='foo-bar') is None) def test_log_path_logfile_overrides_logdir(self): self.flags(logdir='/some/other/path', -- cgit From 9f1c46f5be0520afac21c3320e206ced98acd9ba Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 16 Feb 2011 10:46:59 +0100 Subject: Fix PEP-8 stuff --- nova/log.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nova/log.py b/nova/log.py index ec6681edd..90c40cca7 100644 --- a/nova/log.py +++ b/nova/log.py @@ -112,9 +112,11 @@ def _dictify_context(context): context = context.to_dict() return context + def _get_binary_name(): return os.path.basename(inspect.stack()[-1][1]) + def get_log_file_path(binary=None): if FLAGS.logfile: return FLAGS.logfile @@ -122,6 +124,7 @@ def get_log_file_path(binary=None): binary = binary or _get_binary_name() return '%s.log' % (os.path.join(FLAGS.logdir, binary),) + def basicConfig(): logging.basicConfig() for handler in logging.root.handlers: -- cgit From 7f8595b88df2271c278ae59b6d187b5c3b8dde40 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Wed, 16 Feb 2011 12:15:48 +0100 Subject: added functionality to nova-manage to list created networks --- bin/nova-manage | 12 ++++++++++++ nova/db/api.py | 5 +++++ nova/db/sqlalchemy/api.py | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index 7835ca551..f7cf77adb 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -499,6 +499,18 @@ class NetworkCommands(object): vlan_start=int(vlan_start), vpn_start=int(vpn_start)) + def list(self): + """List all created networks""" + print "%-18s\t%-15s\t%-15s\t%-15s" % ('CIDR', + 'netmask', + 'dhcp_start', + 'DNS') + for network in db.network_get_all(context.get_admin_context()): + print "%-18s\t%-15s\t%-15s\t%-15s" % (network.cidr, + network.netmask, + network.dhcp_start, + network.dns) + class ServiceCommands(object): """Enable and disable running services""" diff --git a/nova/db/api.py b/nova/db/api.py index 789cb8ebb..d06f3731f 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -500,6 +500,11 @@ def network_get(context, network_id): return IMPL.network_get(context, network_id) +def network_get_all(context): + """Return all defined networks.""" + return IMPL.network_get_all(context) + + # pylint: disable-msg=C0103 def network_get_associated_fixed_ips(context, network_id): """Get all network's ips that have been associated.""" diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 02855e7a9..a11572947 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1056,6 +1056,15 @@ def network_get(context, network_id, session=None): return result +@require_admin_context +def network_get_all(context): + session = get_session() + result = session.query(models.Network) + if not result: + raise exception.NotFound('No networks defined') + return result + + # NOTE(vish): pylint complains because of the long method name, but # it fits with the names of the rest of the methods # pylint: disable-msg=C0103 -- cgit From 163e81ac2bc2f9945273b0659ceb473767e5b19f Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Wed, 16 Feb 2011 11:53:50 -0500 Subject: This implements the blueprint 'Openstack API support for hostId': https://blueprints.launchpad.net/nova/+spec/openstack-api-hostid Now instances will have a unique hostId which for now is just a hash of the host. If the instance does not have a host yet, the hostId will be ''. --- nova/api/openstack/servers.py | 7 +++++- nova/tests/api/openstack/test_servers.py | 41 +++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 17c5519a1..58eda53b9 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import hashlib import json import traceback @@ -65,7 +66,11 @@ def _translate_detail_keys(inst): inst_dict['status'] = power_mapping[inst_dict['status']] inst_dict['addresses'] = dict(public=[], private=[]) inst_dict['metadata'] = {} - inst_dict['hostId'] = '' + + if inst['host']: + inst_dict['hostId'] = hashlib.sha224(inst['host']).hexdigest() + else: + inst_dict['hostId'] = '' return dict(server=inst_dict) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 724f14f19..e615141ff 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -43,6 +43,10 @@ def return_servers(context, user_id=1): return [stub_instance(i, user_id) for i in xrange(5)] +def return_servers_with_host(context, user_id=1): + return [stub_instance(i, user_id, 1) for i in xrange(5)] + + def return_security_group(context, instance_id, security_group_id): pass @@ -55,9 +59,13 @@ def instance_address(context, instance_id): return None -def stub_instance(id, user_id=1): - return Instance(id=id, state=0, image_id=10, user_id=user_id, - display_name='server%s' % id) +def stub_instance(id, user_id=1, with_hosts=False): + if with_hosts: + return Instance(id=id, state=0, image_id=10, user_id=user_id, + display_name='server%s' % id, host='host%s' % (id % 2)) + else: + return Instance(id=id, state=0, image_id=10, user_id=user_id, + display_name='server%s' % id) def fake_compute_api(cls, req, id): @@ -229,6 +237,33 @@ class ServersTest(unittest.TestCase): i = 0 for s in res_dict['servers']: self.assertEqual(s['id'], i) + self.assertEqual(s['hostId'], '') + self.assertEqual(s['name'], 'server%d' % i) + self.assertEqual(s['imageId'], 10) + i += 1 + + def test_get_all_server_details_with_host(self): + ''' + We want to make sure that if two instances are on the same host, then + they return the same hostId. If two instances are on different hosts, + they should return different hostId's. In this test, we get 5 instances + back where 2 are on one host and 3 are on another. + ''' + self.stubs.Set(nova.db.api, 'instance_get_all_by_user', + return_servers_with_host) + req = webob.Request.blank('/v1.0/servers/detail') + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + + server_list = res_dict['servers'] + host_ids = [server_list[0]['hostId'], server_list[1]['hostId']] + self.assertTrue(host_ids[0]) + self.assertTrue(host_ids[1]) + self.assertTrue(host_ids[0] != host_ids[1]) + i = 0 + for s in res_dict['servers']: + self.assertEqual(s['id'], i) + self.assertEqual(s['hostId'], host_ids[i % 2]) self.assertEqual(s['name'], 'server%d' % i) self.assertEqual(s['imageId'], 10) i += 1 -- cgit From 4375069b6635d6ccd87231cb7d9f5b17708ffb1a Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Wed, 16 Feb 2011 11:11:49 -0600 Subject: Stubbed out flavor create/delete API calls --- nova/api/openstack/flavors.py | 9 ++++++++- nova/tests/api/openstack/test_flavors.py | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 9b674afbd..215f0b8a6 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -42,7 +42,6 @@ class Controller(wsgi.Controller): def detail(self, req): """Return all flavors in detail.""" items = [self.show(req, id)['flavor'] for id in self._all_ids()] - items = common.limited(items, req) return dict(flavors=items) def show(self, req, id): @@ -57,6 +56,14 @@ class Controller(wsgi.Controller): return dict(flavor=values) raise faults.Fault(exc.HTTPNotFound()) + def create(self, req): + """Create a flavor.""" + print "CREATE! %s" % req + + def delete(self, req, id): + """Delete a flavor.""" + print "DELETE! %s %s" % (req, id) + def _all_ids(self): """Return the list of all flavorids.""" # FIXME(kpepple) do we really need admin context here ? diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 532b34e1b..7bfc46e0b 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -43,7 +43,17 @@ class FlavorsTest(unittest.TestCase): def test_get_flavor_list(self): req = webob.Request.blank('/v1.0/flavors') res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + def test_create_flavor(self): + req = webob.Request.blank("/v1.0/flavors/create/test") + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + + def test_delete_flavor(self): + req = webob.Request.blank("/v1.0/flavors/delete/test") + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) if __name__ == '__main__': unittest.main() -- cgit From 9be342534ee54f86c274f03d1d4a0c310f08e4ae Mon Sep 17 00:00:00 2001 From: Brian Schott Date: Wed, 16 Feb 2011 13:24:46 -0500 Subject: fixed authors, import sys in migration.py --- Authors | 2 +- nova/db/sqlalchemy/migration.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Authors b/Authors index 0edabc531..29fbf76b3 100644 --- a/Authors +++ b/Authors @@ -4,7 +4,7 @@ Anthony Young Antony Messerli Armando Migliaccio Bilal Akhtar -Brian Schott +Brian Schott Chiradeep Vittal Chmouel Boudjnah Chris Behrens diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py index 3b6b4707a..d2671e1a3 100644 --- a/nova/db/sqlalchemy/migration.py +++ b/nova/db/sqlalchemy/migration.py @@ -17,6 +17,7 @@ # under the License. import os +import sys from nova import flags -- cgit From 585ba4d6cf25eabf83b1b33a6de794ce671c0c98 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 16 Feb 2011 18:43:55 +0000 Subject: Putting glance plugin under pep8 control --- nova/compute/api.py | 3 --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 1 + run_tests.sh | 6 +++++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 8a16baf45..ea81c7ff0 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -104,11 +104,8 @@ class API(base.Base): image = self.image_service.show(context, image_id) if kernel_id is None: kernel_id = image.get('kernel_id', None) - # FIXME(sirp): which one to use? - #kernel_id = image.get('kernelId', None) if ramdisk_id is None: ramdisk_id = image.get('ramdisk_id', None) - #ramdisk_id = image.get('ramdiskId', None) # FIXME(sirp): is there a way we can remove null_kernel? # No kernel and ramdisk for raw images diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 47e094f02..f737c6c41 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -299,6 +299,7 @@ def _assert_process_success(proc, cmd): raise Exception(msg) return out, err + def download_image(session, args): """Download an image from Glance, unbundle it, and then deposit the VHDs into the storage repository diff --git a/run_tests.sh b/run_tests.sh index 4e21fe945..6c7dd5f46 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -73,7 +73,11 @@ fi if [ -z "$noseargs" ]; then - run_tests && pep8 --repeat --show-pep8 --show-source --exclude=vcsversion.py bin/* nova setup.py || exit 1 + PEP8_EXCLUDE=vcsversion.py + PEP8_OPTIONS="--exclude=$PEP8_EXCLUDE --repeat --show-pep8 --show-source" + # TODO(sirp): Put tests/ run_tests.py and all plugins/ under pep8 control + PEP8_INCLUDE="bin/* nova setup.py plugins/xenserver/xenapi/etc/xapi.d/plugins/glance" + run_tests && pep8 $PEP8_OPTIONS $PEP8_INCLUDE || exit 1 else run_tests fi -- cgit From 89a2ee5ee5ea7dc3d9fed4a2d5aa2fe2faed9f2b Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 16 Feb 2011 11:04:48 -0800 Subject: novatools call to child zones done --- nova/scheduler/manager.py | 51 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 00cab60cf..693f8cb4b 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -23,8 +23,10 @@ Scheduler Service import functools import novatools +import thread from datetime import datetime +from eventlet.greenpool import GreenPool from nova import db from nova import flags @@ -46,17 +48,28 @@ flags.DEFINE_integer('zone_db_check_interval', class ZoneState(object): """Holds the state of all connected child zones.""" def __init__(self): - self.is_active = True - self.name = None - self.capabilities = None - self.retry = 0 - self.last_seen = datetime.min - + self.is_active = True + self.name = None + self.capabilities = None + self.retry = 0 + self.last_seen = datetime.min + def update(self, zone): - self.zone_id = zone.id - self.api_url = zone.api_url - self.username = zone.username - self.password = zone.password + """Update zone credentials from db""" + self.zone_id = zone.id + self.api_url = zone.api_url + self.username = zone.username + self.password = zone.password + + +def _poll_zone(zone): + """Eventlet worker to poll a zone.""" + logging.debug("_POLL_ZONE: STARTING") + os = novatools.OpenStack(zone.username, zone.password, zone.api_url) + zone_metadata = os.zones.info() + logging.debug("_POLL_ZONE: GOT %s" % zone_metadata._info) + + # Stuff this in our cache. class ZoneManager(object): @@ -66,15 +79,27 @@ class ZoneManager(object): self.zone_states = {} def _refresh_from_db(self, context): + """Make our zone state map match the db.""" + # Add/update existing zones ... zones = db.zone_get_all(context) existing = self.zone_states.keys() + db_keys = [] for zone in zones: + db_keys.append(zone.id) if zone.id not in existing: - self.zone_state[zone.id] = ZoneState() - self.zone_state[zones.id].update(zone) + self.zone_states[zone.id] = ZoneState() + self.zone_states[zone.id].update(zone) + + # Cleanup zones removed from db ... + for zone_id in self.zone_states.keys(): + if zone_id not in db_keys: + del self.zone_states[zone_id] def _poll_zones(self, context): - pass + """Try to connect to each child zone and get update.""" + + green_pool = GreenPool() + green_pool.imap(_poll_zone, self.zone_states.values()) def ping(self, context=None): """Ping should be called periodically to update zone status.""" -- cgit From ec39332356d204d14c8910bf72056efd9e943dd0 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 16 Feb 2011 11:05:03 -0800 Subject: novatools call to child zones done --- nova/scheduler/zone_manager.py | 126 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 nova/scheduler/zone_manager.py diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py new file mode 100644 index 000000000..dd910eb09 --- /dev/null +++ b/nova/scheduler/zone_manager.py @@ -0,0 +1,126 @@ +# Copyright (c) 2010 Openstack, LLC. +# 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. + +""" +ZoneManager oversees all communications with child Zones. +""" + +import novatools +import thread + +from datetime import datetime +from eventlet.greenpool import GreenPool + +from nova import db +from nova import flags +from nova import log as logging + +FLAGS = flags.FLAGS +flags.DEFINE_integer('zone_db_check_interval', 60, + 'Seconds between getting fresh zone info from db.') +flags.DEFINE_integer('zone_failures_to_offline', 3, + 'Number of consecutive errors before marking zone offline') + + +class ZoneState(object): + """Holds the state of all connected child zones.""" + def __init__(self): + self.is_active = True + self.name = None + self.capabilities = None + self.attempt = 0 + self.last_seen = datetime.min + self.last_exception = None + self.last_exception_time = None + + def update_credentials(self, zone): + """Update zone credentials from db""" + self.zone_id = zone.id + self.api_url = zone.api_url + self.username = zone.username + self.password = zone.password + + def update_metadata(self, zone_metadata): + """Update zone metadata after successful communications with + child zone.""" + self.last_seen = datetime.now() + self.attempt = 0 + self.name = zone_metadata["name"] + self.capabilities = zone_metadata["capabilities"] + self.is_active = True + + def log_error(self, exception): + """Something went wrong. Check to see if zone should be + marked as offline.""" + self.last_exception = exception + self.last_exception_time = datetime.now() + logging.warning(_("%s error talking to zone %s") % (exception, + zone.api_url, FLAGS.zone_failures_to_offline)) + + self.attempt += 1 + if self.attempt >= FLAGS.zone_failures_to_offline: + self.is_active = False + logging.error(_("No answer from zone %s after %d " + "attempts. Marking inactive.") % (zone.api_url, + FLAGS.zone_failures_to_offline)) + +def _poll_zone(zone): + """Eventlet worker to poll a zone.""" + logging.debug("_POLL_ZONE: STARTING") + os = novatools.OpenStack(zone.username, zone.password, zone.api_url) + try: + zone.update_metadata(os.zones.info()._info) + except Exception, e: + zone.log_error(e) + +class ZoneManager(object): + """Keeps the zone states updated.""" + def __init__(self): + self.last_zone_db_check = datetime.min + self.zone_states = {} + + def _refresh_from_db(self, context): + """Make our zone state map match the db.""" + # Add/update existing zones ... + zones = db.zone_get_all(context) + existing = self.zone_states.keys() + db_keys = [] + for zone in zones: + db_keys.append(zone.id) + if zone.id not in existing: + self.zone_states[zone.id] = ZoneState() + self.zone_states[zone.id].update_credentials(zone) + + # Cleanup zones removed from db ... + for zone_id in self.zone_states.keys(): + if zone_id not in db_keys: + del self.zone_states[zone_id] + + def _poll_zones(self, context): + """Try to connect to each child zone and get update.""" + green_pool = GreenPool() + green_pool.imap(_poll_zone, self.zone_states.values()) + + def ping(self, context=None): + """Ping should be called periodically to update zone status.""" + logging.debug("ZoneManager PING") + diff = datetime.now() - self.last_zone_db_check + if diff.seconds >= FLAGS.zone_db_check_interval: + logging.debug("ZoneManager RECHECKING DB ") + self.last_zone_db_check = datetime.now() + self._refresh_from_db(context) + self._poll_zones(context) -- cgit From 56ebab08b29da2ed9a4ec29bb1c0695371acc142 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 16 Feb 2011 20:34:17 +0100 Subject: Spell flags correctly (i.e. not in upper case) --- nova/rpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/rpc.py b/nova/rpc.py index 067d954d0..205bb524a 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -43,7 +43,7 @@ from nova import utils FLAGS = flags.FLAGS LOG = logging.getLogger('nova.rpc') -FLAGS.DEFINE_integer('rpc_thread_pool_size', 1024, 'Size of RPC thread pool') +flags.DEFINE_integer('rpc_thread_pool_size', 1024, 'Size of RPC thread pool') class Connection(carrot_connection.BrokerConnection): -- cgit From 879845496a50477ebc2709291c159ae1e8d5aa2a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 13:47:14 -0600 Subject: Derp --- nova/compute/api.py | 26 +++++++++++++++++--------- nova/compute/manager.py | 32 +++++++++++++++++++++++++++++--- nova/db/sqlalchemy/api.py | 5 +++-- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 3fb852ab0..58dea5db6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -402,22 +402,30 @@ class API(base.Base): def revert_resize(self, context, instance_id): """Reverts a resize, deleting the 'new' instance in the process""" context = context.elevated() - instance_ref = self.db.instance_get(instance_id) - self._cast_compute_message('revert_resize', context, instance_id) + migration_ref = self.db.migration_get_by_instance_and_status(context, + instance_id, 'finished') + if not migration_ref: + raise exception.Error(_("No finished migrations found for + instance")) + + params = { 'migration_id': migration_ref['id']) + self._cast_compute_message('revert_resize', context, instance_id, + migration_ref['dest_compute'], params=params) def confirm_resize(self, context, instance_id): """Confirms a migration/resize, deleting the 'old' instance in the process.""" context = context.elevated() - migration_ref = self.db.migration_get_by_instance(context, - instance_id) - if migration_ref['status'] != 'finished': - raise exception.Error(_("Migration has incorrect status %s" % - migration_ref['status'])) + migration_ref = self.db.migration_get_by_instance_and_status(context, + instance_id, 'finished') + if not migration_ref: + raise exception.Error(_("No finished migrations found for + instance")) instance_ref = self.db.instance_get(context, instance_id) - self._cast_compute_message('terminate_instance', context, instance_id, - migration_ref['source_compute']) + params = { 'migration_id': migration_ref['id']) + self._cast_compute_message('confirm_resize', context, instance_id, + migration_ref['source_compute'], params=param) self.db.instance_update(context, instance_id, {'host': migration_ref['dest_compute'], }) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index b405e3763..c05edd140 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -380,15 +380,41 @@ class ComputeManager(manager.Manager): """Update instance state when async task completes.""" self._update_state(context, instance_id) + @exception.wrap_exception + @echecks_instance_lock + def confirm_resize(self, context, instance_id, migration_id): + """ Destroys the source instance """ + context = context.elevated() + instance_ref = self.db.instance_get(context, instance_id) + migration_ref = self.db.migration_get(context, migration_id) + self.driver.destroy(instance_ref) + self.db.migration_update(context, migration_id, + { 'status': 'confirmed' }) @exception.wrap_exception @checks_instance_lock - def revert_resize(self, context, instance_id): + def revert_resize(self, context, instance_id, migration_id): """Destroys the new instance on the destination machine, reverts the model changes, and powers on the old instance on the source machine""" - pass - + instance_ref = self.db.instance_get(context, instance_id) + migration_ref = self.db.migration_get(context, migration_id) + + if migration_ref['source_compute'] == instance_ref['host']: + self.driver.power_on(instance_ref) + self.db.migration_update(context, migration_id, + { 'status': 'reverted' }) + else: + self.driver.destroy(instance_ref) + topic = self.db.queue_get_for(context, FLAGS.compute_topic, + instance_ref['host']) + rpc.cast(context, topic, + { 'method': 'resize_instance', + 'args': { + 'migration_id': migration_ref['id'], + 'instance_id': instance_id, + }, + }) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f96430e67..62484805c 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1952,10 +1952,11 @@ def migration_get(context, id, session=None): @require_admin_context -def migration_get_by_instance(context, instance_id): +def migration_get_by_instance_and_status(context, instance_id, status): session = get_session() result = session.query(models.Migration).\ - filter_by(instance_id=instance_id).first() + filter_by(instance_id=instance_id). + filter_by(status=status).first() if not result: raise exception.NotFound(_("No migration found with instance id %s") % migration_id) -- cgit From 905cf54f06f6dde95039599ae5ea30d2f070f398 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 13:53:47 -0600 Subject: Typos --- nova/compute/api.py | 8 ++++---- nova/compute/manager.py | 2 +- nova/db/sqlalchemy/api.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 4262a771b..2f39b8b47 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -405,10 +405,10 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.Error(_("No finished migrations found for + raise exception.Error(_("No finished migrations found for \ instance")) - params = { 'migration_id': migration_ref['id']) + params = { 'migration_id': migration_ref['id'] } self._cast_compute_message('revert_resize', context, instance_id, migration_ref['dest_compute'], params=params) @@ -419,11 +419,11 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.Error(_("No finished migrations found for + raise exception.Error(_("No finished migrations found for \ instance")) instance_ref = self.db.instance_get(context, instance_id) - params = { 'migration_id': migration_ref['id']) + params = { 'migration_id': migration_ref['id'] } self._cast_compute_message('confirm_resize', context, instance_id, migration_ref['source_compute'], params=param) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index d78318bec..4bab7081a 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -381,7 +381,7 @@ class ComputeManager(manager.Manager): self._update_state(context, instance_id) @exception.wrap_exception - @echecks_instance_lock + @checks_instance_lock def confirm_resize(self, context, instance_id, migration_id): """ Destroys the source instance """ context = context.elevated() diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index b6fb57df7..f4dc8a630 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1961,7 +1961,7 @@ def migration_get(context, id, session=None): def migration_get_by_instance_and_status(context, instance_id, status): session = get_session() result = session.query(models.Migration).\ - filter_by(instance_id=instance_id). + filter_by(instance_id=instance_id).\ filter_by(status=status).first() if not result: raise exception.NotFound(_("No migration found with instance id %s") -- cgit From 6bd620fa3d378b4fe437e498ef17888bf632b9d6 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 16 Feb 2011 12:02:10 -0800 Subject: sanitize all args to strings before sending them to ldap --- nova/auth/ldapdriver.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index e652f1caa..99cbb43f9 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -73,6 +73,23 @@ LOG = logging.getLogger("nova.ldapdriver") # creating this now because I'm expecting an auth refactor # in which we may want to change the interface a bit more. +def _clean(attr): + """Clean attr for insertion into ldap""" + if attr is None: + return None + if type(attr) is unicode: + return str(attr) + return attr + +def sanitize(fn): + """Decorator to sanitize all args""" + def _wrapped(self, *args, **kwargs): + args = [_clean(x) for x in args] + kwargs = dict((k, _clean(v)) for (k, v) in kwargs) + return fn(self, *args, **kwargs) + _wrapped.func_name = fn.func_name + return _wrapped + class LdapDriver(object): """Ldap Auth driver @@ -106,23 +123,27 @@ class LdapDriver(object): self.conn.unbind_s() return False + @sanitize def get_user(self, uid): """Retrieve user by id""" attr = self.__get_ldap_user(uid) return self.__to_user(attr) + @sanitize def get_user_from_access_key(self, access): """Retrieve user by access key""" query = '(accessKey=%s)' % access dn = FLAGS.ldap_user_subtree return self.__to_user(self.__find_object(dn, query)) + @sanitize def get_project(self, pid): """Retrieve project by id""" dn = self.__project_to_dn(pid) attr = self.__find_object(dn, LdapDriver.project_pattern) return self.__to_project(attr) + @sanitize def get_users(self): """Retrieve list of users""" attrs = self.__find_objects(FLAGS.ldap_user_subtree, @@ -134,6 +155,7 @@ class LdapDriver(object): users.append(user) return users + @sanitize def get_projects(self, uid=None): """Retrieve list of projects""" pattern = LdapDriver.project_pattern @@ -143,6 +165,7 @@ class LdapDriver(object): pattern) return [self.__to_project(attr) for attr in attrs] + @sanitize def create_user(self, name, access_key, secret_key, is_admin): """Create a user""" if self.__user_exists(name): @@ -196,6 +219,7 @@ class LdapDriver(object): self.conn.add_s(self.__uid_to_dn(name), attr) return self.__to_user(dict(attr)) + @sanitize def create_project(self, name, manager_uid, description=None, member_uids=None): """Create a project""" @@ -231,6 +255,7 @@ class LdapDriver(object): self.conn.add_s(dn, attr) return self.__to_project(dict(attr)) + @sanitize def modify_project(self, project_id, manager_uid=None, description=None): """Modify an existing project""" if not manager_uid and not description: @@ -249,21 +274,25 @@ class LdapDriver(object): dn = self.__project_to_dn(project_id) self.conn.modify_s(dn, attr) + @sanitize def add_to_project(self, uid, project_id): """Add user to project""" dn = self.__project_to_dn(project_id) return self.__add_to_group(uid, dn) + @sanitize def remove_from_project(self, uid, project_id): """Remove user from project""" dn = self.__project_to_dn(project_id) return self.__remove_from_group(uid, dn) + @sanitize def is_in_project(self, uid, project_id): """Check if user is in project""" dn = self.__project_to_dn(project_id) return self.__is_in_group(uid, dn) + @sanitize def has_role(self, uid, role, project_id=None): """Check if user has role @@ -273,6 +302,7 @@ class LdapDriver(object): role_dn = self.__role_to_dn(role, project_id) return self.__is_in_group(uid, role_dn) + @sanitize def add_role(self, uid, role, project_id=None): """Add role for user (or user and project)""" role_dn = self.__role_to_dn(role, project_id) @@ -283,11 +313,13 @@ class LdapDriver(object): else: return self.__add_to_group(uid, role_dn) + @sanitize def remove_role(self, uid, role, project_id=None): """Remove role for user (or user and project)""" role_dn = self.__role_to_dn(role, project_id) return self.__remove_from_group(uid, role_dn) + @sanitize def get_user_roles(self, uid, project_id=None): """Retrieve list of roles for user (or user and project)""" if project_id is None: @@ -307,6 +339,7 @@ class LdapDriver(object): roles = self.__find_objects(project_dn, query) return [role['cn'][0] for role in roles] + @sanitize def delete_user(self, uid): """Delete a user""" if not self.__user_exists(uid): @@ -332,12 +365,14 @@ class LdapDriver(object): # Delete entry self.conn.delete_s(self.__uid_to_dn(uid)) + @sanitize def delete_project(self, project_id): """Delete a project""" project_dn = self.__project_to_dn(project_id) self.__delete_roles(project_dn) self.__delete_group(project_dn) + @sanitize def modify_user(self, uid, access_key=None, secret_key=None, admin=None): """Modify an existing user""" if not access_key and not secret_key and admin is None: -- cgit From 5b82416998369203f07f3b3adef9570622caa369 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 16 Feb 2011 12:03:59 -0800 Subject: pep8 fixes --- nova/auth/ldapdriver.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index 99cbb43f9..5da7751a0 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -73,6 +73,7 @@ LOG = logging.getLogger("nova.ldapdriver") # creating this now because I'm expecting an auth refactor # in which we may want to change the interface a bit more. + def _clean(attr): """Clean attr for insertion into ldap""" if attr is None: @@ -81,6 +82,7 @@ def _clean(attr): return str(attr) return attr + def sanitize(fn): """Decorator to sanitize all args""" def _wrapped(self, *args, **kwargs): -- cgit From c6b8f129ae57da2ea0cd844150e58d4fac7eb71d Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 16 Feb 2011 14:12:54 -0600 Subject: added test for reset_network to openstack api tests, tabstop 5 to 4, renamed migration --- nova/api/openstack/__init__.py | 1 + nova/compute/api.py | 2 +- .../versions/003_add_label_to_networks.py | 52 ++++++++++++++++++++++ .../sqlalchemy/migrate_repo/versions/003_cactus.py | 52 ---------------------- nova/tests/api/openstack/test_servers.py | 12 +++++ 5 files changed, 66 insertions(+), 53 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 056c7dd27..dc3738d4a 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -79,6 +79,7 @@ class APIRouter(wsgi.Router): server_members["actions"] = "GET" server_members['suspend'] = 'POST' server_members['resume'] = 'POST' + server_members['reset_network'] = 'POST' mapper.resource("server", "servers", controller=servers.Controller(), collection={'detail': 'GET'}, diff --git a/nova/compute/api.py b/nova/compute/api.py index 857028605..71879b5b7 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -1,4 +1,4 @@ -# vim: tabstop=5 shiftwidth=4 softtabstop=4 +# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py b/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py new file mode 100644 index 000000000..ddfe114cb --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py @@ -0,0 +1,52 @@ +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +networks = Table('networks', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +# +# New Tables +# + + +# +# Tables to alter +# + +networks_label = Column( + 'label', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + networks.create_column(networks_label) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py deleted file mode 100644 index ddfe114cb..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ /dev/null @@ -1,52 +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. - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - - -networks = Table('networks', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -# -# New Tables -# - - -# -# Tables to alter -# - -networks_label = Column( - 'label', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - networks.create_column(networks_label) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 724f14f19..89e192eed 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -281,6 +281,18 @@ class ServersTest(unittest.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) + def test_server_reset_network(self): + FLAGS.allow_admin_api = True + body = dict(server=dict( + name='server_test', imageId=2, flavorId=2, metadata={}, + personality={})) + req = webob.Request.blank('/v1.0/servers/1/reset_network') + req.method = 'POST' + req.content_type = 'application/json' + req.body = json.dumps(body) + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 202) + def test_server_diagnostics(self): req = webob.Request.blank("/v1.0/servers/1/diagnostics") req.method = "GET" -- cgit From 49a7e430ca30768a68a111223068652c781206fe Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 16 Feb 2011 13:17:42 -0800 Subject: zone manager tests --- nova/scheduler/zone_manager.py | 2 +- nova/tests/test_zones.py | 132 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 nova/tests/test_zones.py diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index dd910eb09..0974f271b 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -71,7 +71,7 @@ class ZoneState(object): logging.warning(_("%s error talking to zone %s") % (exception, zone.api_url, FLAGS.zone_failures_to_offline)) - self.attempt += 1 + self.attempt += 1 if self.attempt >= FLAGS.zone_failures_to_offline: self.is_active = False logging.error(_("No answer from zone %s after %d " diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py new file mode 100644 index 000000000..b4c8815d5 --- /dev/null +++ b/nova/tests/test_zones.py @@ -0,0 +1,132 @@ +# 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. +""" +Tests For ZoneManager +""" + +import datetime +import mox + +from nova import context +from nova import db +from nova import flags +from nova import service +from nova import test +from nova import rpc +from nova import utils +from nova.auth import manager as auth_manager +from nova.scheduler import zone_manager + + +class FakeZone: + """Represents a fake zone from the db""" + def __init__(self, *args, **kwargs): + for k, v in kwargs.iteritems(): + setattr(self, k, v) + + +class ZoneManagerTestCase(test.TestCase): + """Test case for zone manager""" + def test_ping(self): + zm = zone_manager.ZoneManager() + self.mox.StubOutWithMock(zm, '_refresh_from_db') + self.mox.StubOutWithMock(zm, '_poll_zones') + zm._refresh_from_db(mox.IgnoreArg()) + zm._poll_zones(mox.IgnoreArg()) + + self.mox.ReplayAll() + zm.ping(None) + self.mox.VerifyAll() + + def test_refresh_from_db_new(self): + zm = zone_manager.ZoneManager() + + self.mox.StubOutWithMock(db, 'zone_get_all') + db.zone_get_all(mox.IgnoreArg()).AndReturn([ + FakeZone(id=1, api_url='http://foo.com', username='user1', + password='pass1'), + ]) + + self.assertEquals(len(zm.zone_states), 0) + + self.mox.ReplayAll() + zm._refresh_from_db(None) + self.mox.VerifyAll() + + self.assertEquals(len(zm.zone_states), 1) + self.assertEquals(zm.zone_states[1].username, 'user1') + + def test_refresh_from_db_replace_existing(self): + zm = zone_manager.ZoneManager() + zone_state = zone_manager.ZoneState() + zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com', + username='user1', password='pass1')) + zm.zone_states[1] = zone_state + + self.mox.StubOutWithMock(db, 'zone_get_all') + db.zone_get_all(mox.IgnoreArg()).AndReturn([ + FakeZone(id=1, api_url='http://foo.com', username='user2', + password='pass2'), + ]) + + self.assertEquals(len(zm.zone_states), 1) + + self.mox.ReplayAll() + zm._refresh_from_db(None) + self.mox.VerifyAll() + + self.assertEquals(len(zm.zone_states), 1) + self.assertEquals(zm.zone_states[1].username, 'user2') + + def test_refresh_from_db_missing(self): + zm = zone_manager.ZoneManager() + zone_state = zone_manager.ZoneState() + zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com', + username='user1', password='pass1')) + zm.zone_states[1] = zone_state + + self.mox.StubOutWithMock(db, 'zone_get_all') + db.zone_get_all(mox.IgnoreArg()).AndReturn([ ]) + + self.assertEquals(len(zm.zone_states), 1) + + self.mox.ReplayAll() + zm._refresh_from_db(None) + self.mox.VerifyAll() + + self.assertEquals(len(zm.zone_states), 0) + + def test_refresh_from_db_add_and_delete(self): + zm = zone_manager.ZoneManager() + zone_state = zone_manager.ZoneState() + zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com', + username='user1', password='pass1')) + zm.zone_states[1] = zone_state + + self.mox.StubOutWithMock(db, 'zone_get_all') + + db.zone_get_all(mox.IgnoreArg()).AndReturn([ + FakeZone(id=2, api_url='http://foo.com', username='user2', + password='pass2'), + ]) + self.assertEquals(len(zm.zone_states), 1) + + self.mox.ReplayAll() + zm._refresh_from_db(None) + self.mox.VerifyAll() + + self.assertEquals(len(zm.zone_states), 1) + self.assertEquals(zm.zone_states[2].username, 'user2') -- cgit From 5f0340504784c1a0847e5b19aa9a317d9be16c20 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Wed, 16 Feb 2011 16:19:57 -0500 Subject: Added alternate email to mailmap --- .mailmap | 1 + Authors | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index c6f6c9a8b..a05520884 100644 --- a/.mailmap +++ b/.mailmap @@ -34,3 +34,4 @@ + diff --git a/Authors b/Authors index c57ca8aed..eb9d540bf 100644 --- a/Authors +++ b/Authors @@ -4,7 +4,6 @@ Anthony Young Antony Messerli Armando Migliaccio Bilal Akhtar -Brian Lamar Chiradeep Vittal Chmouel Boudjnah Chris Behrens -- cgit From 05b96f9ddd0cc54c74c55c170b2037eeeafb527a Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Wed, 16 Feb 2011 16:22:16 -0500 Subject: Accidently removed myself from Authors. --- Authors | 1 + 1 file changed, 1 insertion(+) diff --git a/Authors b/Authors index eb9d540bf..c57ca8aed 100644 --- a/Authors +++ b/Authors @@ -4,6 +4,7 @@ Anthony Young Antony Messerli Armando Migliaccio Bilal Akhtar +Brian Lamar Chiradeep Vittal Chmouel Boudjnah Chris Behrens -- cgit From 6b5823f0aa75707fad6ca38dde490a47b740c3da Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Wed, 16 Feb 2011 16:40:40 -0500 Subject: Flipped mailmap entries --- .mailmap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index a05520884..b2287d65f 100644 --- a/.mailmap +++ b/.mailmap @@ -34,4 +34,4 @@ - + -- cgit From 5faa6e59ff9dff02e8d583e6711bd08dd1f821fd Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 16 Feb 2011 14:15:41 -0800 Subject: add periodic disassociate from VlanManager to FlatDHCPManager. --- nova/network/manager.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/nova/network/manager.py b/nova/network/manager.py index 8eb9f041b..a4a4c6064 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -394,6 +394,18 @@ class FlatDHCPManager(FlatManager): like FlatDHCPManager. """ + def periodic_tasks(self, context=None): + """Tasks to be run at a periodic interval.""" + super(FlatDHCPManager, self).periodic_tasks(context) + now = datetime.datetime.utcnow() + timeout = FLAGS.fixed_ip_disassociate_timeout + time = now - datetime.timedelta(seconds=timeout) + num = self.db.fixed_ip_disassociate_all_by_timeout(context, + self.host, + time) + if num: + LOG.debug(_("Dissassociated %s stale fixed ip(s)"), num) + def init_host(self): """Do any initialization that needs to be run if this is a standalone service. -- cgit From 8f206774ee75c2d96c15dd2c604ae5da9601d91f Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 17:02:57 -0600 Subject: Better exceptions --- nova/api/openstack/servers.py | 15 +++++++++------ nova/db/api.py | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 83b421127..2fc105d07 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -209,15 +209,17 @@ class Controller(wsgi.Controller): def _action_confirm_resize(self, input_dict, req, id): try: self.compute_api.confirm_resize(req.environ['nova.context'], id) - except: - return faults.Fault(exc.HTTPBadRequest()) + except Exception, e: + LOG.exception(_("Error in confirm-resize %s"), e) + return faults.Fault(exc.HTTPBadRequest(e)) return exc.HTTPNoContent() def _action_revert_resize(self, input_dict, req, id): try: self.compute_api.confirm_resize(req.environ['nova.context'], id) - except: - return faults.Fault(exc.HTTPBadRequest()) + except Exception, e: + LOG.exception(_("Error in revert-resize %s"), e) + return faults.Fault(exc.HTTPBadRequest(e)) return exc.HTTPAccepted() def _action_rebuild(self, input_dict, req, id): @@ -229,8 +231,9 @@ class Controller(wsgi.Controller): flavor_id = input_dict['resize']['flavorId'] self.compute_api.resize(req.environ['nova.context'], id, flavor_id) - except: - return faults.Fault(exc.HTTPUnprocessableEntity()) + except Exception, e: + LOG.exception(_("Error in resize %s"), e) + return faults.Fault(exc.HTTPUnprocessableEntity(e)) return faults.Fault(exc.HTTPAccepted()) diff --git a/nova/db/api.py b/nova/db/api.py index 9ed5efedb..295d1a90a 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -271,7 +271,7 @@ def migration_get(context, migration_id): """Finds a migration by the id""" return IMPL.migration_get(context, migration_id) -def migration_get_by_instance(context, instance_id): +def migration_get_by_instance_and_status(context, instance_id, status): """Finds a migration by the instance id its migrating""" return IMPL.migration_get_by_instance(context, instance_id) -- cgit From a5ec2be709d28267075ddc9616c5c29b62622af5 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 16 Feb 2011 23:07:43 +0000 Subject: Adding basic test --- nova/tests/glance/stubs.py | 22 ++++++++++++++++++---- nova/tests/test_xenapi.py | 4 ++++ nova/tests/xenapi/stubs.py | 6 ++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index 4cd5c357f..fc120e523 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -26,13 +26,27 @@ def stubout_glance_client(stubs, cls): class FakeGlance(object): + IMAGE_FIXTURES = { + 1: {'image_meta': {'name': 'fakemachine', 'size': 0, + 'properties': {}}, + 'image_data': StringIO.StringIO('') }, + 2: {'image_meta': {'name': 'fakekernel', 'size': 0, + 'properties': {}}, + 'image_data': StringIO.StringIO('') }, + 3: {'image_meta': {'name': 'fakekernel', 'size': 0, + 'properties': {}}, + 'image_data': StringIO.StringIO('') }, + 4: {'image_meta': {'name': 'fakekernel', 'size': 0, + 'properties': {'disk_format': 'vhd'}}, + 'image_data': StringIO.StringIO('') }, + } + def __init__(self, host, port=None, use_ssl=False): pass def get_image_meta(self, image_id): - return {'size': 0, 'properties': {}} + return self.IMAGE_FIXTURES[image_id]['image_meta'] def get_image(self, image_id): - meta = self.get_image_meta(image_id) - image_file = StringIO.StringIO('') - return meta, image_file + image = self.IMAGE_FIXTURES[image_id] + return image['image_meta'], image['image_data'] diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index d5660c5d1..75387e7f5 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -279,6 +279,10 @@ class XenAPIVMTestCase(test.TestCase): FLAGS.xenapi_image_service = 'glance' self._test_spawn(1, None, None) + def test_spawn_vhd_glance(self): + FLAGS.xenapi_image_service = 'glance' + self._test_spawn(4, None, None) + def test_spawn_glance(self): FLAGS.xenapi_image_service = 'glance' self._test_spawn(1, 2, 3) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 624995ada..2e3b62a77 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -171,6 +171,12 @@ class FakeSessionForVMTests(fake.SessionBase): def VM_destroy(self, session_ref, vm_ref): fake.destroy_vm(vm_ref) + def SR_scan(self, session_ref, sr_ref): + pass + + def VDI_set_name_label(self, session_ref, vdi_ref, name_label): + pass + class FakeSessionForVolumeTests(fake.SessionBase): """ Stubs out a XenAPISession for Volume tests """ -- cgit From c56b1814cfae7a9c814b2d37388aff5e772771b6 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 16 Feb 2011 23:39:12 +0000 Subject: Pep8 fixes --- nova/tests/glance/stubs.py | 8 ++++---- nova/virt/xenapi/vm_utils.py | 8 +++++--- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 8 ++++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index fc120e523..c58357962 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -29,16 +29,16 @@ class FakeGlance(object): IMAGE_FIXTURES = { 1: {'image_meta': {'name': 'fakemachine', 'size': 0, 'properties': {}}, - 'image_data': StringIO.StringIO('') }, + 'image_data': StringIO.StringIO('')}, 2: {'image_meta': {'name': 'fakekernel', 'size': 0, 'properties': {}}, - 'image_data': StringIO.StringIO('') }, + 'image_data': StringIO.StringIO('')}, 3: {'image_meta': {'name': 'fakekernel', 'size': 0, 'properties': {}}, - 'image_data': StringIO.StringIO('') }, + 'image_data': StringIO.StringIO('')}, 4: {'image_meta': {'name': 'fakekernel', 'size': 0, 'properties': {'disk_format': 'vhd'}}, - 'image_data': StringIO.StringIO('') }, + 'image_data': StringIO.StringIO('')}, } def __init__(self, host, port=None, use_ssl=False): diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d77db2ddb..33945aca3 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -289,6 +289,8 @@ class VMHelper(HelperBase): """ Requests that the Glance plugin bundle the specified VDIs and push them into Glance using the specified human-friendly name. """ + # NOTE(sirp): Currently we only support uploading images as VHD, there + # is no RAW equivalent (yet) logging.debug(_("Asking xapi to upload %(vdi_uuids)s as" " ID %(image_id)s") % locals()) @@ -299,7 +301,7 @@ class VMHelper(HelperBase): 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} - task = session.async_call_plugin('glance', 'upload_image', kwargs) + task = session.async_call_plugin('glance', 'upload_vhd', kwargs) session.wait_for_task(instance_id, task) @classmethod @@ -340,7 +342,7 @@ class VMHelper(HelperBase): 'sr_path': get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} - task = session.async_call_plugin('glance', 'download_image', kwargs) + task = session.async_call_plugin('glance', 'download_vhd', kwargs) vdi_uuid = session.wait_for_task(instance_id, task) scan_sr(session, instance_id, sr_ref) @@ -350,7 +352,7 @@ class VMHelper(HelperBase): name_label = get_name_label_for_image(image) session.get_xenapi().VDI.set_name_label(vdi_ref, name_label) - LOG.debug(_("xapi 'download_image' returned VDI UUID %(vdi_uuid)s") + LOG.debug(_("xapi 'download_vhd' returned VDI UUID %(vdi_uuid)s") % locals()) return vdi_uuid diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index f737c6c41..c3f793ddd 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -300,7 +300,7 @@ def _assert_process_success(proc, cmd): return out, err -def download_image(session, args): +def download_vhd(session, args): """Download an image from Glance, unbundle it, and then deposit the VHDs into the storage repository """ @@ -321,7 +321,7 @@ def download_image(session, args): _cleanup_staging_area(staging_path) -def upload_image(session, args): +def upload_vhd(session, args): """Bundle the VHDs comprising an image and then stream them into Glance. """ params = pickle.loads(exists(args, 'params')) @@ -365,7 +365,7 @@ def remove_kernel_ramdisk(session, args): if __name__ == '__main__': - XenAPIPlugin.dispatch({'upload_image': upload_image, - 'download_image': download_image, + XenAPIPlugin.dispatch({'upload_vhd': upload_vhd, + 'download_vhd': download_vhd, 'copy_kernel_vdi': copy_kernel_vdi, 'remove_kernel_ramdisk': remove_kernel_ramdisk}) -- cgit From c01519112245f5e991ab438fe983bf9331d4e952 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 16 Feb 2011 17:51:43 -0600 Subject: fixed --- nova/compute/api.py | 5 ++++- nova/compute/manager.py | 2 -- nova/db/api.py | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 2f39b8b47..635632b73 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -425,7 +425,10 @@ class API(base.Base): params = { 'migration_id': migration_ref['id'] } self._cast_compute_message('confirm_resize', context, instance_id, - migration_ref['source_compute'], params=param) + migration_ref['source_compute'], params=params) + + self.db.migration_update(context, migration_id, + { 'status': 'confirmed' }) self.db.instance_update(context, instance_id, {'host': migration_ref['dest_compute'], }) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 4bab7081a..33fad50fd 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -388,8 +388,6 @@ class ComputeManager(manager.Manager): instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_get(context, migration_id) self.driver.destroy(instance_ref) - self.db.migration_update(context, migration_id, - { 'status': 'confirmed' }) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/api.py b/nova/db/api.py index 295d1a90a..ab871c67e 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -273,7 +273,8 @@ def migration_get(context, migration_id): def migration_get_by_instance_and_status(context, instance_id, status): """Finds a migration by the instance id its migrating""" - return IMPL.migration_get_by_instance(context, instance_id) + return IMPL.migration_get_by_instance_and_status(context, instance_id, + status) #################### -- cgit From ce847afcc1e24463d7aa522f227a08193c72fcc0 Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Wed, 16 Feb 2011 19:12:44 -0500 Subject: Moved definition of return_servers_with_host stub to inside the test_get_all_server_details_with_host test. --- nova/api/openstack/servers.py | 3 +-- nova/tests/api/openstack/test_servers.py | 29 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 58eda53b9..323e6fda6 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -67,10 +67,9 @@ def _translate_detail_keys(inst): inst_dict['addresses'] = dict(public=[], private=[]) inst_dict['metadata'] = {} + inst_dict['hostId'] = '' if inst['host']: inst_dict['hostId'] = hashlib.sha224(inst['host']).hexdigest() - else: - inst_dict['hostId'] = '' return dict(server=inst_dict) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index e615141ff..6c91b3f5b 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -43,10 +43,6 @@ def return_servers(context, user_id=1): return [stub_instance(i, user_id) for i in xrange(5)] -def return_servers_with_host(context, user_id=1): - return [stub_instance(i, user_id, 1) for i in xrange(5)] - - def return_security_group(context, instance_id, security_group_id): pass @@ -60,12 +56,8 @@ def instance_address(context, instance_id): def stub_instance(id, user_id=1, with_hosts=False): - if with_hosts: - return Instance(id=id, state=0, image_id=10, user_id=user_id, - display_name='server%s' % id, host='host%s' % (id % 2)) - else: - return Instance(id=id, state=0, image_id=10, user_id=user_id, - display_name='server%s' % id) + return Instance(id=id, state=0, image_id=10, user_id=user_id, + display_name='server%s' % id) def fake_compute_api(cls, req, id): @@ -246,20 +238,27 @@ class ServersTest(unittest.TestCase): ''' We want to make sure that if two instances are on the same host, then they return the same hostId. If two instances are on different hosts, - they should return different hostId's. In this test, we get 5 instances - back where 2 are on one host and 3 are on another. + they should return different hostId's. In this test, there are 5 + instances - 2 on one host and 3 on another. ''' + + def return_servers_with_host(context, user_id=1): + return [ + Instance(id=i, state=0, image_id=10, user_id=user_id, + display_name='server%s' % i, host='host%s' % (i % 2)) + for i in xrange(5)] self.stubs.Set(nova.db.api, 'instance_get_all_by_user', - return_servers_with_host) + return_servers_with_host) + req = webob.Request.blank('/v1.0/servers/detail') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) server_list = res_dict['servers'] host_ids = [server_list[0]['hostId'], server_list[1]['hostId']] - self.assertTrue(host_ids[0]) - self.assertTrue(host_ids[1]) + self.assertTrue(host_ids[0] and host_ids[1]) self.assertTrue(host_ids[0] != host_ids[1]) + i = 0 for s in res_dict['servers']: self.assertEqual(s['id'], i) -- cgit From 56ad2a63f1dcf4a900fa4464671015dbaac05fdc Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Wed, 16 Feb 2011 19:37:28 -0500 Subject: Minor change. Adding a helper function stub_instance() inside the test test_get_all_server_details_with_host for readability. --- nova/tests/api/openstack/test_servers.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 6c91b3f5b..630e1e5eb 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -55,7 +55,7 @@ def instance_address(context, instance_id): return None -def stub_instance(id, user_id=1, with_hosts=False): +def stub_instance(id, user_id=1): return Instance(id=id, state=0, image_id=10, user_id=user_id, display_name='server%s' % id) @@ -242,11 +242,13 @@ class ServersTest(unittest.TestCase): instances - 2 on one host and 3 on another. ''' + def stub_instance(id, user_id=1): + return Instance(id=id, state=0, image_id=10, user_id=user_id, + display_name='server%s' % id, host='host%s' % (id % 2)) + def return_servers_with_host(context, user_id=1): - return [ - Instance(id=i, state=0, image_id=10, user_id=user_id, - display_name='server%s' % i, host='host%s' % (i % 2)) - for i in xrange(5)] + return [stub_instance(i) for i in xrange(5)] + self.stubs.Set(nova.db.api, 'instance_get_all_by_user', return_servers_with_host) -- cgit From 04e29f6dc4b13b6fd0cbe5013cf241a727eb56ac Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 17 Feb 2011 01:24:31 +0000 Subject: Use glance image type to determine disk type --- nova/tests/glance/stubs.py | 12 ++-- nova/virt/xenapi/vm_utils.py | 68 ++++++++++++---------- nova/virt/xenapi/vmops.py | 9 ++- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 3 +- 4 files changed, 49 insertions(+), 43 deletions(-) diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index c58357962..1a5fb7ffb 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -28,16 +28,16 @@ def stubout_glance_client(stubs, cls): class FakeGlance(object): IMAGE_FIXTURES = { 1: {'image_meta': {'name': 'fakemachine', 'size': 0, - 'properties': {}}, + 'type': 'machine'}, 'image_data': StringIO.StringIO('')}, 2: {'image_meta': {'name': 'fakekernel', 'size': 0, - 'properties': {}}, + 'type': 'kernel'}, 'image_data': StringIO.StringIO('')}, - 3: {'image_meta': {'name': 'fakekernel', 'size': 0, - 'properties': {}}, + 3: {'image_meta': {'name': 'fakeramdisk', 'size': 0, + 'type': 'ramdisk'}, 'image_data': StringIO.StringIO('')}, - 4: {'image_meta': {'name': 'fakekernel', 'size': 0, - 'properties': {'disk_format': 'vhd'}}, + 4: {'image_meta': {'name': 'fakevhd', 'size': 0, + 'type': 'vhd'}, 'image_data': StringIO.StringIO('')}, } diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 33945aca3..278e52211 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -413,47 +413,51 @@ class VMHelper(HelperBase): within an image. To figure out which type we're dealing with, we use the following rules: - 1. If the instance is specifying a kernel explicitly, we must be using - a 'disk' image (kernel outside of the image) + 1. If we're using Glance, we can use the image_type field to + determine the image_type - 2. If the kernel isn't specified, then we have two different - scenarios: - - a) If the image is in Glance, then we can use the 'disk_format' - property to determine if the image is really a VHD-style image - or if it's a RAW image - - b) If the image is not in Glance, then it must be a RAW image - (since we don't have a way of identifying VHD images...yet) + 2. If we're not using Glance, then we need to deduce this based on + whether a kernel_id is specified. """ - def log_disk_format(disk_format): - disk_format = disk_format.upper() + def log_disk_format(image_type): + pretty_format = {ImageType.KERNEL_RAMDISK: 'KERNEL_RAMDISK', + ImageType.DISK: 'DISK', + ImageType.DISK_RAW: 'DISK_RAW', + ImageType.DISK_VHD: 'DISK_VHD'} + disk_format = pretty_format[image_type] image_id = instance.image_id instance_id = instance.id LOG.debug(_("Detected %(disk_format)s format for image " "%(image_id)s, instance %(instance_id)s") % locals()) - if instance.kernel_id: - # 1. DISK - log_disk_format('disk') - return ImageType.DISK - elif FLAGS.xenapi_image_service == 'glance': - # if using glance, then we could be VHD format + def determine_from_glance(): + glance_type2nova_type = {'machine': ImageType.DISK, + 'raw': ImageType.DISK_RAW, + 'vhd': ImageType.DISK_VHD, + 'kernel': ImageType.KERNEL_RAMDISK, + 'ramdisk': ImageType.KERNEL_RAMDISK} client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) meta = client.get_image_meta(instance.image_id) - properties = meta['properties'] - disk_format = properties.get('disk_format', None) - # TODO(sirp): When Glance treats disk_format as a first class - # attribute, we should start using that rather than an - # image-property - if disk_format == 'vhd': - # 2a. DISK_VHD - log_disk_format('disk_vhd') - return ImageType.DISK_VHD - - # 2b. DISK_RAW - log_disk_format('disk_raw') - return ImageType.DISK_RAW + type_ = meta['type'] + try: + return glance_type2nova_type[type_] + except KeyError: + raise exception.NotFound( + _("Unrecognized image type '%(type_)s'") % locals()) + + def determine_from_instance(): + if instance.kernel_id: + return ImageType.DISK + else: + return ImageType.DISK_RAW + + if FLAGS.xenapi_image_service == 'glance': + image_type = determine_from_glance() + else: + image_type = determine_from_instance() + + log_disk_format(image_type) + return image_type @classmethod def _fetch_image_glance(cls, session, instance_id, image, access, type): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 943d74da3..961d589d5 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -224,7 +224,8 @@ class VMOps(object): VMHelper.upload_image( self._session, instance.id, template_vdi_uuids, image_id) finally: - self._destroy(instance, template_vm_ref, shutdown=False) + self._destroy(instance, template_vm_ref, shutdown=False, + destroy_kernel_ramdisk=False) logging.debug(_("Finished snapshot and upload for VM %s"), instance) @@ -368,7 +369,8 @@ class VMOps(object): vm = VMHelper.lookup(self._session, instance.name) return self._destroy(instance, vm, shutdown=True) - def _destroy(self, instance, vm, shutdown=True): + def _destroy(self, instance, vm, shutdown=True, + destroy_kernel_ramdisk=True): """ Destroys VM instance by performing: @@ -385,7 +387,8 @@ class VMOps(object): self._shutdown(instance, vm) self._destroy_vdis(instance, vm) - self._destroy_kernel_ramdisk(instance, vm) + if destroy_kernel_ramdisk: + self._destroy_kernel_ramdisk(instance, vm) self._destroy_vm(instance, vm) def _wait_with_callback(self, instance_id, task, callback): diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index c3f793ddd..a91f8a7c1 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -211,8 +211,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): headers = { 'x-image-meta-store': 'file', 'x-image-meta-is_public': 'True', - 'x-image-meta-type': 'raw', - 'x-image-meta-property-disk-format': 'vhd', + 'x-image-meta-type': 'vhd', 'x-image-meta-property-kernel-id': 'nokernel', 'x-image-meta-property-ramdisk-id': 'noramdisk', 'x-image-meta-property-container-format': 'tarball', -- cgit From 719dbda7f8b856af334744de4807036e6ee704c1 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 16 Feb 2011 18:30:56 -0800 Subject: polling tests --- nova/scheduler/zone_manager.py | 15 ++++++++++----- nova/tests/test_zones.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index 0974f271b..a6bbc2ebd 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -68,22 +68,27 @@ class ZoneState(object): marked as offline.""" self.last_exception = exception self.last_exception_time = datetime.now() - logging.warning(_("%s error talking to zone %s") % (exception, - zone.api_url, FLAGS.zone_failures_to_offline)) + logging.warning(_("'%s' error talking to zone %s") % (exception, + self.api_url)) self.attempt += 1 if self.attempt >= FLAGS.zone_failures_to_offline: self.is_active = False logging.error(_("No answer from zone %s after %d " - "attempts. Marking inactive.") % (zone.api_url, + "attempts. Marking inactive.") % (self.api_url, FLAGS.zone_failures_to_offline)) + +def _call_novatools(zone): + """Call novatools. Broken out for testing purposes.""" + os = novatools.OpenStack(zone.username, zone.password, zone.api_url) + return os.zones.info()._info + def _poll_zone(zone): """Eventlet worker to poll a zone.""" logging.debug("_POLL_ZONE: STARTING") - os = novatools.OpenStack(zone.username, zone.password, zone.api_url) try: - zone.update_metadata(os.zones.info()._info) + zone.update_metadata(_call_novatools(zone)) except Exception, e: zone.log_error(e) diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py index b4c8815d5..2cb070aca 100644 --- a/nova/tests/test_zones.py +++ b/nova/tests/test_zones.py @@ -19,6 +19,7 @@ Tests For ZoneManager import datetime import mox +import novatools from nova import context from nova import db @@ -30,6 +31,8 @@ from nova import utils from nova.auth import manager as auth_manager from nova.scheduler import zone_manager +FLAGS = flags.FLAGS + class FakeZone: """Represents a fake zone from the db""" @@ -38,6 +41,11 @@ class FakeZone: setattr(self, k, v) +def exploding_novatools(zone): + """Used when we want to simulate a novatools call failing.""" + raise Exception("kaboom") + + class ZoneManagerTestCase(test.TestCase): """Test case for zone manager""" def test_ping(self): @@ -130,3 +138,36 @@ class ZoneManagerTestCase(test.TestCase): self.assertEquals(len(zm.zone_states), 1) self.assertEquals(zm.zone_states[2].username, 'user2') + + def test_poll_zone(self): + self.mox.StubOutWithMock(zone_manager, '_call_novatools') + zone_manager._call_novatools(mox.IgnoreArg()).AndReturn( + dict(name='zohan', capabilities='hairdresser')) + + zone_state = zone_manager.ZoneState() + zone_state.update_credentials(FakeZone(id=2, + api_url='http://foo.com', username='user2', + password='pass2')) + zone_state.attempt = 1 + + self.mox.ReplayAll() + zone_manager._poll_zone(zone_state) + self.mox.VerifyAll() + self.assertEquals(zone_state.attempt, 0) + self.assertEquals(zone_state.name, 'zohan') + + def test_poll_zone_fails(self): + self.stubs.Set(zone_manager, "_call_novatools", exploding_novatools) + + zone_state = zone_manager.ZoneState() + zone_state.update_credentials(FakeZone(id=2, + api_url='http://foo.com', username='user2', + password='pass2')) + zone_state.attempt = FLAGS.zone_failures_to_offline - 1 + + self.mox.ReplayAll() + zone_manager._poll_zone(zone_state) + self.mox.VerifyAll() + self.assertEquals(zone_state.attempt, 3) + self.assertFalse(zone_state.is_active) + self.assertEquals(zone_state.name, None) -- cgit From 984db08a205bdd9196c3e1cc3415873a853c33ba Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 16 Feb 2011 18:35:43 -0800 Subject: style cleanup --- nova/scheduler/zone_manager.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index a6bbc2ebd..a35acb000 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -84,14 +84,16 @@ def _call_novatools(zone): os = novatools.OpenStack(zone.username, zone.password, zone.api_url) return os.zones.info()._info + def _poll_zone(zone): """Eventlet worker to poll a zone.""" - logging.debug("_POLL_ZONE: STARTING") + logging.debug(_("Polling zone: %s") % zone.api_url) try: zone.update_metadata(_call_novatools(zone)) except Exception, e: zone.log_error(e) + class ZoneManager(object): """Keeps the zone states updated.""" def __init__(self): @@ -122,10 +124,9 @@ class ZoneManager(object): def ping(self, context=None): """Ping should be called periodically to update zone status.""" - logging.debug("ZoneManager PING") diff = datetime.now() - self.last_zone_db_check if diff.seconds >= FLAGS.zone_db_check_interval: - logging.debug("ZoneManager RECHECKING DB ") + logging.debug("Updating zone cache from db.") self.last_zone_db_check = datetime.now() self._refresh_from_db(context) self._poll_zones(context) -- cgit From 1ba8f07b9fb696ffa601f5d9104612505207d147 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 16 Feb 2011 23:02:24 -0800 Subject: first crack at instance types docs --- doc/source/adminguide/managing.instance.types.rst | 79 +++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 doc/source/adminguide/managing.instance.types.rst diff --git a/doc/source/adminguide/managing.instance.types.rst b/doc/source/adminguide/managing.instance.types.rst new file mode 100644 index 000000000..ff4e2b087 --- /dev/null +++ b/doc/source/adminguide/managing.instance.types.rst @@ -0,0 +1,79 @@ + + Copyright 2010-2011 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. + +Managing Instance Types and Flavors +=================================== + +What are Instance Types or Flavors ? +------------------------------------ + +Instance types are the container descriptions (meta-data) about instances. In layman terms, this is the size of the instance (vCPUs, RAM, etc.) that you will be launching. In the EC2 API, these are called by names such as "m1.large" or "m1.tiny", while the OpenStack API terms these "flavors" with names like "" + +Flavors are simply the name for instance types used in the OpenStack API. In nova, these are equivalent terms, so when you create an instance type you are also creating a flavor. For the rest of this document, I will refer to these as instance types. + +In the current (Cactus) version of nova, instance types can only be created by the nova administrator through the nova-manage command. Future versions of nova (in concert with the OpenStack API or EC2 API), may expose this functionality directly to users. + +Basic Management +---------------- + +Instance types / flavor are managed through the nova-manage binary with +the "instance_type" command and an appropriate subcommand. Note that you can also use +the "flavor" command as a synonym for "instance_types". + +To see all currently active instance types, use the list subcommand:: + + $ nova-manage instance_type list + m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + +By default, the list subcommand only shows active instance types. To see all instance types +(even those deleted), add the argument 1 after the list subcommand like so:: + + $ nova-manage instance_type list 1 + m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.deleted: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + +To create an instance type, use the "create" subcommand with the following positional arguments: + * memory (expressed in megabytes) + * vcpu(s) (integer) + * local storage (expressed in gigabytes) + * flavorid (unique integer) + * swap space (expressed in megabytes, defaults to zero, optional) + * RXTX quotas (expressed in gigabytes, defaults to zero, optional) + * RXTX cap (expressed in gigabytes, defaults to zero, optional) + +The following example creates an instance type named "m1.xxlarge":: + + # nova-manage instance_type create m1.xxlarge 32768 16 320 0 0 0 + m1.xxlarge created + +To delete an instance type, use the "delete" subcommand and specify the name:: + + # nova-manage instance_type delete m1.xxlarge + m1.xxlarge deleted + +Please note that the "delete" command only marks the instance type as +inactive in the database; it does not actually remove the instance type. This is done +to preserve the instance type definition for long running instances (which may not +terminate for months or years). -- cgit From 923a4938b73b84aa8a31f08a7c7b983cc82959fe Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 17 Feb 2011 07:29:50 +0000 Subject: Adding tests --- nova/tests/glance/stubs.py | 25 ++++++++++++---- nova/tests/test_xenapi.py | 70 ++++++++++++++++++++++++++++++++++++++++++-- nova/tests/xenapi/stubs.py | 10 +++++++ nova/virt/xenapi/vm_utils.py | 4 +++ 4 files changed, 100 insertions(+), 9 deletions(-) diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index 1a5fb7ffb..3ff8d7ce5 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -26,20 +26,33 @@ def stubout_glance_client(stubs, cls): class FakeGlance(object): + IMAGE_MACHINE = 1 + IMAGE_KERNEL = 2 + IMAGE_RAMDISK = 3 + IMAGE_RAW = 4 + IMAGE_VHD = 5 + IMAGE_FIXTURES = { - 1: {'image_meta': {'name': 'fakemachine', 'size': 0, + IMAGE_MACHINE: { + 'image_meta': {'name': 'fakemachine', 'size': 0, 'type': 'machine'}, 'image_data': StringIO.StringIO('')}, - 2: {'image_meta': {'name': 'fakekernel', 'size': 0, + IMAGE_KERNEL: { + 'image_meta': {'name': 'fakekernel', 'size': 0, 'type': 'kernel'}, 'image_data': StringIO.StringIO('')}, - 3: {'image_meta': {'name': 'fakeramdisk', 'size': 0, + IMAGE_RAMDISK: { + 'image_meta': {'name': 'fakeramdisk', 'size': 0, 'type': 'ramdisk'}, 'image_data': StringIO.StringIO('')}, - 4: {'image_meta': {'name': 'fakevhd', 'size': 0, - 'type': 'vhd'}, + IMAGE_RAW: { + 'image_meta': {'name': 'fakeraw', 'size': 0, + 'type': 'raw'}, 'image_data': StringIO.StringIO('')}, - } + IMAGE_VHD: { + 'image_meta': {'name': 'fakevhd', 'size': 0, + 'type': 'vhd'}, + 'image_data': StringIO.StringIO('')}} def __init__(self, host, port=None, use_ssl=False): pass diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 75387e7f5..f8a3d72c4 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -31,6 +31,7 @@ from nova.compute import power_state from nova.virt import xenapi_conn from nova.virt.xenapi import fake as xenapi_fake from nova.virt.xenapi import volume_utils +from nova.virt.xenapi import vm_utils from nova.virt.xenapi.vmops import SimpleDH from nova.tests.db import fakes as db_fakes from nova.tests.xenapi import stubs @@ -162,6 +163,7 @@ class XenAPIVMTestCase(test.TestCase): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) stubs.stubout_get_this_vm_uuid(self.stubs) stubs.stubout_stream_disk(self.stubs) + stubs.stubout_lookup_image(self.stubs) glance_stubs.stubout_glance_client(self.stubs, glance_stubs.FakeGlance) self.conn = xenapi_conn.get_connection(False) @@ -277,15 +279,17 @@ class XenAPIVMTestCase(test.TestCase): def test_spawn_raw_glance(self): FLAGS.xenapi_image_service = 'glance' - self._test_spawn(1, None, None) + self._test_spawn(glance_stubs.FakeGlance.IMAGE_RAW, None, None) def test_spawn_vhd_glance(self): FLAGS.xenapi_image_service = 'glance' - self._test_spawn(4, None, None) + self._test_spawn(glance_stubs.FakeGlance.IMAGE_VHD, None, None) def test_spawn_glance(self): FLAGS.xenapi_image_service = 'glance' - self._test_spawn(1, 2, 3) + self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE, + glance_stubs.FakeGlance.IMAGE_KERNEL, + glance_stubs.FakeGlance.IMAGE_RAMDISK) def tearDown(self): super(XenAPIVMTestCase, self).tearDown() @@ -334,3 +338,63 @@ class XenAPIDiffieHellmanTestCase(test.TestCase): def tearDown(self): super(XenAPIDiffieHellmanTestCase, self).tearDown() + + +class XenAPIDetermineDiskImageTestCase(test.TestCase): + """ + Unit tests for code that detects the ImageType + """ + def setUp(self): + super(XenAPIDetermineDiskImageTestCase, self).setUp() + glance_stubs.stubout_glance_client(self.stubs, + glance_stubs.FakeGlance) + + class FakeInstance(object): + pass + + self.fake_instance = FakeInstance() + self.fake_instance.id = 42 + + def assert_disk_type(self, disk_type): + dt = vm_utils.VMHelper.determine_disk_image_type( + self.fake_instance) + self.assertEqual(disk_type, dt) + + def test_instance_disk(self): + """ + If a kernel is specified then the image type is DISK (aka machine) + """ + FLAGS.xenapi_image_service = 'objectstore' + self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_MACHINE + self.fake_instance.kernel_id = glance_stubs.FakeGlance.IMAGE_KERNEL + self.assert_disk_type(vm_utils.ImageType.DISK) + + def test_instance_disk_raw(self): + """ + If the kernel isn't specified, and we're not using Glance, then + DISK_RAW is assumed. + """ + FLAGS.xenapi_image_service = 'objectstore' + self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_RAW + self.fake_instance.kernel_id = None + self.assert_disk_type(vm_utils.ImageType.DISK_RAW) + + def test_glance_disk_raw(self): + """ + If we're using Glance, then defer to the image_type field, which in + this case will be 'raw'. + """ + FLAGS.xenapi_image_service = 'glance' + self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_RAW + self.fake_instance.kernel_id = None + self.assert_disk_type(vm_utils.ImageType.DISK_RAW) + + def test_glance_disk_vhd(self): + """ + If we're using Glance, then defer to the image_type field, which in + this case will be 'vhd'. + """ + FLAGS.xenapi_image_service = 'glance' + self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_VHD + self.fake_instance.kernel_id = None + self.assert_disk_type(vm_utils.ImageType.DISK_VHD) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 2e3b62a77..1e6758a33 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -130,6 +130,16 @@ def stubout_stream_disk(stubs): stubs.Set(vm_utils, '_stream_disk', f) +def stubout_lookup_image(stubs): + @classmethod + def fake_lookup_image(cls, session, instance_id, vdi_ref): + # NOTE(sirp): pretending each image is paravirtualized for now + is_pv = True + return is_pv + + stubs.Set(vm_utils.VMHelper, 'lookup_image', fake_lookup_image) + + class FakeSessionForVMTests(fake.SessionBase): """ Stubs out a XenAPISession for VM tests """ def __init__(self, uri): diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 278e52211..9027d58c4 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -490,6 +490,9 @@ class VMHelper(HelperBase): @classmethod def lookup_image(cls, session, instance_id, vdi_ref): + """ + Determine if VDI is using a PV kernel + """ if FLAGS.xenapi_image_service == 'glance': return cls._lookup_image_glance(session, vdi_ref) else: @@ -517,6 +520,7 @@ class VMHelper(HelperBase): def is_vdi_pv(dev): LOG.debug(_("Running pygrub against %s"), dev) + # TODO(sirp): use subprocess here output = os.popen('pygrub -qn /dev/%s' % dev) for line in output.readlines(): #try to find kernel string -- cgit From 8dceaccb81e95b55fac2156df4f04ef0a7469112 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 17 Feb 2011 07:58:42 +0000 Subject: Typo fixes --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 4 ++-- run_tests.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index a91f8a7c1..3b5cedda7 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -85,7 +85,7 @@ def _download_tarball(sr_path, staging_path, image_id, glance_host, conn.close() -def fixup_vhds(sr_path, staging_path, uuid_stack): +def _fixup_vhds(sr_path, staging_path, uuid_stack): """Fixup the downloaded VHDs before we move them into the SR. We cannot extract VHDs directly into the SR since they don't yet have @@ -314,7 +314,7 @@ def download_vhd(session, args): try: _download_tarball(sr_path, staging_path, image_id, glance_host, glance_port) - vdi_uuid = fixup_vhds(sr_path, staging_path, uuid_stack) + vdi_uuid = _fixup_vhds(sr_path, staging_path, uuid_stack) return vdi_uuid finally: _cleanup_staging_area(staging_path) diff --git a/run_tests.sh b/run_tests.sh index 6c7dd5f46..7ac5ae071 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -75,7 +75,7 @@ if [ -z "$noseargs" ]; then PEP8_EXCLUDE=vcsversion.py PEP8_OPTIONS="--exclude=$PEP8_EXCLUDE --repeat --show-pep8 --show-source" - # TODO(sirp): Put tests/ run_tests.py and all plugins/ under pep8 control + # TODO(sirp): Put run_tests.py and all plugins/ under pep8 control PEP8_INCLUDE="bin/* nova setup.py plugins/xenserver/xenapi/etc/xapi.d/plugins/glance" run_tests && pep8 $PEP8_OPTIONS $PEP8_INCLUDE || exit 1 else -- cgit From f50101fcf845e93637f50e426ceb759641a20b76 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Thu, 17 Feb 2011 13:46:24 +0100 Subject: Make eth0 the default for FLAGS.public_interface. --- nova/network/linux_net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index c1cbff7d8..535ce87bc 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -44,7 +44,7 @@ flags.DEFINE_string('dhcp_domain', flags.DEFINE_string('networks_path', '$state_path/networks', 'Location to keep network config files') -flags.DEFINE_string('public_interface', 'vlan1', +flags.DEFINE_string('public_interface', 'eth0', 'Interface for public IP addresses') flags.DEFINE_string('vlan_interface', 'eth0', 'network device for vlans') -- cgit From bf7ec579786d3f02e63fc870fdfcb1e9c2674f05 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Thu, 17 Feb 2011 15:14:45 +0100 Subject: added i18n of 'No networks defined' --- nova/db/sqlalchemy/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index a11572947..65436ab0f 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1061,7 +1061,7 @@ def network_get_all(context): session = get_session() result = session.query(models.Network) if not result: - raise exception.NotFound('No networks defined') + raise exception.NotFound(_('No networks defined')) return result -- cgit From e28ce7f82d1c89ab0c4e5ebfa98c12f502a33138 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Thu, 17 Feb 2011 09:48:16 -0500 Subject: removing superfluous pass statements; replacing list comprehension with for loop; alphabetizing imports --- nova/api/openstack/servers.py | 6 ++---- nova/tests/api/openstack/test_servers.py | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 60f3d96e3..312d83ba5 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -72,15 +72,13 @@ def _translate_detail_keys(inst): inst_dict['addresses']['private'].append(private_ip) except KeyError: LOG.debug(_("Failed to read private ip")) - pass # grab all public floating ips try: - [inst_dict['addresses']['public'].append(floating['address']) \ - for floating in inst['fixed_ip']['floating_ips']] + for floating in inst['fixed_ip']['floating_ips']: + inst_dict['addresses']['public'].append(floating['address']) except KeyError: LOG.debug(_("Failed to read public ip(s)")) - pass inst_dict['metadata'] = {} inst_dict['hostId'] = '' diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 816a0ab8c..ace3b6850 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -15,9 +15,9 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime import json import unittest -import datetime import stubout import webob -- cgit From 5b97ee78b1bc2073bca0204caf92ae4560ec1e8e Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Thu, 17 Feb 2011 16:46:55 +0100 Subject: added more I18N --- bin/nova-manage | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index f7cf77adb..c64277908 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -501,9 +501,9 @@ class NetworkCommands(object): def list(self): """List all created networks""" - print "%-18s\t%-15s\t%-15s\t%-15s" % ('CIDR', - 'netmask', - 'dhcp_start', + print "%-18s\t%-15s\t%-15s\t%-15s" % (_('network'), + _('netmask'), + _('start address'), 'DNS') for network in db.network_get_all(context.get_admin_context()): print "%-18s\t%-15s\t%-15s\t%-15s" % (network.cidr, -- cgit From 7bb9e4c598f829a16cc6444346e087ddb506182a Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Thu, 17 Feb 2011 16:58:00 +0100 Subject: added new functionality to list all defined fixed ips --- bin/nova-manage | 31 +++++++++++++++++++++++++++++++ nova/db/api.py | 5 +++++ nova/db/sqlalchemy/api.py | 11 +++++++++++ 3 files changed, 47 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index e4c0684c4..a8c9441e2 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -433,6 +433,37 @@ class ProjectCommands(object): "nova-api server on this host.") +class FixedIpCommands(object): + """Class for managing fixed ip.""" + + def list(self, host=None): + """Lists all fixed ips (optionally by host) arguments: [host]""" + ctxt = context.get_admin_context() + if host == None: + fixed_ips = db.fixed_ip_get_all(ctxt) + else: + fixed_ips = db.fixed_ip_get_all_by_host(ctxt, host) + + print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % (_('network'), + _('IP address'), + _('MAC address'), + _('hostname'), + _('host')) + for fixed_ip in fixed_ips: + hostname = None + host = None + mac_address = None + if fixed_ip['instance']: + instance = fixed_ip['instance'] + hostname = instance['hostname'] + host = instance['host'] + mac_address = instance['mac_address'] + print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % ( + fixed_ip['network']['cidr'], + fixed_ip['address'], + mac_address, hostname, host) + + class FloatingIpCommands(object): """Class for managing floating ip.""" diff --git a/nova/db/api.py b/nova/db/api.py index 789cb8ebb..2b621044a 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -288,6 +288,11 @@ def fixed_ip_disassociate_all_by_timeout(context, host, time): return IMPL.fixed_ip_disassociate_all_by_timeout(context, host, time) +def fixed_ip_get_all(context): + """Get all defined fixed ips.""" + return IMPL.fixed_ip_get_all(context) + + def fixed_ip_get_by_address(context, address): """Get a fixed ip by address or raise if it does not exist.""" return IMPL.fixed_ip_get_by_address(context, address) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 02855e7a9..55f2f28bd 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -583,6 +583,17 @@ def fixed_ip_disassociate_all_by_timeout(_context, host, time): return result.rowcount +@require_admin_context +def fixed_ip_get_all(context, session=None): + if not session: + session = get_session() + result = session.query(models.FixedIp).all() + if not result: + raise exception.NotFound(_('No fixed ips defined')) + + return result + + @require_context def fixed_ip_get_by_address(context, address, session=None): if not session: -- cgit From 0b4641a90e5f51cddccb9886902a90d64ceb3200 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Thu, 17 Feb 2011 17:10:51 +0100 Subject: added entry in the category list --- bin/nova-manage | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/nova-manage b/bin/nova-manage index a8c9441e2..ddc78f7e5 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -653,6 +653,7 @@ CATEGORIES = [ ('role', RoleCommands), ('shell', ShellCommands), ('vpn', VpnCommands), + ('fixed', FixedIpCommands), ('floating', FloatingIpCommands), ('network', NetworkCommands), ('service', ServiceCommands), -- cgit From 9d056b6fadcefed9ef9573bd89125b00af5e2726 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 17 Feb 2011 10:50:49 -0600 Subject: More testing --- nova/api/openstack/__init__.py | 1 + nova/api/openstack/flavors.py | 14 ++++++++++++-- nova/tests/api/openstack/test_flavors.py | 6 ++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 056c7dd27..1fafebe37 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -73,6 +73,7 @@ class APIRouter(wsgi.Router): server_members = {'action': 'POST'} if FLAGS.allow_admin_api: LOG.debug(_("Including admin operations in API.")) + server_members['pause'] = 'POST' server_members['unpause'] = 'POST' server_members["diagnostics"] = "GET" diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 215f0b8a6..a3a664506 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -58,11 +58,21 @@ class Controller(wsgi.Controller): def create(self, req): """Create a flavor.""" + instance_types.create( + name, + memory, + vcpus, + local_gb, + flavor_id, + swap, + rxtx_quota, + rxtx_cap) print "CREATE! %s" % req - def delete(self, req, id): + def delete(self, req, name): """Delete a flavor.""" - print "DELETE! %s %s" % (req, id) + instance_type.destroy(name) + print "DELETE! %s %s" % (req, name) def _all_ids(self): """Return the list of all flavorids.""" diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 7bfc46e0b..209ace0f8 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -46,12 +46,14 @@ class FlavorsTest(unittest.TestCase): self.assertEqual(res.status_int, 200) def test_create_flavor(self): - req = webob.Request.blank("/v1.0/flavors/create/test") + req = webob.Request.blank("/v1.0/flavors") + req.method = "POST" res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) def test_delete_flavor(self): - req = webob.Request.blank("/v1.0/flavors/delete/test") + req = webob.Request.blank("/v1.0/flavors/1") + req.method = "DELETE" res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) -- cgit From 28b77765fd38038fd7093589170dead48ffc417f Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Thu, 17 Feb 2011 12:13:20 -0500 Subject: Switched mailmap entries --- .mailmap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index b2287d65f..a05520884 100644 --- a/.mailmap +++ b/.mailmap @@ -34,4 +34,4 @@ - + -- cgit From 841b02230866cc163c26a264e86bba94c4b0335d Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Thu, 17 Feb 2011 13:15:28 -0500 Subject: Adding myself to Authors and .mailmap files. --- .mailmap | 1 + Authors | 1 + 2 files changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index c6f6c9a8b..95bf1a1bc 100644 --- a/.mailmap +++ b/.mailmap @@ -34,3 +34,4 @@ + diff --git a/Authors b/Authors index b359fec22..168a3cf9a 100644 --- a/Authors +++ b/Authors @@ -42,6 +42,7 @@ Monty Taylor MORITA Kazutaka Muneyuki Noguchi Nachi Ueno +Naveed Massjouni Paul Voccio Ricardo Carrillo Cruz Rick Clark -- cgit From ea4d21b546d9447bac50cf97a62c11129da12d21 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 17 Feb 2011 13:10:37 -0600 Subject: comments + Englilish, changed copyright in migration, removed network_get_all from db.api (vestigial) --- nova/api/openstack/servers.py | 2 +- nova/compute/api.py | 2 +- nova/compute/manager.py | 2 +- nova/db/api.py | 7 +------ .../sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py | 3 +-- nova/virt/xenapi/vmops.py | 6 ++++++ plugins/xenserver/xenapi/etc/xapi.d/plugins/agent | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 8b72704ba..33cc3bbde 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -251,7 +251,7 @@ class Controller(wsgi.Controller): def reset_network(self, req, id): """ - admin only operation which resets networking on an instance + Reset networking on an instance (admin only). """ context = req.environ['nova.context'] diff --git a/nova/compute/api.py b/nova/compute/api.py index 71879b5b7..ed6f0e34a 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -468,7 +468,7 @@ class API(base.Base): def reset_network(self, context, instance_id): """ - resets networking on the instance + Reset networking on the instance. """ self._cast_compute_message('reset_network', context, instance_id) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 1e2b95294..6fab1a41c 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -501,7 +501,7 @@ class ComputeManager(manager.Manager): @checks_instance_lock def reset_network(self, context, instance_id): """ - resets the networking on the instance + Reset networking on the instance. """ context = context.elevated() diff --git a/nova/db/api.py b/nova/db/api.py index 850a5126f..ce3395932 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -505,11 +505,6 @@ def network_get(context, network_id): return IMPL.network_get(context, network_id) -def network_get_all(context): - """Get all networks""" - return IMPL.network_get_all(context) - - # pylint: disable-msg=C0103 def network_get_associated_fixed_ips(context, network_id): """Get all network's ips that have been associated.""" @@ -527,7 +522,7 @@ def network_get_by_instance(context, instance_id): def network_get_all_by_instance(context, instance_id): - """Get all networks by instance id or raise if it does not exist.""" + """Get all networks by instance id or raise if none exist.""" return IMPL.network_get_all_by_instance(context, instance_id) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py b/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py index ddfe114cb..5ba7910f1 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.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 diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ea99ff626..842e08f22 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -151,6 +151,8 @@ class VMOps(object): % locals()) # NOTE(armando): Do we really need to do this in virt? + # NOTE(tr3buchet): not sure but wherever we do it, we need to call + # reset_network afterwards timer = utils.LoopingCall(f=None) def _wait_for_boot(): @@ -437,6 +439,10 @@ class VMOps(object): return 'http://fakeajaxconsole/fake_url' def reset_network(self, instance): + """ + Creates uuid arg to pass to make_agent_call and calls it. + + """ args = {'id': str(uuid.uuid4())} resp = self._make_agent_call('resetnetwork', instance, '', args) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent index 07c7e4df9..f99ea4082 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent @@ -94,7 +94,7 @@ def password(self, arg_dict): @jsonify def resetnetwork(self, arg_dict): """ - writes a resquest to xenstore that tells the agent to reset the networking + Writes a resquest to xenstore that tells the agent to reset networking. """ arg_dict['value'] = json.dumps({'name': 'resetnetwork', 'value': ''}) -- cgit From e67927c181a1f24df35a6df5663e397e260979cf Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 17 Feb 2011 13:28:39 -0600 Subject: Foo --- nova/api/openstack/servers.py | 2 +- nova/compute/manager.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 2fc105d07..5f369463d 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -216,7 +216,7 @@ class Controller(wsgi.Controller): def _action_revert_resize(self, input_dict, req, id): try: - self.compute_api.confirm_resize(req.environ['nova.context'], id) + self.compute_api.revert_resize(req.environ['nova.context'], id) except Exception, e: LOG.exception(_("Error in revert-resize %s"), e) return faults.Fault(exc.HTTPBadRequest(e)) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 33fad50fd..4e6532ef4 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -398,7 +398,8 @@ class ComputeManager(manager.Manager): instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_get(context, migration_id) - if migration_ref['source_compute'] == instance_ref['host']: + #TODO(mdietz): we may want to split these into separate methods. + if migration_ref['source_compute'] == FLAGS.host: self.driver.power_on(instance_ref) self.db.migration_update(context, migration_id, { 'status': 'reverted' }) -- cgit From b1fe9a64143505235eb2e3dbe6a6c0966a85ae76 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 17 Feb 2011 14:10:08 -0600 Subject: Stop blowing away the ramdisk --- nova/compute/manager.py | 2 +- nova/virt/xenapi/vmops.py | 29 ++++++++++++++++------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 4e6532ef4..e81ee3148 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -408,7 +408,7 @@ class ComputeManager(manager.Manager): topic = self.db.queue_get_for(context, FLAGS.compute_topic, instance_ref['host']) rpc.cast(context, topic, - { 'method': 'resize_instance', + { 'method': 'revert_resize', 'args': { 'migration_id': migration_ref['id'], 'instance_id': instance_id, diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 13c13d0f6..17b91d27c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -234,7 +234,8 @@ class VMOps(object): return self def __exit__(self, type, value, traceback): - self.virt._destroy(self.instance, self.vm_ref, shutdown=False) + self.virt._destroy(self.instance, self.vm_ref, shutdown=False, + destroy_kernel_ramdisk=False) #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added @@ -393,7 +394,7 @@ class VMOps(object): except self.XenAPI.Failure, exc: LOG.exception(exc) - def _destroy_vm(self, instance, vm): + def _destroy_vm(self, instance, vm, destroy_kernel_ramdisk): """Destroys a VM record """ try: kernel = None @@ -402,16 +403,18 @@ class VMOps(object): (kernel, ramdisk) = VMHelper.lookup_kernel_ramdisk( self._session, vm) task1 = self._session.call_xenapi('Async.VM.destroy', vm) - LOG.debug(_("Removing kernel/ramdisk files")) - fn = "remove_kernel_ramdisk" - args = {} - if kernel: - args['kernel-file'] = kernel - if ramdisk: - args['ramdisk-file'] = ramdisk - task2 = self._session.async_call_plugin('glance', fn, args) + if destroy_kernel_ramdisk: + LOG.debug(_("Removing kernel/ramdisk files")) + fn = "remove_kernel_ramdisk" + args = {} + if kernel: + args['kernel-file'] = kernel + if ramdisk: + args['ramdisk-file'] = ramdisk + task2 = self._session.async_call_plugin('glance', fn, args) self._session.wait_for_task(instance.id, task1) - self._session.wait_for_task(instance.id, task2) + if destroy_kernel_ramdisk: + self._session.wait_for_task(instance.id, task2) LOG.debug(_("kernel/ramdisk files removed")) except self.XenAPI.Failure, exc: LOG.exception(exc) @@ -426,7 +429,7 @@ class VMOps(object): vm = VMHelper.lookup(self._session, instance.name) return self._destroy(instance, vm, shutdown=True) - def _destroy(self, instance, vm, shutdown=True): + def _destroy(self, instance, vm, shutdown=True, destroy_kernel_ramdisk=True): """ Destroys VM instance by performing: @@ -443,7 +446,7 @@ class VMOps(object): self._shutdown(instance, vm) self._destroy_vdis(instance, vm) - self._destroy_vm(instance, vm) + self._destroy_vm(instance, vm, destroy_kernel_ramdisk) def _wait_with_callback(self, instance_id, task, callback): ret = None -- cgit From aa71a25c9f9bf5df3aea781138fa8d69654f06d9 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 17 Feb 2011 12:12:19 -0800 Subject: zone list now comes from scheduler zonemanager --- nova/api/openstack/zones.py | 32 +++++++++++++++-- nova/scheduler/manager.py | 82 ++++-------------------------------------- nova/scheduler/zone_manager.py | 11 +++++- 3 files changed, 46 insertions(+), 79 deletions(-) diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index 16e5e366b..bd2c488d9 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -19,6 +19,7 @@ import logging from nova import flags from nova import wsgi from nova import db +from nova import rpc FLAGS = flags.FLAGS @@ -33,6 +34,10 @@ def _filter_keys(item, keys): return dict((k, v) for k, v in item.iteritems() if k in keys) +def _exclude_keys(item, keys): + return dict((k, v) for k, v in item.iteritems() if k not in keys) + + def _scrub_zone(zone): return _filter_keys(zone, ('id', 'api_url')) @@ -44,11 +49,34 @@ class Controller(wsgi.Controller): "attributes": { "zone": ["id", "api_url", "name", "capabilities"]}}} + def _call_scheduler(self, method, context, params=None): + """Generic handler for RPC calls to the scheduler. + + :param params: Optional dictionary of arguments to be passed to the + scheduler worker + + :retval: Result returned by scheduler worker + """ + if not params: + params = {} + queue = FLAGS.scheduler_topic + kwargs = {'method': method, 'args': params} + return rpc.call(context, queue, kwargs) + def index(self, req): """Return all zones in brief""" - items = db.zone_get_all(req.environ['nova.context']) + # Ask the ZoneManager in the Scheduler for most recent data. + items = self._call_scheduler('get_zone_list', + req.environ['nova.context']) + for item in items: + item['api_url'] = item['api_url'].replace('\\/', '/') + + # Or fall-back to the database ... + if len(items) == 0: + items = db.zone_get_all(req.environ['nova.context']) items = common.limited(items, req) - items = [_scrub_zone(item) for item in items] + items = [_exclude_keys(item, ['username', 'password']) + for item in items] return dict(zones=items) def detail(self, req): diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 693f8cb4b..00a0f4100 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -22,11 +22,6 @@ Scheduler Service """ import functools -import novatools -import thread - -from datetime import datetime -from eventlet.greenpool import GreenPool from nova import db from nova import flags @@ -34,83 +29,14 @@ from nova import log as logging from nova import manager from nova import rpc from nova import utils +from nova.scheduler.zone_manager import ZoneManager LOG = logging.getLogger('nova.scheduler.manager') FLAGS = flags.FLAGS flags.DEFINE_string('scheduler_driver', 'nova.scheduler.chance.ChanceScheduler', 'Driver to use for the scheduler') -flags.DEFINE_integer('zone_db_check_interval', - 60, - 'Seconds between getting fresh zone info from db.') - - -class ZoneState(object): - """Holds the state of all connected child zones.""" - def __init__(self): - self.is_active = True - self.name = None - self.capabilities = None - self.retry = 0 - self.last_seen = datetime.min - - def update(self, zone): - """Update zone credentials from db""" - self.zone_id = zone.id - self.api_url = zone.api_url - self.username = zone.username - self.password = zone.password - - -def _poll_zone(zone): - """Eventlet worker to poll a zone.""" - logging.debug("_POLL_ZONE: STARTING") - os = novatools.OpenStack(zone.username, zone.password, zone.api_url) - zone_metadata = os.zones.info() - logging.debug("_POLL_ZONE: GOT %s" % zone_metadata._info) - - # Stuff this in our cache. - - -class ZoneManager(object): - """Keeps the zone states updated.""" - def __init__(self): - self.last_zone_db_check = datetime.min - self.zone_states = {} - - def _refresh_from_db(self, context): - """Make our zone state map match the db.""" - # Add/update existing zones ... - zones = db.zone_get_all(context) - existing = self.zone_states.keys() - db_keys = [] - for zone in zones: - db_keys.append(zone.id) - if zone.id not in existing: - self.zone_states[zone.id] = ZoneState() - self.zone_states[zone.id].update(zone) - - # Cleanup zones removed from db ... - for zone_id in self.zone_states.keys(): - if zone_id not in db_keys: - del self.zone_states[zone_id] - - def _poll_zones(self, context): - """Try to connect to each child zone and get update.""" - - green_pool = GreenPool() - green_pool.imap(_poll_zone, self.zone_states.values()) - - def ping(self, context=None): - """Ping should be called periodically to update zone status.""" - logging.debug("ZoneManager PING") - diff = datetime.now() - self.last_zone_db_check - if diff.seconds >= FLAGS.zone_db_check_interval: - logging.debug("ZoneManager RECHECKING DB ") - self.last_zone_db_check = datetime.now() - self._refresh_from_db(context) - self._poll_zones(context) - + class SchedulerManager(manager.Manager): """Chooses a host to run instances on.""" @@ -129,6 +55,10 @@ class SchedulerManager(manager.Manager): """Poll child zones periodically to get status.""" self.zone_manager.ping(context) + def get_zone_list(self, context=None): + """Get a list of zones from the ZoneManager.""" + return self.zone_manager.get_zone_list() + def _schedule(self, method, context, topic, *args, **kwargs): """Tries to call schedule_* method on the driver to retrieve host. diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index a35acb000..4fa528973 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -21,6 +21,7 @@ ZoneManager oversees all communications with child Zones. import novatools import thread +import traceback from datetime import datetime from eventlet.greenpool import GreenPool @@ -63,6 +64,11 @@ class ZoneState(object): self.capabilities = zone_metadata["capabilities"] self.is_active = True + def to_dict(self): + return dict(name=self.name, capabilities=self.capabilities, + is_active=self.is_active, api_url=self.api_url, + id=self.zone_id) + def log_error(self, exception): """Something went wrong. Check to see if zone should be marked as offline.""" @@ -91,7 +97,7 @@ def _poll_zone(zone): try: zone.update_metadata(_call_novatools(zone)) except Exception, e: - zone.log_error(e) + zone.log_error(traceback.format_exc()) class ZoneManager(object): @@ -100,6 +106,9 @@ class ZoneManager(object): self.last_zone_db_check = datetime.min self.zone_states = {} + def get_zone_list(self): + return [ zone.to_dict() for zone in self.zone_states.values() ] + def _refresh_from_db(self, context): """Make our zone state map match the db.""" # Add/update existing zones ... -- cgit From e77f8751dd59e5d650d047a6711c3d137947dda7 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 17 Feb 2011 16:18:03 -0400 Subject: fixup --- bin/nova-combined | 4 ++-- nova/api/openstack/zones.py | 6 +++--- nova/flags.py | 2 +- nova/scheduler/manager.py | 2 +- nova/scheduler/zone_manager.py | 18 +++++++++--------- nova/tests/test_zones.py | 10 +++++----- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/bin/nova-combined b/bin/nova-combined index a0f552d64..913c866bf 100755 --- a/bin/nova-combined +++ b/bin/nova-combined @@ -53,11 +53,11 @@ if __name__ == '__main__': compute = service.Service.create(binary='nova-compute') network = service.Service.create(binary='nova-network') - #volume = service.Service.create(binary='nova-volume') + volume = service.Service.create(binary='nova-volume') scheduler = service.Service.create(binary='nova-scheduler') #objectstore = service.Service.create(binary='nova-objectstore') - service.serve(compute, network, scheduler) + service.serve(compute, network, volume, scheduler) apps = [] paste_config_file = wsgi.paste_config_file('nova-api.conf') diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index bd2c488d9..f75176824 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -66,7 +66,7 @@ class Controller(wsgi.Controller): def index(self, req): """Return all zones in brief""" # Ask the ZoneManager in the Scheduler for most recent data. - items = self._call_scheduler('get_zone_list', + items = self._call_scheduler('get_zone_list', req.environ['nova.context']) for item in items: item['api_url'] = item['api_url'].replace('\\/', '/') @@ -75,7 +75,7 @@ class Controller(wsgi.Controller): if len(items) == 0: items = db.zone_get_all(req.environ['nova.context']) items = common.limited(items, req) - items = [_exclude_keys(item, ['username', 'password']) + items = [_exclude_keys(item, ['username', 'password']) for item in items] return dict(zones=items) @@ -85,7 +85,7 @@ class Controller(wsgi.Controller): def info(self, req): """Return name and capabilities for this zone.""" - return dict(zone=dict(name=FLAGS.zone_name, + return dict(zone=dict(name=FLAGS.zone_name, capabilities=FLAGS.zone_capabilities)) def show(self, req, id): diff --git a/nova/flags.py b/nova/flags.py index 0a45499f3..60d7cdd06 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -314,6 +314,6 @@ DEFINE_string('node_availability_zone', 'nova', 'availability zone of this node') DEFINE_string('zone_name', 'nova', 'name of this zone') -DEFINE_string('zone_capabilities', 'xen, linux', +DEFINE_string('zone_capabilities', 'xen, linux', 'comma-delimited list of tags which represent boolean' ' capabilities of this zone') diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 00a0f4100..7ced33b9c 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -36,7 +36,7 @@ FLAGS = flags.FLAGS flags.DEFINE_string('scheduler_driver', 'nova.scheduler.chance.ChanceScheduler', 'Driver to use for the scheduler') - + class SchedulerManager(manager.Manager): """Chooses a host to run instances on.""" diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index 4fa528973..e7c37a9a6 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -47,14 +47,14 @@ class ZoneState(object): self.last_seen = datetime.min self.last_exception = None self.last_exception_time = None - + def update_credentials(self, zone): """Update zone credentials from db""" self.zone_id = zone.id self.api_url = zone.api_url self.username = zone.username self.password = zone.password - + def update_metadata(self, zone_metadata): """Update zone metadata after successful communications with child zone.""" @@ -79,10 +79,10 @@ class ZoneState(object): self.attempt += 1 if self.attempt >= FLAGS.zone_failures_to_offline: - self.is_active = False - logging.error(_("No answer from zone %s after %d " - "attempts. Marking inactive.") % (self.api_url, - FLAGS.zone_failures_to_offline)) + self.is_active = False + logging.error(_("No answer from zone %s after %d " + "attempts. Marking inactive.") % (self.api_url, + FLAGS.zone_failures_to_offline)) def _call_novatools(zone): @@ -107,7 +107,7 @@ class ZoneManager(object): self.zone_states = {} def get_zone_list(self): - return [ zone.to_dict() for zone in self.zone_states.values() ] + return [zone.to_dict() for zone in self.zone_states.values()] def _refresh_from_db(self, context): """Make our zone state map match the db.""" @@ -125,7 +125,7 @@ class ZoneManager(object): for zone_id in self.zone_states.keys(): if zone_id not in db_keys: del self.zone_states[zone_id] - + def _poll_zones(self, context): """Try to connect to each child zone and get update.""" green_pool = GreenPool() @@ -134,7 +134,7 @@ class ZoneManager(object): def ping(self, context=None): """Ping should be called periodically to update zone status.""" diff = datetime.now() - self.last_zone_db_check - if diff.seconds >= FLAGS.zone_db_check_interval: + if diff.seconds >= FLAGS.zone_db_check_interval: logging.debug("Updating zone cache from db.") self.last_zone_db_check = datetime.now() self._refresh_from_db(context) diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py index 2cb070aca..7036ebe58 100644 --- a/nova/tests/test_zones.py +++ b/nova/tests/test_zones.py @@ -67,7 +67,7 @@ class ZoneManagerTestCase(test.TestCase): FakeZone(id=1, api_url='http://foo.com', username='user1', password='pass1'), ]) - + self.assertEquals(len(zm.zone_states), 0) self.mox.ReplayAll() @@ -89,7 +89,7 @@ class ZoneManagerTestCase(test.TestCase): FakeZone(id=1, api_url='http://foo.com', username='user2', password='pass2'), ]) - + self.assertEquals(len(zm.zone_states), 1) self.mox.ReplayAll() @@ -107,8 +107,8 @@ class ZoneManagerTestCase(test.TestCase): zm.zone_states[1] = zone_state self.mox.StubOutWithMock(db, 'zone_get_all') - db.zone_get_all(mox.IgnoreArg()).AndReturn([ ]) - + db.zone_get_all(mox.IgnoreArg()).AndReturn([]) + self.assertEquals(len(zm.zone_states), 1) self.mox.ReplayAll() @@ -125,7 +125,7 @@ class ZoneManagerTestCase(test.TestCase): zm.zone_states[1] = zone_state self.mox.StubOutWithMock(db, 'zone_get_all') - + db.zone_get_all(mox.IgnoreArg()).AndReturn([ FakeZone(id=2, api_url='http://foo.com', username='user2', password='pass2'), -- cgit From 3dd6e369c0aa2e3092eaa32a6b04cbba712ba5ad Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 17 Feb 2011 14:23:20 -0600 Subject: Move the ramdisk logging stuff --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 17b91d27c..f7e434300 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -412,10 +412,10 @@ class VMOps(object): if ramdisk: args['ramdisk-file'] = ramdisk task2 = self._session.async_call_plugin('glance', fn, args) + LOG.debug(_("kernel/ramdisk files removed")) self._session.wait_for_task(instance.id, task1) if destroy_kernel_ramdisk: self._session.wait_for_task(instance.id, task2) - LOG.debug(_("kernel/ramdisk files removed")) except self.XenAPI.Failure, exc: LOG.exception(exc) -- cgit From c2f585952a67aa0c922d7ec80b387e8617587541 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Thu, 17 Feb 2011 21:27:48 +0100 Subject: Re-alphabetise Authors, move extra addressses into .mailmap. --- .mailmap | 46 ++++++++++++++++++++++++++-------------------- Authors | 10 +++++----- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/.mailmap b/.mailmap index a05520884..a839eba6c 100644 --- a/.mailmap +++ b/.mailmap @@ -1,37 +1,43 @@ # Format is: -# - - +# +# + + + + - - + + + + + + - - - - + - + Masumoto + + + - + + + + - + + + + - - - - - - - - + diff --git a/Authors b/Authors index 395c6b9ed..494e614a0 100644 --- a/Authors +++ b/Authors @@ -3,17 +3,17 @@ Anne Gentle Anthony Young Antony Messerli Armando Migliaccio -Brian Waldon Bilal Akhtar Brian Lamar -Brian Schott +Brian Schott +Brian Waldon Chiradeep Vittal Chmouel Boudjnah Chris Behrens Christian Berendt Cory Wright -David Pravec Dan Prince +David Pravec Dean Troyer Devin Carlen Ed Leafe @@ -44,7 +44,7 @@ Monsyne Dragon Monty Taylor MORITA Kazutaka Muneyuki Noguchi -Nachi Ueno +Nachi Ueno Naveed Massjouni Paul Voccio Ricardo Carrillo Cruz @@ -59,7 +59,7 @@ Soren Hansen Thierry Carrez Todd Willey Trey Morris -Tushar Patil +Tushar Patil Vasiliy Shlykov Vishvananda Ishaya Youcef Laribi -- cgit From aa53c9476ed37f0a1359413d4a710eb08c997b06 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 17 Feb 2011 14:42:01 -0600 Subject: Finished flavor OS API stubs --- nova/api/openstack/flavors.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index a3a664506..375e12b18 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -58,21 +58,23 @@ class Controller(wsgi.Controller): def create(self, req): """Create a flavor.""" - instance_types.create( - name, - memory, - vcpus, - local_gb, - flavor_id, - swap, - rxtx_quota, - rxtx_cap) - print "CREATE! %s" % req + #TODO(jk0): Finish this later + #instance_types.create( + # name, + # memory, + # vcpus, + # local_gb, + # flavor_id, + # swap, + # rxtx_quota, + # rxtx_cap) + return "CREATE! %s" % req - def delete(self, req, name): + def delete(self, req, id): """Delete a flavor.""" - instance_type.destroy(name) - print "DELETE! %s %s" % (req, name) + #TODO(jk0): Finish this later + #instance_type.destroy(name) + return "DELETE! %s %s" % (req, id) def _all_ids(self): """Return the list of all flavorids.""" -- cgit From b48201be9a5fa08ce21ef241052071800e5777ca Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 17 Feb 2011 12:43:22 -0800 Subject: fixed zone list tests --- nova/tests/api/openstack/test_zones.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 5542a1cf3..6c06fa8b8 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -52,7 +52,20 @@ def zone_delete(context, zone_id): pass -def zone_get_all(context): +def zone_get_all_scheduler(x, y, z): + return [ + dict(id=1, api_url='http://foo.com', username='bob', + password='xxx'), + dict(id=2, api_url='http://blah.com', username='alice', + password='qwerty') + ] + + +def zone_get_all_scheduler_empty(x, y, z): + return [] + + +def zone_get_all_db(context): return [ dict(id=1, api_url='http://foo.com', username='bob', password='xxx'), @@ -74,7 +87,6 @@ class ZonesTest(unittest.TestCase): FLAGS.allow_admin_api = True self.stubs.Set(nova.db, 'zone_get', zone_get) - self.stubs.Set(nova.db, 'zone_get_all', zone_get_all) self.stubs.Set(nova.db, 'zone_update', zone_update) self.stubs.Set(nova.db, 'zone_create', zone_create) self.stubs.Set(nova.db, 'zone_delete', zone_delete) @@ -83,7 +95,9 @@ class ZonesTest(unittest.TestCase): self.stubs.UnsetAll() FLAGS.allow_admin_api = self.allow_admin - def test_get_zone_list(self): + def test_get_zone_list_scheduler(self): + self.stubs.Set(zones.Controller, '_call_scheduler', + zone_get_all_scheduler) req = webob.Request.blank('/v1.0/zones') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) @@ -91,6 +105,18 @@ class ZonesTest(unittest.TestCase): self.assertEqual(res.status_int, 200) self.assertEqual(len(res_dict['zones']), 2) + def test_get_zone_list_db(self): + self.stubs.Set(zones.Controller, '_call_scheduler', + zone_get_all_scheduler_empty) + self.stubs.Set(nova.db, 'zone_get_all', zone_get_all_db) + req = webob.Request.blank('/v1.0/zones') + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(len(res_dict['zones']), 2) + + def test_get_zone_by_id(self): req = webob.Request.blank('/v1.0/zones/1') res = req.get_response(fakes.wsgi_app()) -- cgit From 8da339d53b4039c3a8e5e8a15ccf1434eeda5fa2 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 17 Feb 2011 15:05:19 -0600 Subject: Fixed unit test --- nova/virt/xenapi/vm_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 339cecd40..7031f2f82 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -141,7 +141,8 @@ class VMHelper(HelperBase): @classmethod def ensure_free_mem(cls, session, instance): - instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] + instance_type = instance_types.get_instance_type( + instance.instance_type) mem = long(instance_type['memory_mb']) * 1024 * 1024 #get free memory from host host = session.get_xenapi_host() -- cgit From 0e3c86dcdc49890eecaa2d1ea64c0012e569682f Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Thu, 17 Feb 2011 22:07:00 +0100 Subject: Use a semaphore to ensure we don't run more than one iptables-restore at a time. --- nova/virt/libvirt_conn.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4e0fd106f..7548fff63 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -46,6 +46,7 @@ from xml.dom import minidom from eventlet import greenthread from eventlet import event +from eventlet import semaphore from eventlet import tpool import IPy @@ -63,6 +64,7 @@ from nova.compute import power_state from nova.virt import disk from nova.virt import images +libvirt_semaphore = semaphore.Semaphore() libvirt = None libxml2 = None Template = None @@ -1237,17 +1239,19 @@ class IptablesFirewallDriver(FirewallDriver): self.apply_ruleset() def apply_ruleset(self): - current_filter, _ = self.execute('sudo iptables-save -t filter') - current_lines = current_filter.split('\n') - new_filter = self.modify_rules(current_lines, 4) - self.execute('sudo iptables-restore', - process_input='\n'.join(new_filter)) - if(FLAGS.use_ipv6): - current_filter, _ = self.execute('sudo ip6tables-save -t filter') + with libvirt_semaphore: + current_filter, _ = self.execute('sudo iptables-save -t filter') current_lines = current_filter.split('\n') - new_filter = self.modify_rules(current_lines, 6) - self.execute('sudo ip6tables-restore', + new_filter = self.modify_rules(current_lines, 4) + self.execute('sudo iptables-restore', process_input='\n'.join(new_filter)) + if(FLAGS.use_ipv6): + current_filter, _ = self.execute('sudo ip6tables-save ' + '-t filter') + current_lines = current_filter.split('\n') + new_filter = self.modify_rules(current_lines, 6) + self.execute('sudo ip6tables-restore', + process_input='\n'.join(new_filter)) def modify_rules(self, current_lines, ip_version=4): ctxt = context.get_admin_context() -- cgit From b9a03524d03c0ce7fa98fab5531db720941bbfdb Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 17 Feb 2011 13:23:56 -0800 Subject: multi positional string fix --- nova/scheduler/zone_manager.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index e7c37a9a6..af0b90f9f 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -74,15 +74,17 @@ class ZoneState(object): marked as offline.""" self.last_exception = exception self.last_exception_time = datetime.now() - logging.warning(_("'%s' error talking to zone %s") % (exception, - self.api_url)) + api_url = self.api_url + logging.warning(_("'%(exception)s' error talking to " + "zone %(api_url)s") % locals()) + max_errors = FLAGS.zone_failures_to_offline: self.attempt += 1 - if self.attempt >= FLAGS.zone_failures_to_offline: + if self.attempt >= max_errors: self.is_active = False - logging.error(_("No answer from zone %s after %d " - "attempts. Marking inactive.") % (self.api_url, - FLAGS.zone_failures_to_offline)) + logging.error(_("No answer from zone %(api_url)s " + "after %(max_errors)d " + "attempts. Marking inactive.") % locals()) def _call_novatools(zone): -- cgit From a33f5495fe261641713131901fee1e83ccc4890f Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 17 Feb 2011 13:29:19 -0800 Subject: fixed strings --- nova/scheduler/zone_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index af0b90f9f..4bf6e36c6 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -78,7 +78,7 @@ class ZoneState(object): logging.warning(_("'%(exception)s' error talking to " "zone %(api_url)s") % locals()) - max_errors = FLAGS.zone_failures_to_offline: + max_errors = FLAGS.zone_failures_to_offline self.attempt += 1 if self.attempt >= max_errors: self.is_active = False -- cgit From 46269872192b843c80d72206a05c8b759c9f66a8 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 17 Feb 2011 17:32:25 -0400 Subject: merge from dev --- nova/tests/api/openstack/test_zones.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 6c06fa8b8..65cc1c023 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -62,7 +62,7 @@ def zone_get_all_scheduler(x, y, z): def zone_get_all_scheduler_empty(x, y, z): - return [] + return [] def zone_get_all_db(context): @@ -106,7 +106,7 @@ class ZonesTest(unittest.TestCase): self.assertEqual(len(res_dict['zones']), 2) def test_get_zone_list_db(self): - self.stubs.Set(zones.Controller, '_call_scheduler', + self.stubs.Set(zones.Controller, '_call_scheduler', zone_get_all_scheduler_empty) self.stubs.Set(nova.db, 'zone_get_all', zone_get_all_db) req = webob.Request.blank('/v1.0/zones') @@ -116,7 +116,6 @@ class ZonesTest(unittest.TestCase): self.assertEqual(res.status_int, 200) self.assertEqual(len(res_dict['zones']), 2) - def test_get_zone_by_id(self): req = webob.Request.blank('/v1.0/zones/1') res = req.get_response(fakes.wsgi_app()) -- cgit From e3f461b3b1087fa6342942daa764ba6ffb9ae383 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Thu, 17 Feb 2011 22:47:02 +0100 Subject: Add **kwargs to VlanManager's create_networks so that optional args from other managers don't break. --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index b906a83ed..6647692ca 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -509,7 +509,7 @@ class VlanManager(NetworkManager): network_ref['bridge']) def create_networks(self, context, cidr, num_networks, network_size, - cidr_v6, vlan_start, vpn_start): + cidr_v6, vlan_start, vpn_start, **kwargs): """Create networks based on parameters.""" # Check that num_networks + vlan_start is not > 4094, fixes lp708025 if num_networks + vlan_start > 4094: -- cgit From 273119957fb3f6cfa72d4357054f6ad1743704e8 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 17 Feb 2011 13:49:36 -0800 Subject: moved 003_cactus.py migration file to 004_add_instance_types.py to avoid naming collision with new trunk migration --- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 86 ---------------------- .../versions/004_add_instance_types.py | 86 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 86 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py deleted file mode 100644 index fec191214..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ /dev/null @@ -1,86 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from sqlalchemy import * -from migrate import * - -from nova import api -from nova import db -from nova import log as logging - -import datetime - -meta = MetaData() - - -# -# New Tables -# -instance_types = Table('instance_types', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('name', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False), - unique=True), - Column('id', Integer(), primary_key=True, nullable=False), - Column('memory_mb', Integer(), nullable=False), - Column('vcpus', Integer(), nullable=False), - Column('local_gb', Integer(), nullable=False), - Column('flavorid', Integer(), nullable=False, unique=True), - Column('swap', Integer(), nullable=False, default=0), - Column('rxtx_quota', Integer(), nullable=False, default=0), - Column('rxtx_cap', Integer(), nullable=False, default=0)) - - -def upgrade(migrate_engine): - # Upgrade operations go here - # Don't create your own engine; bind migrate_engine - # to your metadata - meta.bind = migrate_engine - try: - instance_types.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating instance_types table') - raise - - # Here are the old static instance types - INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - try: - i = instance_types.insert() - for name, values in INSTANCE_TYPES.iteritems(): - # FIXME(kpepple) should we be seeding created_at / updated_at ? - # now = datetime.datatime.utcnow() - i.execute({'name': name, 'memory_mb': values["memory_mb"], - 'vcpus': values["vcpus"], 'deleted': 0, - 'local_gb': values["local_gb"], - 'flavorid': values["flavorid"]}) - except Exception: - logging.info(repr(table)) - logging.exception('Exception while seeding instance_types table') - raise - - -def downgrade(migrate_engine): - # Operations to reverse the above upgrade go here. - for table in (instance_types): - table.drop() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py new file mode 100644 index 000000000..fec191214 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py @@ -0,0 +1,86 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import api +from nova import db +from nova import log as logging + +import datetime + +meta = MetaData() + + +# +# New Tables +# +instance_types = Table('instance_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + unique=True), + Column('id', Integer(), primary_key=True, nullable=False), + Column('memory_mb', Integer(), nullable=False), + Column('vcpus', Integer(), nullable=False), + Column('local_gb', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False, unique=True), + Column('swap', Integer(), nullable=False, default=0), + Column('rxtx_quota', Integer(), nullable=False, default=0), + Column('rxtx_cap', Integer(), nullable=False, default=0)) + + +def upgrade(migrate_engine): + # Upgrade operations go here + # Don't create your own engine; bind migrate_engine + # to your metadata + meta.bind = migrate_engine + try: + instance_types.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating instance_types table') + raise + + # Here are the old static instance types + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + try: + i = instance_types.insert() + for name, values in INSTANCE_TYPES.iteritems(): + # FIXME(kpepple) should we be seeding created_at / updated_at ? + # now = datetime.datatime.utcnow() + i.execute({'name': name, 'memory_mb': values["memory_mb"], + 'vcpus': values["vcpus"], 'deleted': 0, + 'local_gb': values["local_gb"], + 'flavorid': values["flavorid"]}) + except Exception: + logging.info(repr(table)) + logging.exception('Exception while seeding instance_types table') + raise + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + for table in (instance_types): + table.drop() -- cgit From 60ed73265a52f264021bb7452cde9f83181b3dfc Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 17 Feb 2011 17:51:24 -0400 Subject: copyright notice --- nova/api/openstack/servers.py | 2 -- nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index c7f863764..009ef6db1 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2010 OpenStack LLC. # All Rights Reserved. # diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py index d2b6b9570..ade981687 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py @@ -1,5 +1,4 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. +# Copyright 2010 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may -- cgit From e5443fa3e436a95de9d1c353e8772436c7cba8b6 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 17 Feb 2011 17:58:38 -0400 Subject: pip requires novatools --- tools/pip-requires | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/pip-requires b/tools/pip-requires index 3587df644..a5d4675be 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -10,6 +10,7 @@ boto==1.9b carrot==0.10.5 eventlet==0.9.12 lockfile==0.8 +python-novatools==2.0 python-daemon==1.5.5 python-gflags==1.3 redis==2.0.0 -- cgit From f6f0135bb320de3cde093f48cb3189380c173b12 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 17 Feb 2011 14:14:07 -0800 Subject: Correctly pass the associate paramater to project_get_network --- nova/db/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/api.py b/nova/db/api.py index 52c2bb84d..d7f3746d2 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -576,7 +576,7 @@ def project_get_network(context, project_id, associate=True): """ - return IMPL.project_get_network(context, project_id) + return IMPL.project_get_network(context, project_id, associate) def project_get_network_v6(context, project_id): -- cgit From fe576e28a6ed8e15d4cdb96313d9f58426715bb0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 17 Feb 2011 14:39:36 -0800 Subject: move periodic tasks to base class based on class variable as per review --- nova/network/manager.py | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index a4a4c6064..78b7f0ae1 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -110,6 +110,7 @@ class NetworkManager(manager.Manager): This class must be subclassed to support specific topologies. """ + timeout_fixed_ips = True def __init__(self, network_driver=None, *args, **kwargs): if not network_driver: @@ -138,6 +139,19 @@ class NetworkManager(manager.Manager): self.driver.ensure_floating_forward(floating_ip['address'], fixed_address) + def periodic_tasks(self, context=None): + """Tasks to be run at a periodic interval.""" + super(NetworkManager, self).periodic_tasks(context) + if self.timeout_fixed_ips: + now = utils.utcnow() + timeout = FLAGS.fixed_ip_disassociate_timeout + time = now - datetime.timedelta(seconds=timeout) + num = self.db.fixed_ip_disassociate_all_by_timeout(context, + self.host, + time) + if num: + LOG.debug(_("Dissassociated %s stale fixed ip(s)"), num) + def set_network_host(self, context, network_id): """Safely sets the host of the network.""" LOG.debug(_("setting network host"), context=context) @@ -306,6 +320,7 @@ class FlatManager(NetworkManager): not do any setup in this mode, it must be done manually. Requests to 169.254.169.254 port 80 will need to be forwarded to the api server. """ + timeout_fixed_ips = False def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Gets a fixed ip from the pool.""" @@ -397,14 +412,7 @@ class FlatDHCPManager(FlatManager): def periodic_tasks(self, context=None): """Tasks to be run at a periodic interval.""" super(FlatDHCPManager, self).periodic_tasks(context) - now = datetime.datetime.utcnow() - timeout = FLAGS.fixed_ip_disassociate_timeout - time = now - datetime.timedelta(seconds=timeout) - num = self.db.fixed_ip_disassociate_all_by_timeout(context, - self.host, - time) - if num: - LOG.debug(_("Dissassociated %s stale fixed ip(s)"), num) + self._disassociate_old_ips(context) def init_host(self): """Do any initialization that needs to be run if this is a @@ -463,18 +471,6 @@ class VlanManager(NetworkManager): instances in its subnet. """ - def periodic_tasks(self, context=None): - """Tasks to be run at a periodic interval.""" - super(VlanManager, self).periodic_tasks(context) - now = datetime.datetime.utcnow() - timeout = FLAGS.fixed_ip_disassociate_timeout - time = now - datetime.timedelta(seconds=timeout) - num = self.db.fixed_ip_disassociate_all_by_timeout(context, - self.host, - time) - if num: - LOG.debug(_("Dissassociated %s stale fixed ip(s)"), num) - def init_host(self): """Do any initialization that needs to be run if this is a standalone service. -- cgit From e5d979596ff8c588c7bbe82b7f1cb90de8af041a Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 17 Feb 2011 18:49:30 -0400 Subject: missing docstring and fixed copyrights --- nova/scheduler/zone_manager.py | 3 +-- nova/tests/test_zones.py | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index 4bf6e36c6..3e7c1eba8 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -1,6 +1,4 @@ # Copyright (c) 2010 Openstack, LLC. -# 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 @@ -109,6 +107,7 @@ class ZoneManager(object): self.zone_states = {} def get_zone_list(self): + """Return the list of zones we know about.""" return [zone.to_dict() for zone in self.zone_states.values()] def _refresh_from_db(self, context): diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py index 7036ebe58..c273230a9 100644 --- a/nova/tests/test_zones.py +++ b/nova/tests/test_zones.py @@ -1,5 +1,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 -- cgit From eef994eb690a9454e187a2b0cdbde85aba4c55cd Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 17 Feb 2011 14:50:29 -0800 Subject: remove leftover periodic tasks --- nova/network/manager.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 78b7f0ae1..9f37d966a 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -409,11 +409,6 @@ class FlatDHCPManager(FlatManager): like FlatDHCPManager. """ - def periodic_tasks(self, context=None): - """Tasks to be run at a periodic interval.""" - super(FlatDHCPManager, self).periodic_tasks(context) - self._disassociate_old_ips(context) - def init_host(self): """Do any initialization that needs to be run if this is a standalone service. -- cgit From 3f3dddee0245cb143004dfb8c20204c511bec658 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 17 Feb 2011 16:52:31 -0600 Subject: a few changes and a bunch of unit tests --- nova/api/openstack/servers.py | 16 ++-- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 60 ------------- .../versions/004_add_instance_migrations.py | 60 +++++++++++++ nova/tests/api/openstack/common.py | 30 +++++++ nova/tests/api/openstack/test_servers.py | 98 +++++++++++++++++++++- 5 files changed, 197 insertions(+), 67 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py create mode 100644 nova/tests/api/openstack/common.py diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index fd6b10d5b..a719f5e15 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -227,7 +227,7 @@ class Controller(wsgi.Controller): self.compute_api.confirm_resize(req.environ['nova.context'], id) except Exception, e: LOG.exception(_("Error in confirm-resize %s"), e) - return faults.Fault(exc.HTTPBadRequest(e)) + return faults.Fault(exc.HTTPBadRequest()) return exc.HTTPNoContent() def _action_revert_resize(self, input_dict, req, id): @@ -235,7 +235,7 @@ class Controller(wsgi.Controller): self.compute_api.revert_resize(req.environ['nova.context'], id) except Exception, e: LOG.exception(_("Error in revert-resize %s"), e) - return faults.Fault(exc.HTTPBadRequest(e)) + return faults.Fault(exc.HTTPBadRequest()) return exc.HTTPAccepted() def _action_rebuild(self, input_dict, req, id): @@ -244,12 +244,16 @@ class Controller(wsgi.Controller): def _action_resize(self, input_dict, req, id): """ Resizes a given instance to the flavor size requested """ try: - flavor_id = input_dict['resize']['flavorId'] - self.compute_api.resize(req.environ['nova.context'], id, - flavor_id) + if 'resize' in input_dict and 'flavorId' in input_dict['resize']: + flavor_id = input_dict['resize']['flavorId'] + self.compute_api.resize(req.environ['nova.context'], id, + flavor_id) + else: + LOG.exception(_("Missing arguments for resize")) + return faults.Fault(exc.HTTPUnprocessableEntity()) except Exception, e: LOG.exception(_("Error in resize %s"), e) - return faults.Fault(exc.HTTPUnprocessableEntity(e)) + return faults.Fault(exc.HTTPBadRequest()) return faults.Fault(exc.HTTPAccepted()) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py deleted file mode 100644 index 4aab5bdc6..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ /dev/null @@ -1,60 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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.from sqlalchemy import * - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - -# -# New Tables -# - -migrations = Table('migrations', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('source_compute', String(255)), - Column('dest_compute', String(255)), - Column('dest_host', String(255)), - Column('instance_id', Integer, ForeignKey('instances.id'), - nullable=True), - Column('status', String(255)) - ) - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (migrations, ): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py new file mode 100644 index 000000000..4aab5bdc6 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py @@ -0,0 +1,60 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 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.from sqlalchemy import * + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + +# +# New Tables +# + +migrations = Table('migrations', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('source_compute', String(255)), + Column('dest_compute', String(255)), + Column('dest_host', String(255)), + Column('instance_id', Integer, ForeignKey('instances.id'), + nullable=True), + Column('status', String(255)) + ) + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (migrations, ): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise diff --git a/nova/tests/api/openstack/common.py b/nova/tests/api/openstack/common.py new file mode 100644 index 000000000..b55d3087b --- /dev/null +++ b/nova/tests/api/openstack/common.py @@ -0,0 +1,30 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. + +import json + +import webob + +def webob_factory(url): + base_url = url + def web_request(url, method, body=None): + req = webob.Request.blank("%s%s" % (base_url, url)) + req.method = method + req.body = json.dumps(body) + return req + return web_request + diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index a7be0796e..878b62f85 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1,6 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2010 OpenStack LLC. +# Copyright 2010-2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -29,6 +29,7 @@ from nova.api.openstack import servers import nova.db.api from nova.db.sqlalchemy.models import Instance import nova.rpc +from nova.tests.api.openstack import common from nova.tests.api.openstack import fakes @@ -138,6 +139,8 @@ class ServersTest(unittest.TestCase): self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api) self.allow_admin = FLAGS.allow_admin_api + self.webreq = common.webob_factor('/v1.0/servers') + def tearDown(self): self.stubs.UnsetAll() FLAGS.allow_admin_api = self.allow_admin @@ -411,6 +414,99 @@ class ServersTest(unittest.TestCase): self.assertEqual(res.status, '202 Accepted') self.assertEqual(self.server_delete_called, True) + def test_resize_server(self): + req = self.webreq('/1/action', 'POST', dict(resize={flavorId=3}})) + res = req.get_response(fakes.wsgi_app()) + + self.resize_called = False + def resize_mock(context, inst_id, flavor): + self.resize_called = True + + self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) + self.assertEqual(res.status_int, 202) + self.assertEqual(self.resize_called, True) + + def test_resize_bad_param_fails(self): + req = self.webreq('/1/action', 'POST', dict('ferp')) + res = req.get_response(fakes.wsgi_app()) + + self.resize_called = False + def resize_mock(context, inst_id, flavor): + self.resize_called = True + + self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) + self.assertEqual(res.status_int, 422) + self.assertEqual(self.resize_called, False) + + def test_resize_bad_flavor_fails(self): + req = self.webreq('/1/action', 'POST', dict(resize={derp=3})) + res = req.get_response(fakes.wsgi_app()) + + self.resize_called = False + def resize_mock(context, inst_id, flavor): + self.resize_called = True + + self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) + self.assertEqual(res.status_int, 422) + self.assertEqual(self.resize_called, False) + + def test_resize_raises_fails(self): + req = self.webreq('/1/action', 'POST', dict(resize={flavorId=3})) + res = req.get_response(fakes.wsgi_app()) + + def resize_mock(context, inst_id, flavor): + raise Exception, 'hurr durr' + + self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) + self.assertEqual(res.status_int, 500) + + def test_confirm_resize_server(self): + req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) + res = req.get_response(fakes.wsgi_app()) + + self.resize_called = False + def confirm_resize_mock(context, inst_id): + self.resize_called = True + + self.stubs.Set(nova.compute.api, 'confirm_resize', + confirm_resize_mock) + self.assertEqual(res.status_int, 202) + self.assertEqual(self.resize_called, True) + + def test_confirm_resize_server_fails(self): + req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) + res = req.get_response(fakes.wsgi_app()) + + def confirm_resize_mock(context, inst_id): + raise Exception, 'hurr durr' + + self.stubs.Set(nova.compute.api, 'confirm_resize', + confirm_resize_mock) + self.assertEqual(res.status_int, 500) + + def test_revert_resize_server(self): + req = self.webreq('/1/action', 'POST', dict(revertResize=None)) + res = req.get_response(fakes.wsgi_app()) + + self.resize_called = False + def revert_resize_mock(context, inst_id): + self.resize_called = True + + self.stubs.Set(nova.compute.api, 'revert_resize', + confirm_resize_mock) + self.assertEqual(res.status_int, 202) + self.assertEqual(self.resize_called, True) + + def test_revert_resize_server_fails(self): + req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) + res = req.get_response(fakes.wsgi_app()) + + def revert_resize_mock(context, inst_id): + raise Exception, 'hurr durr' + + self.stubs.Set(nova.compute.api, 'revert_resize', + confirm_resize_mock) + self.assertEqual(res.status_int, 500) if __name__ == "__main__": unittest.main() -- cgit From 9a7213b615bcaa2127f76146d594f5247ea0d0a4 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Thu, 17 Feb 2011 15:00:18 -0800 Subject: Initial support for per-instance metadata, though the OpenStack API. Key/value pairs can be specified at instance creation time and are returned in the details view. Support limits based on quota system. --- nova/api/ec2/cloud.py | 6 +- nova/api/openstack/servers.py | 30 +++++++-- nova/compute/api.py | 29 +++++++- nova/db/sqlalchemy/api.py | 2 + .../versions/004_add_instance_metadata.py | 78 ++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 18 ++++- nova/quota.py | 14 +++- nova/tests/api/openstack/test_servers.py | 11 ++- nova/tests/test_quota.py | 24 +++++++ run_tests.sh | 4 +- 10 files changed, 202 insertions(+), 14 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_metadata.py diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 6919cd8d2..33eba5028 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -783,6 +783,9 @@ class CloudController(object): def run_instances(self, context, **kwargs): max_count = int(kwargs.get('max_count', 1)) + # NOTE(justinsb): the EC2 API doesn't support metadata here, but this + # is needed for the unit tests. Maybe the unit tests shouldn't be + # calling the EC2 code instances = self.compute_api.create(context, instance_type=instance_types.get_by_type( kwargs.get('instance_type', None)), @@ -797,7 +800,8 @@ class CloudController(object): user_data=kwargs.get('user_data'), security_group=kwargs.get('security_group'), availability_zone=kwargs.get('placement', {}).get( - 'AvailabilityZone')) + 'AvailabilityZone'), + metadata=kwargs.get('metadata', [])) return self._format_run_instances(context, instances[0]['reservation_id']) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 009ef6db1..49611703a 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -78,9 +78,14 @@ def _translate_detail_keys(inst): except KeyError: LOG.debug(_("Failed to read public ip(s)")) - inst_dict['metadata'] = {} inst_dict['hostId'] = '' + # Return the metadata as a dictionary + metadata = {} + for item in inst['metadata']: + metadata[item['key']] = item['value'] + inst_dict['metadata'] = metadata + return dict(server=inst_dict) @@ -162,14 +167,26 @@ class Controller(wsgi.Controller): if not env: return faults.Fault(exc.HTTPUnprocessableEntity()) - key_pair = auth_manager.AuthManager.get_key_pairs( - req.environ['nova.context'])[0] + context = req.environ['nova.context'] + + key_pair = auth_manager.AuthManager.get_key_pairs(context)[0] image_id = common.get_image_id_from_image_hash(self._image_service, - req.environ['nova.context'], env['server']['imageId']) + context, env['server']['imageId']) kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image( req, image_id) + + # Metadata is a list, not a Dictionary, because we allow duplicate keys + # (even though JSON can't encode this) + # In future, we may not allow duplicate keys. + # However, the CloudServers API is not definitive on this front, + # and we want to be compatible. + metadata = [] + if env['server']['metadata']: + for k, v in env['server']['metadata'].items(): + metadata.append({'key': k, 'value': v}) + instances = self.compute_api.create( - req.environ['nova.context'], + context, instance_types.get_by_flavor_id(env['server']['flavorId']), image_id, kernel_id=kernel_id, @@ -177,7 +194,8 @@ class Controller(wsgi.Controller): display_name=env['server']['name'], display_description=env['server']['name'], key_name=key_pair['name'], - key_data=key_pair['public_key']) + key_data=key_pair['public_key'], + metadata=metadata) return _translate_keys(instances[0]) def update(self, req, id): diff --git a/nova/compute/api.py b/nova/compute/api.py index ed6f0e34a..cad167f4d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -85,7 +85,7 @@ class API(base.Base): min_count=1, max_count=1, display_name='', display_description='', key_name=None, key_data=None, security_group='default', - availability_zone=None, user_data=None): + availability_zone=None, user_data=None, metadata=[]): """Create the number of instances requested if quota and other arguments check out ok.""" @@ -99,6 +99,30 @@ class API(base.Base): "run %s more instances of this type.") % num_instances, "InstanceLimitExceeded") + num_metadata = len(metadata) + quota_metadata = quota.allowed_metadata_items(context, num_metadata) + if quota_metadata < num_metadata: + pid = context.project_id + msg = (_("Quota exceeeded for %(pid)s," + " tried to set %(num_metadata)s metadata properties") + % locals()) + LOG.warn(msg) + raise quota.QuotaError(msg, "MetadataLimitExceeded") + + # Because metadata is stored in the DB, we hard-code the size limits + # In future, we may support more variable length strings, so we act + # as if this is quota-controlled for forwards compatibility + for metadata_item in metadata: + k = metadata_item['key'] + v = metadata_item['value'] + if len(k) > 255 or len(v) > 255: + pid = context.project_id + msg = (_("Quota exceeeded for %(pid)s," + " metadata property key or value too long") + % locals()) + LOG.warn(msg) + raise quota.QuotaError(msg, "MetadataLimitExceeded") + is_vpn = image_id == FLAGS.vpn_image_id if not is_vpn: image = self.image_service.show(context, image_id) @@ -155,7 +179,8 @@ class API(base.Base): 'key_name': key_name, 'key_data': key_data, 'locked': False, - 'availability_zone': availability_zone} + 'availability_zone': availability_zone, + 'metadata': metadata} elevated = context.elevated() instances = [] diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 2697fac73..a6b8066b9 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -715,6 +715,7 @@ def instance_get(context, instance_id, session=None): options(joinedload_all('security_groups.rules')).\ options(joinedload('volumes')).\ options(joinedload_all('fixed_ip.network')).\ + options(joinedload('metadata')).\ filter_by(id=instance_id).\ filter_by(deleted=can_read_deleted(context)).\ first() @@ -723,6 +724,7 @@ def instance_get(context, instance_id, session=None): options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload_all('security_groups.rules')).\ options(joinedload('volumes')).\ + options(joinedload('metadata')).\ filter_by(project_id=context.project_id).\ filter_by(id=instance_id).\ filter_by(deleted=False).\ diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_metadata.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_metadata.py new file mode 100644 index 000000000..4cb07e0d8 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_metadata.py @@ -0,0 +1,78 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Justin Santa Barbara +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + +quotas = Table('quotas', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +# +# New Tables +# + +instance_metadata_table = Table('instance_metadata', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('instance_id', + Integer(), + ForeignKey('instances.id'), + nullable=False), + Column('key', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('value', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False))) + + +# +# New columns +# +quota_metadata_items = Column('metadata_items', Integer()) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (instance_metadata_table, ): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise + + quotas.create_column(quota_metadata_items) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 40a96fc17..a842e4cc4 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -256,6 +256,7 @@ class Quota(BASE, NovaBase): volumes = Column(Integer) gigabytes = Column(Integer) floating_ips = Column(Integer) + metadata_items = Column(Integer) class ExportDevice(BASE, NovaBase): @@ -536,6 +537,20 @@ class Console(BASE, NovaBase): pool = relationship(ConsolePool, backref=backref('consoles')) +class InstanceMetadata(BASE, NovaBase): + """Represents a metadata key/value pair for an instance""" + __tablename__ = 'instance_metadata' + id = Column(Integer, primary_key=True) + key = Column(String(255)) + value = Column(String(255)) + instance_id = Column(Integer, ForeignKey('instances.id'), nullable=False) + instance = relationship(Instance, backref="metadata", + foreign_keys=instance_id, + primaryjoin='and_(' + 'InstanceMetadata.instance_id == Instance.id,' + 'InstanceMetadata.deleted == False)') + + class Zone(BASE, NovaBase): """Represents a child zone of this zone.""" __tablename__ = 'zones' @@ -557,7 +572,8 @@ def register_models(): Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, - Project, Certificate, ConsolePool, Console, Zone) + Project, Certificate, ConsolePool, Console, Zone, + InstanceMetadata) engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: model.metadata.create_all(engine) diff --git a/nova/quota.py b/nova/quota.py index 3884eb308..6b52a97fa 100644 --- a/nova/quota.py +++ b/nova/quota.py @@ -35,6 +35,8 @@ flags.DEFINE_integer('quota_gigabytes', 1000, 'number of volume gigabytes allowed per project') flags.DEFINE_integer('quota_floating_ips', 10, 'number of floating ips allowed per project') +flags.DEFINE_integer('quota_metadata_items', 128, + 'number of metadata items allowed per instance') def get_quota(context, project_id): @@ -42,7 +44,8 @@ def get_quota(context, project_id): 'cores': FLAGS.quota_cores, 'volumes': FLAGS.quota_volumes, 'gigabytes': FLAGS.quota_gigabytes, - 'floating_ips': FLAGS.quota_floating_ips} + 'floating_ips': FLAGS.quota_floating_ips, + 'metadata_items': FLAGS.quota_metadata_items} try: quota = db.quota_get(context, project_id) for key in rval.keys(): @@ -94,6 +97,15 @@ def allowed_floating_ips(context, num_floating_ips): return min(num_floating_ips, allowed_floating_ips) +def allowed_metadata_items(context, num_metadata_items): + """Check quota; return min(num_metadata_items,allowed_metadata_items)""" + project_id = context.project_id + context = context.elevated() + quota = get_quota(context, project_id) + num_allowed_metadata_items = quota['metadata_items'] + return min(num_metadata_items, num_allowed_metadata_items) + + class QuotaError(exception.ApiError): """Quota Exceeeded""" pass diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index a7be0796e..7eb81c2b8 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -28,6 +28,7 @@ import nova.api.openstack from nova.api.openstack import servers import nova.db.api from nova.db.sqlalchemy.models import Instance +from nova.db.sqlalchemy.models import InstanceMetadata import nova.rpc from nova.tests.api.openstack import fakes @@ -64,6 +65,9 @@ def instance_address(context, instance_id): def stub_instance(id, user_id=1, private_address=None, public_addresses=None): + metadata = [] + metadata.append(InstanceMetadata(key='seq', value=id)) + if public_addresses == None: public_addresses = list() @@ -95,7 +99,8 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None): "availability_zone": "", "display_name": "server%s" % id, "display_description": "", - "locked": False} + "locked": False, + "metadata": metadata} instance["fixed_ip"] = { "address": private_address, @@ -214,7 +219,8 @@ class ServersTest(unittest.TestCase): "get_image_id_from_image_hash", image_id_from_hash) body = dict(server=dict( - name='server_test', imageId=2, flavorId=2, metadata={}, + name='server_test', imageId=2, flavorId=2, + metadata={'hello': 'world', 'open': 'stack'}, personality={})) req = webob.Request.blank('/v1.0/servers') req.method = 'POST' @@ -291,6 +297,7 @@ class ServersTest(unittest.TestCase): self.assertEqual(s['id'], i) self.assertEqual(s['name'], 'server%d' % i) self.assertEqual(s['imageId'], 10) + self.assertEqual(s['metadata']['seq'], i) i += 1 def test_server_pause(self): diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 9548a8c13..36ccc273e 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -87,6 +87,18 @@ class QuotaTestCase(test.TestCase): num_instances = quota.allowed_instances(self.context, 100, instance_types.INSTANCE_TYPES['m1.small']) self.assertEqual(num_instances, 10) + + # metadata_items + too_many_items = FLAGS.quota_metadata_items + 1000 + num_metadata_items = quota.allowed_metadata_items(self.context, + too_many_items) + self.assertEqual(num_metadata_items, FLAGS.quota_metadata_items) + db.quota_update(self.context, self.project.id, {'metadata_items': 5}) + num_metadata_items = quota.allowed_metadata_items(self.context, + too_many_items) + self.assertEqual(num_metadata_items, 5) + + # Cleanup db.quota_destroy(self.context, self.project.id) def test_too_many_instances(self): @@ -151,3 +163,15 @@ class QuotaTestCase(test.TestCase): self.assertRaises(quota.QuotaError, self.cloud.allocate_address, self.context) db.floating_ip_destroy(context.get_admin_context(), address) + + def test_too_many_metadata_items(self): + metadata = {} + for i in range(FLAGS.quota_metadata_items + 1): + metadata['key%s' % i] = 'value%s' % i + self.assertRaises(quota.QuotaError, self.cloud.run_instances, + self.context, + min_count=1, + max_count=1, + instance_type='m1.small', + image_id='fake', + metadata=metadata) diff --git a/run_tests.sh b/run_tests.sh index 4e21fe945..58e92c06b 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -73,7 +73,9 @@ fi if [ -z "$noseargs" ]; then - run_tests && pep8 --repeat --show-pep8 --show-source --exclude=vcsversion.py bin/* nova setup.py || exit 1 + srcfiles=`find bin -type f ! -name "nova.conf*"` + srcfiles+=" nova setup.py" + run_tests && pep8 --repeat --show-pep8 --show-source --exclude=vcsversion.py ${srcfiles} || exit 1 else run_tests fi -- cgit From 94f5a5748158e61bde43327970dd8e513ca36575 Mon Sep 17 00:00:00 2001 From: Nirmal Ranganathan Date: Thu, 17 Feb 2011 17:13:59 -0600 Subject: Always compare incoming flavor_id as an int --- nova/compute/instance_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 309313fd0..7a2a5baa3 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -45,6 +45,6 @@ def get_by_type(instance_type): def get_by_flavor_id(flavor_id): for instance_type, details in INSTANCE_TYPES.iteritems(): - if details['flavorid'] == flavor_id: + if details['flavorid'] == int(flavor_id): return instance_type return FLAGS.default_instance_type -- cgit From 88aa545b53d96c25da01218c79e8be8c1ae3370f Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Thu, 17 Feb 2011 23:55:56 +0000 Subject: Test changes --- nova/tests/api/openstack/test_servers.py | 82 +++++++++++++++----------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 878b62f85..665551c55 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -26,6 +26,7 @@ from nova import db from nova import flags import nova.api.openstack from nova.api.openstack import servers +import nova.compute.api import nova.db.api from nova.db.sqlalchemy.models import Instance import nova.rpc @@ -139,7 +140,7 @@ class ServersTest(unittest.TestCase): self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api) self.allow_admin = FLAGS.allow_admin_api - self.webreq = common.webob_factor('/v1.0/servers') + self.webreq = common.webob_factory('/v1.0/servers') def tearDown(self): self.stubs.UnsetAll() @@ -415,98 +416,93 @@ class ServersTest(unittest.TestCase): self.assertEqual(self.server_delete_called, True) def test_resize_server(self): - req = self.webreq('/1/action', 'POST', dict(resize={flavorId=3}})) - res = req.get_response(fakes.wsgi_app()) + req = self.webreq('/1/action', 'POST', dict(resize=dict(flavorId=3))) self.resize_called = False - def resize_mock(context, inst_id, flavor): + def resize_mock(*args): self.resize_called = True - self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) + self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) + + res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) self.assertEqual(self.resize_called, True) - def test_resize_bad_param_fails(self): - req = self.webreq('/1/action', 'POST', dict('ferp')) - res = req.get_response(fakes.wsgi_app()) + def test_resize_bad_flavor_fails(self): + req = self.webreq('/1/action', 'POST', dict(resize=dict(derp=3))) self.resize_called = False - def resize_mock(context, inst_id, flavor): + def resize_mock(*args): self.resize_called = True - self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) - self.assertEqual(res.status_int, 422) - self.assertEqual(self.resize_called, False) + self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) - def test_resize_bad_flavor_fails(self): - req = self.webreq('/1/action', 'POST', dict(resize={derp=3})) res = req.get_response(fakes.wsgi_app()) - - self.resize_called = False - def resize_mock(context, inst_id, flavor): - self.resize_called = True - - self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) self.assertEqual(res.status_int, 422) self.assertEqual(self.resize_called, False) def test_resize_raises_fails(self): - req = self.webreq('/1/action', 'POST', dict(resize={flavorId=3})) - res = req.get_response(fakes.wsgi_app()) + req = self.webreq('/1/action', 'POST', dict(resize=dict(flavorId=3))) - def resize_mock(context, inst_id, flavor): + def resize_mock(*args): raise Exception, 'hurr durr' - self.stubs.Set(nova.compute.api, 'resize_instance', resize_mock) - self.assertEqual(res.status_int, 500) + self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) def test_confirm_resize_server(self): req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) - res = req.get_response(fakes.wsgi_app()) self.resize_called = False - def confirm_resize_mock(context, inst_id): + def confirm_resize_mock(*args): self.resize_called = True - self.stubs.Set(nova.compute.api, 'confirm_resize', + self.stubs.Set(nova.compute.api.API, 'confirm_resize', confirm_resize_mock) - self.assertEqual(res.status_int, 202) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 204) self.assertEqual(self.resize_called, True) def test_confirm_resize_server_fails(self): req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) - res = req.get_response(fakes.wsgi_app()) - def confirm_resize_mock(context, inst_id): + def confirm_resize_mock(*args): raise Exception, 'hurr durr' - self.stubs.Set(nova.compute.api, 'confirm_resize', + self.stubs.Set(nova.compute.api.API, 'confirm_resize', confirm_resize_mock) - self.assertEqual(res.status_int, 500) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) def test_revert_resize_server(self): req = self.webreq('/1/action', 'POST', dict(revertResize=None)) - res = req.get_response(fakes.wsgi_app()) self.resize_called = False - def revert_resize_mock(context, inst_id): + def revert_resize_mock(*args): self.resize_called = True - self.stubs.Set(nova.compute.api, 'revert_resize', - confirm_resize_mock) + self.stubs.Set(nova.compute.api.API, 'revert_resize', + revert_resize_mock) + + res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) self.assertEqual(self.resize_called, True) def test_revert_resize_server_fails(self): - req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) - res = req.get_response(fakes.wsgi_app()) + req = self.webreq('/1/action', 'POST', dict(revertResize=None)) - def revert_resize_mock(context, inst_id): + def revert_resize_mock(*args): raise Exception, 'hurr durr' - self.stubs.Set(nova.compute.api, 'revert_resize', - confirm_resize_mock) - self.assertEqual(res.status_int, 500) + self.stubs.Set(nova.compute.api.API, 'revert_resize', + revert_resize_mock) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) if __name__ == "__main__": unittest.main() -- cgit From f0d58ea141116ccfd1c977b0f3e5fc669c0ea8a9 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 17 Feb 2011 18:06:34 -0600 Subject: moved inject network info to a function which accepts only instance, and call it from reset network --- nova/virt/xenapi/vmops.py | 96 +++++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 842e08f22..d1ef95c6c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -104,46 +104,6 @@ class VMOps(object): instance, kernel, ramdisk, pv_kernel) VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True) - # write network info - admin_context = context.get_admin_context() - - # TODO(tr3buchet) - remove comment in multi-nic - # I've decided to go ahead and consider multiple IPs and networks - # at this stage even though they aren't implemented because these will - # be needed for multi-nic and there was no sense writing it for single - # network/single IP and then having to turn around and re-write it - IPs = db.fixed_ip_get_all_by_instance(admin_context, instance['id']) - for network in db.network_get_all_by_instance(admin_context, - instance['id']): - network_IPs = [ip for ip in IPs if ip.network_id == network.id] - - def ip_dict(ip): - return {'netmask': network['netmask'], - 'enabled': '1', - 'ip': ip.address} - - mac_id = instance.mac_address.replace(':', '') - location = 'vm-data/networking/%s' % mac_id - mapping = {'label': network['label'], - 'gateway': network['gateway'], - 'mac': instance.mac_address, - 'dns': [network['dns']], - 'ips': [ip_dict(ip) for ip in network_IPs]} - self.write_to_param_xenstore(vm_ref, {location: mapping}) - - # TODO(tr3buchet) - remove comment in multi-nic - # this bit here about creating the vifs will be updated - # in multi-nic to handle multiple IPs on the same network - # and multiple networks - # for now it works as there is only one of each - bridge = network['bridge'] - network_ref = \ - NetworkHelper.find_network_with_bridge(self._session, bridge) - - if network_ref: - VMHelper.create_vif(self._session, vm_ref, - network_ref, instance.mac_address) - LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) instance_name = instance.name @@ -174,7 +134,7 @@ class VMOps(object): timer.f = _wait_for_boot - # call reset networking + # call to reset network to inject network info and configure self.reset_network(instance) return timer.start(interval=0.5, now=True) @@ -438,11 +398,65 @@ class VMOps(object): # TODO: implement this! return 'http://fakeajaxconsole/fake_url' + def inject_network_info(self, instance): + """ + Generate the network info and make calls to place it into the + xenstore and the xenstore param list + + """ + # TODO(tr3buchet) - remove comment in multi-nic + # I've decided to go ahead and consider multiple IPs and networks + # at this stage even though they aren't implemented because these will + # be needed for multi-nic and there was no sense writing it for single + # network/single IP and then having to turn around and re-write it + vm_opaque_ref = self._get_vm_opaque_ref(instance.id) + logging.debug(_("injecting network info to xenstore for vm: |%s|"), + vm_opaque_ref) + admin_context = context.get_admin_context() + IPs = db.fixed_ip_get_all_by_instance(admin_context, instance['id']) + for network in db.network_get_all_by_instance(admin_context, + instance['id']): + network_IPs = [ip for ip in IPs if ip.network_id == network.id] + + def ip_dict(ip): + return {'netmask': network['netmask'], + 'enabled': '1', + 'ip': ip.address} + + mac_id = instance.mac_address.replace(':', '') + location = 'vm-data/networking/%s' % mac_id + mapping = {'label': network['label'], + 'gateway': network['gateway'], + 'mac': instance.mac_address, + 'dns': [network['dns']], + 'ips': [ip_dict(ip) for ip in network_IPs]} + self.write_to_param_xenstore(vm_opaque_ref, {location: mapping}) + try: + self.write_to_xenstore(vm_opaque_ref, location, + mapping['location']) + except KeyError: + # catch KeyError for domid if instance isn't running + pass + + # TODO(tr3buchet) - remove comment in multi-nic + # this bit here about creating the vifs will be updated + # in multi-nic to handle multiple IPs on the same network + # and multiple networks + # for now it works as there is only one of each + bridge = network['bridge'] + network_ref = \ + NetworkHelper.find_network_with_bridge(self._session, bridge) + + if network_ref: + VMHelper.create_vif(self._session, vm_opaque_ref, + network_ref, instance.mac_address) + def reset_network(self, instance): """ Creates uuid arg to pass to make_agent_call and calls it. """ + self.inject_network_info(instance) args = {'id': str(uuid.uuid4())} resp = self._make_agent_call('resetnetwork', instance, '', args) -- cgit From 9991f23957c07493d503c9667a6920e1235ef8a1 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 17 Feb 2011 16:07:00 -0800 Subject: completed doc and added --purge option to instance type delete --- bin/nova-manage | 11 ++++++++--- doc/source/adminguide/managing.instance.types.rst | 10 +++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 21de11474..3318a593e 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -713,16 +713,21 @@ class InstanceTypeCommands(object): else: print "%s created" % name - def delete(self, name): + def delete(self, name, purge=None): """Marks instance types / flavors as deleted arguments: name""" try: - instance_types.destroy(name) + if purge == "--purge": + instance_types.purge(name) + verb = "deleted" + else: + instance_types.destroy(name) + verb = "purged" except exception.ApiError: print "Valid instance type name is required" sys.exit(1) else: - print "%s deleted" % name + print "%s %s" % (name, verb) def list(self, name=None): """Lists all or specific instance types / flavors diff --git a/doc/source/adminguide/managing.instance.types.rst b/doc/source/adminguide/managing.instance.types.rst index ff4e2b087..30a47e47d 100644 --- a/doc/source/adminguide/managing.instance.types.rst +++ b/doc/source/adminguide/managing.instance.types.rst @@ -36,7 +36,7 @@ the "flavor" command as a synonym for "instance_types". To see all currently active instance types, use the list subcommand:: - $ nova-manage instance_type list + # nova-manage instance_type list m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB @@ -46,7 +46,7 @@ To see all currently active instance types, use the list subcommand:: By default, the list subcommand only shows active instance types. To see all instance types (even those deleted), add the argument 1 after the list subcommand like so:: - $ nova-manage instance_type list 1 + # nova-manage instance_type list 1 m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB @@ -76,4 +76,8 @@ To delete an instance type, use the "delete" subcommand and specify the name:: Please note that the "delete" command only marks the instance type as inactive in the database; it does not actually remove the instance type. This is done to preserve the instance type definition for long running instances (which may not -terminate for months or years). +terminate for months or years). If you are sure that you want to delete this instance +type from the database, pass the "--purge" flag after the name:: + + # nova-manage instance_type delete m1.xxlarge --purge + m1.xxlarge deleted -- cgit From ff0ef603fd3f87ad9294260d13ea3c122bab387f Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 17 Feb 2011 16:22:04 -0800 Subject: changed migration to 006 for trunk compatibility --- .../versions/004_add_instance_types.py | 86 ---------------------- .../versions/006_add_instance_types.py | 86 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 86 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py deleted file mode 100644 index fec191214..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_types.py +++ /dev/null @@ -1,86 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from sqlalchemy import * -from migrate import * - -from nova import api -from nova import db -from nova import log as logging - -import datetime - -meta = MetaData() - - -# -# New Tables -# -instance_types = Table('instance_types', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('name', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False), - unique=True), - Column('id', Integer(), primary_key=True, nullable=False), - Column('memory_mb', Integer(), nullable=False), - Column('vcpus', Integer(), nullable=False), - Column('local_gb', Integer(), nullable=False), - Column('flavorid', Integer(), nullable=False, unique=True), - Column('swap', Integer(), nullable=False, default=0), - Column('rxtx_quota', Integer(), nullable=False, default=0), - Column('rxtx_cap', Integer(), nullable=False, default=0)) - - -def upgrade(migrate_engine): - # Upgrade operations go here - # Don't create your own engine; bind migrate_engine - # to your metadata - meta.bind = migrate_engine - try: - instance_types.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating instance_types table') - raise - - # Here are the old static instance types - INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - try: - i = instance_types.insert() - for name, values in INSTANCE_TYPES.iteritems(): - # FIXME(kpepple) should we be seeding created_at / updated_at ? - # now = datetime.datatime.utcnow() - i.execute({'name': name, 'memory_mb': values["memory_mb"], - 'vcpus': values["vcpus"], 'deleted': 0, - 'local_gb': values["local_gb"], - 'flavorid': values["flavorid"]}) - except Exception: - logging.info(repr(table)) - logging.exception('Exception while seeding instance_types table') - raise - - -def downgrade(migrate_engine): - # Operations to reverse the above upgrade go here. - for table in (instance_types): - table.drop() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py new file mode 100644 index 000000000..fec191214 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py @@ -0,0 +1,86 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import api +from nova import db +from nova import log as logging + +import datetime + +meta = MetaData() + + +# +# New Tables +# +instance_types = Table('instance_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + unique=True), + Column('id', Integer(), primary_key=True, nullable=False), + Column('memory_mb', Integer(), nullable=False), + Column('vcpus', Integer(), nullable=False), + Column('local_gb', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False, unique=True), + Column('swap', Integer(), nullable=False, default=0), + Column('rxtx_quota', Integer(), nullable=False, default=0), + Column('rxtx_cap', Integer(), nullable=False, default=0)) + + +def upgrade(migrate_engine): + # Upgrade operations go here + # Don't create your own engine; bind migrate_engine + # to your metadata + meta.bind = migrate_engine + try: + instance_types.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating instance_types table') + raise + + # Here are the old static instance types + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + try: + i = instance_types.insert() + for name, values in INSTANCE_TYPES.iteritems(): + # FIXME(kpepple) should we be seeding created_at / updated_at ? + # now = datetime.datatime.utcnow() + i.execute({'name': name, 'memory_mb': values["memory_mb"], + 'vcpus': values["vcpus"], 'deleted': 0, + 'local_gb': values["local_gb"], + 'flavorid': values["flavorid"]}) + except Exception: + logging.info(repr(table)) + logging.exception('Exception while seeding instance_types table') + raise + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + for table in (instance_types): + table.drop() -- cgit From 2da6494d20624177c0077d0709e1fdb0e5f8f03c Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Thu, 17 Feb 2011 17:42:49 -0800 Subject: pep8 --- nova/tests/api/openstack/test_zones.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 5542a1cf3..df497ef1b 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -57,8 +57,7 @@ def zone_get_all(context): dict(id=1, api_url='http://foo.com', username='bob', password='xxx'), dict(id=2, api_url='http://blah.com', username='alice', - password='qwerty') - ] + password='qwerty')] class ZonesTest(unittest.TestCase): -- cgit From 4b51ec3e9bca7421c66816c77c43396e51e68ea6 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 17 Feb 2011 23:09:06 -0600 Subject: Tests --- nova/tests/api/openstack/common.py | 8 +++++--- nova/tests/test_compute.py | 7 +++++++ nova/virt/fake.py | 13 +++++++++++++ nova/virt/xenapi_conn.py | 8 -------- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/nova/tests/api/openstack/common.py b/nova/tests/api/openstack/common.py index b55d3087b..66207cddc 100644 --- a/nova/tests/api/openstack/common.py +++ b/nova/tests/api/openstack/common.py @@ -21,10 +21,12 @@ import webob def webob_factory(url): base_url = url - def web_request(url, method, body=None): + def web_request(url, method=None, body=None): req = webob.Request.blank("%s%s" % (base_url, url)) - req.method = method - req.body = json.dumps(body) + if method: + req.method = method + if body: + req.body = json.dumps(body) return req return web_request diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 2aa0690e7..e27e08827 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -258,3 +258,10 @@ class ComputeTestCase(test.TestCase): self.assertEqual(ret_val, None) self.compute.terminate_instance(self.context, instance_id) + + def test_resize_instance(self): + """Ensure instance can be migrated/resized""" + instance_id = self._create_instance() + self.compute.run_instnce(self.context, instance_id) + self.compute.prep_resize(self.context, instance_id) + diff --git a/nova/virt/fake.py b/nova/virt/fake.py index ff5e22603..da86df6d4 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -176,6 +176,19 @@ class FakeConnection(object): """ 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. diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index aafd836e2..be018b47f 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -184,14 +184,6 @@ class XenAPIConnection(object): """Unpause paused VM instance""" self._vmops.unpause(instance, callback) - def power_off(self, instance): - """Shuts down a running VM instance""" - self._vmops._shutdown(instance, method='clean') - - def power_on(self, instance): - """powers on a powered off VM instance""" - self._vmops.power_on(instance) - 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""" -- cgit From 671766cb4ada59b0e575b395b5afff82950ddb76 Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Fri, 18 Feb 2011 06:03:15 +0000 Subject: Resize compute tests --- nova/tests/test_compute.py | 24 +++++++++++++++++++++--- nova/virt/fake.py | 6 ++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index e27e08827..3f2e64c87 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -56,7 +56,7 @@ class ComputeTestCase(test.TestCase): self.manager.delete_project(self.project) super(ComputeTestCase, self).tearDown() - def _create_instance(self): + def _create_instance(self, params={}): """Create a test instance""" inst = {} inst['image_id'] = 'ami-test' @@ -67,6 +67,7 @@ class ComputeTestCase(test.TestCase): inst['instance_type'] = 'm1.tiny' inst['mac_address'] = utils.generate_mac() inst['ami_launch_index'] = 0 + inst.update(params) return db.instance_create(self.context, inst)['id'] def _create_group(self): @@ -262,6 +263,23 @@ class ComputeTestCase(test.TestCase): def test_resize_instance(self): """Ensure instance can be migrated/resized""" instance_id = self._create_instance() - self.compute.run_instnce(self.context, instance_id) - self.compute.prep_resize(self.context, instance_id) + context = self.context.elevated() + self.compute.run_instance(self.context, instance_id) + db.instance_update(self.context, instance_id, {'host':'foo'}) + + self.compute.prep_resize(context, instance_id) + migration_ref = db.migration_get_by_instance_and_status(context, + instance_id, 'pre-migrating') + self.compute.resize_instance(context, instance_id, + migration_ref['id']) + self.compute.terminate_instance(context, instance_id) + + def test_resize_same_source_fails(self): + """Ensure instance fails to migrate when source and destination are + the same host""" + instance_id = self._create_instance() + self.compute.run_instance(self.context, instance_id) + self.assertRaises(exception.Error, self.compute.prep_resize, + self.context, instance_id) + self.compute.terminate_instance(self.context, instance_id) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index da86df6d4..9106ebf03 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -139,6 +139,12 @@ class FakeConnection(object): """ pass + def get_host_ip_addr(self): + """ + Retrieves the IP address of the dom0 + """ + pass + def resize(self, instance, flavor): """ Resizes/Migrates the specified instance. -- cgit From 4673afddcb5a1069f75fb3493e43498ed1de11f9 Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Fri, 18 Feb 2011 02:23:30 -0500 Subject: Incorporating minor cleanups suggested by Rick Harris: * Use assertNotEqual instead of assertTrue * Use enumerate function instead of maintaining a counter --- nova/tests/api/openstack/test_servers.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 630e1e5eb..4ed13022b 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -259,15 +259,13 @@ class ServersTest(unittest.TestCase): server_list = res_dict['servers'] host_ids = [server_list[0]['hostId'], server_list[1]['hostId']] self.assertTrue(host_ids[0] and host_ids[1]) - self.assertTrue(host_ids[0] != host_ids[1]) + self.assertNotEqual(host_ids[0], host_ids[1]) - i = 0 - for s in res_dict['servers']: + for i, s in enumerate(res_dict['servers']): self.assertEqual(s['id'], i) self.assertEqual(s['hostId'], host_ids[i % 2]) self.assertEqual(s['name'], 'server%d' % i) self.assertEqual(s['imageId'], 10) - i += 1 def test_server_pause(self): FLAGS.allow_admin_api = True -- cgit From 982ac6b348981fa26ef6b70b8673da45477a6b36 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 18 Feb 2011 10:24:55 +0100 Subject: Use WatchedFileHandler instead of RotatingFileHandler. --- nova/log.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/log.py b/nova/log.py index 87a6dd51b..6b201ffcc 100644 --- a/nova/log.py +++ b/nova/log.py @@ -94,7 +94,7 @@ critical = logging.critical log = logging.log # handlers StreamHandler = logging.StreamHandler -RotatingFileHandler = logging.handlers.RotatingFileHandler +WatchedFileHandler = logging.handlers.WatchedFileHandler # logging.SysLogHandler is nicer than logging.logging.handler.SysLogHandler. SysLogHandler = logging.handlers.SysLogHandler @@ -139,7 +139,7 @@ def basicConfig(): logging.root.addHandler(syslog) logpath = get_log_file_path() if logpath: - logfile = RotatingFileHandler(logpath) + logfile = WatchedFileHandler(logpath) logfile.setFormatter(_formatter) logging.root.addHandler(logfile) -- cgit From debfca945627323c160b4ad9aa9b63b364deff99 Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Fri, 18 Feb 2011 16:02:55 +0100 Subject: Switch to API_listen and API_listen_port, drop wsgi.paste_config_to_flags --- bin/nova-api | 18 ++++++++++++------ etc/nova-api.conf | 3 --- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/bin/nova-api b/bin/nova-api index eb59d0191..e7ee6f6fe 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -47,6 +47,12 @@ FLAGS = flags.FLAGS API_ENDPOINTS = ['ec2', 'osapi'] +for api in API_ENDPOINTS: + flags.DEFINE_string("%s_api_listen" % api, "0.0.0.0", + "IP address to listen to for API %s" % api) + flags.DEFINE_integer("%s_api_listen_port" % api, + getattr(FLAGS, "%s_port" % api), + "Port to listen to for API %s" % api) def run_app(paste_config_file): LOG.debug(_("Using paste.deploy config at: %s"), paste_config_file) @@ -57,14 +63,10 @@ def run_app(paste_config_file): LOG.debug(_("No paste configuration for app: %s"), api) continue LOG.debug(_("App Config: %(api)s\n%(config)r") % locals()) - wsgi.paste_config_to_flags(config, { - "verbose": FLAGS.verbose, - "%s_host" % api: getattr(FLAGS, "%s_host" % api), - "%s_port" % api: getattr(FLAGS, "%s_port" % api)}) LOG.info(_("Running %s API"), api) app = wsgi.load_paste_app(paste_config_file, api) - apps.append((app, getattr(FLAGS, "%s_port" % api), - config.get('listen', '0.0.0.0'))) + apps.append((app, getattr(FLAGS, "%s_api_listen_port" % api), + getattr(FLAGS, "%s_api_listen" % api))) if len(apps) == 0: LOG.error(_("No known API applications configured in %s."), paste_config_file) @@ -82,6 +84,10 @@ if __name__ == '__main__': FLAGS(sys.argv) LOG.audit(_("Starting nova-api node (version %s)"), version.version_string_with_vcs()) + LOG.debug(_("Full set of FLAGS:")) + for flag in FLAGS: + flag_get = FLAGS.get(flag, None) + LOG.debug("%(flag)s : %(flag_get)s" % locals()) conf = wsgi.paste_config_file('nova-api.conf') if conf: run_app(conf) diff --git a/etc/nova-api.conf b/etc/nova-api.conf index f0e749805..9f7e93d4c 100644 --- a/etc/nova-api.conf +++ b/etc/nova-api.conf @@ -1,6 +1,3 @@ -[DEFAULT] -verbose = 1 - ####### # EC2 # ####### -- cgit From a0145eed239a7afb545def17f25a08e8e4c68824 Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Fri, 18 Feb 2011 16:08:33 +0100 Subject: Set up logging once FLAGS properly read, no need to redo logging config anymore (was inoperant anyway) --- bin/nova-api | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/bin/nova-api b/bin/nova-api index e7ee6f6fe..46f695248 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -39,10 +39,6 @@ from nova import log as logging from nova import version from nova import wsgi -logging.basicConfig() -LOG = logging.getLogger('nova.api') -LOG.setLevel(logging.DEBUG) - FLAGS = flags.FLAGS API_ENDPOINTS = ['ec2', 'osapi'] @@ -72,8 +68,6 @@ def run_app(paste_config_file): paste_config_file) return - # NOTE(todd): redo logging config, verbose could be set in paste config - logging.basicConfig() server = wsgi.Server() for app in apps: server.start(*app) @@ -82,6 +76,8 @@ def run_app(paste_config_file): if __name__ == '__main__': FLAGS(sys.argv) + logging.basicConfig() + LOG = logging.getLogger('nova.api') LOG.audit(_("Starting nova-api node (version %s)"), version.version_string_with_vcs()) LOG.debug(_("Full set of FLAGS:")) -- cgit From 27c2de313a41bced77f7a4769deae089a70f5385 Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Fri, 18 Feb 2011 16:21:14 +0100 Subject: Port changes to nova-combined, rename flags to API_listen and API_listen_port --- bin/nova-api | 8 ++++---- bin/nova-combined | 20 ++++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/bin/nova-api b/bin/nova-api index 46f695248..8d47a656e 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -44,9 +44,9 @@ FLAGS = flags.FLAGS API_ENDPOINTS = ['ec2', 'osapi'] for api in API_ENDPOINTS: - flags.DEFINE_string("%s_api_listen" % api, "0.0.0.0", + flags.DEFINE_string("%s_listen" % api, "0.0.0.0", "IP address to listen to for API %s" % api) - flags.DEFINE_integer("%s_api_listen_port" % api, + flags.DEFINE_integer("%s_listen_port" % api, getattr(FLAGS, "%s_port" % api), "Port to listen to for API %s" % api) @@ -61,8 +61,8 @@ def run_app(paste_config_file): LOG.debug(_("App Config: %(api)s\n%(config)r") % locals()) LOG.info(_("Running %s API"), api) app = wsgi.load_paste_app(paste_config_file, api) - apps.append((app, getattr(FLAGS, "%s_api_listen_port" % api), - getattr(FLAGS, "%s_api_listen" % api))) + apps.append((app, getattr(FLAGS, "%s_listen_port" % api), + getattr(FLAGS, "%s_listen" % api))) if len(apps) == 0: LOG.error(_("No known API applications configured in %s."), paste_config_file) diff --git a/bin/nova-combined b/bin/nova-combined index 889600eb7..40dc2945d 100755 --- a/bin/nova-combined +++ b/bin/nova-combined @@ -45,6 +45,14 @@ from nova import wsgi FLAGS = flags.FLAGS +API_ENDPOINTS = ['ec2', 'osapi'] + +for api in API_ENDPOINTS: + flags.DEFINE_string("%s_listen" % api, "0.0.0.0", + "IP address to listen to for API %s" % api) + flags.DEFINE_integer("%s_listen_port" % api, + getattr(FLAGS, "%s_port" % api), + "Port to listen to for API %s" % api) if __name__ == '__main__': utils.default_flagfile() @@ -57,21 +65,17 @@ if __name__ == '__main__': scheduler = service.Service.create(binary='nova-scheduler') #objectstore = service.Service.create(binary='nova-objectstore') - service.serve(compute, network, volume, scheduler) +# service.serve(compute, network, volume, scheduler) apps = [] paste_config_file = wsgi.paste_config_file('nova-api.conf') - for api in ['osapi', 'ec2']: + for api in API_ENDPOINTS: config = wsgi.load_paste_configuration(paste_config_file, api) if config is None: continue - wsgi.paste_config_to_flags(config, { - "verbose": FLAGS.verbose, - "%s_host" % api: getattr(FLAGS, "%s_host" % api), - "%s_port" % api: getattr(FLAGS, "%s_port" % api)}) app = wsgi.load_paste_app(paste_config_file, api) - apps.append((app, getattr(FLAGS, "%s_port" % api), - config.get('listen', '0.0.0.0'))) + apps.append((app, getattr(FLAGS, "%s_listen_port" % api), + getattr(FLAGS, "%s_listen" % api))) if len(apps) > 0: logging.basicConfig() server = wsgi.Server() -- cgit From a02af158f781b61dba67c454afb59b34f27ca5d7 Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Fri, 18 Feb 2011 16:23:15 +0100 Subject: Remove paste_config_to_flags since it's now unused --- nova/wsgi.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/nova/wsgi.py b/nova/wsgi.py index e01cc1e1e..d9b1cae86 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -515,10 +515,3 @@ def load_paste_app(filename, appname): except LookupError: pass return app - - -def paste_config_to_flags(config, mixins): - for k, v in mixins.iteritems(): - value = config.get(k, v) - converted_value = FLAGS[k].parser.Parse(value) - setattr(FLAGS, k, converted_value) -- cgit From 15d140992a980d959be6633b59d178a5ea273cdc Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Fri, 18 Feb 2011 16:23:52 +0100 Subject: PEP8 fix --- bin/nova-api | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/nova-api b/bin/nova-api index 8d47a656e..1228f723c 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -50,6 +50,7 @@ for api in API_ENDPOINTS: getattr(FLAGS, "%s_port" % api), "Port to listen to for API %s" % api) + def run_app(paste_config_file): LOG.debug(_("Using paste.deploy config at: %s"), paste_config_file) apps = [] -- cgit From bf570ca5f199091d505d96b91a3dc3acfbfc9fc7 Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Fri, 18 Feb 2011 16:37:00 +0100 Subject: Fixed testing mode leftover --- bin/nova-combined | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-combined b/bin/nova-combined index 40dc2945d..dd21c8df4 100755 --- a/bin/nova-combined +++ b/bin/nova-combined @@ -65,7 +65,7 @@ if __name__ == '__main__': scheduler = service.Service.create(binary='nova-scheduler') #objectstore = service.Service.create(binary='nova-objectstore') -# service.serve(compute, network, volume, scheduler) + service.serve(compute, network, volume, scheduler) apps = [] paste_config_file = wsgi.paste_config_file('nova-api.conf') -- cgit From c884064e7a9af04b2ebdbbb9ee32318a00716412 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Fri, 18 Feb 2011 12:08:35 -0400 Subject: fixups backed on merge comments --- nova/api/openstack/zones.py | 30 +++++---------------- nova/flags.py | 5 ++-- nova/scheduler/api.py | 49 ++++++++++++++++++++++++++++++++++ nova/scheduler/zone_manager.py | 15 ++++++----- nova/tests/api/openstack/test_zones.py | 33 +++++++++++------------ 5 files changed, 82 insertions(+), 50 deletions(-) create mode 100644 nova/scheduler/api.py diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index f75176824..24a4444f7 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -1,4 +1,4 @@ -# Copyright 2010 OpenStack LLC. +# Copyright 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -20,6 +20,7 @@ from nova import flags from nova import wsgi from nova import db from nova import rpc +from nova.scheduler.api import API FLAGS = flags.FLAGS @@ -49,31 +50,14 @@ class Controller(wsgi.Controller): "attributes": { "zone": ["id", "api_url", "name", "capabilities"]}}} - def _call_scheduler(self, method, context, params=None): - """Generic handler for RPC calls to the scheduler. - - :param params: Optional dictionary of arguments to be passed to the - scheduler worker - - :retval: Result returned by scheduler worker - """ - if not params: - params = {} - queue = FLAGS.scheduler_topic - kwargs = {'method': method, 'args': params} - return rpc.call(context, queue, kwargs) - def index(self, req): """Return all zones in brief""" - # Ask the ZoneManager in the Scheduler for most recent data. - items = self._call_scheduler('get_zone_list', - req.environ['nova.context']) - for item in items: - item['api_url'] = item['api_url'].replace('\\/', '/') - - # Or fall-back to the database ... - if len(items) == 0: + # Ask the ZoneManager in the Scheduler for most recent data, + # or fall-back to the database ... + items = API().get_zone_list(req.environ['nova.context']) + if not items: items = db.zone_get_all(req.environ['nova.context']) + items = common.limited(items, req) items = [_exclude_keys(item, ['username', 'password']) for item in items] diff --git a/nova/flags.py b/nova/flags.py index 7e4919d6e..41f01fcd7 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -316,6 +316,5 @@ DEFINE_string('node_availability_zone', 'nova', 'availability zone of this node') DEFINE_string('zone_name', 'nova', 'name of this zone') -DEFINE_string('zone_capabilities', 'xen, linux', - 'comma-delimited list of tags which represent boolean' - ' capabilities of this zone') +DEFINE_string('zone_capabilities', 'kypervisor:xenserver;os:linux', + 'Key/Value tags which represent capabilities of this zone') diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py new file mode 100644 index 000000000..8491bf3a9 --- /dev/null +++ b/nova/scheduler/api.py @@ -0,0 +1,49 @@ +# Copyright (c) 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. + +""" +Handles all requests relating to schedulers. +""" + +from nova import flags +from nova import log as logging +from nova import rpc + +FLAGS = flags.FLAGS +LOG = logging.getLogger('nova.scheduler.api') + + +class API: + """API for interacting with the scheduler.""" + + def _call_scheduler(self, method, context, params=None): + """Generic handler for RPC calls to the scheduler. + + :param params: Optional dictionary of arguments to be passed to the + scheduler worker + + :retval: Result returned by scheduler worker + """ + if not params: + params = {} + queue = FLAGS.scheduler_topic + kwargs = {'method': method, 'args': params} + return rpc.call(context, queue, kwargs) + + def get_zone_list(self, context): + items = self._call_scheduler('get_zone_list', context) + for item in items: + item['api_url'] = item['api_url'].replace('\\/', '/') + return items diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index 3e7c1eba8..783783d06 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Openstack, LLC. +# Copyright (c) 2011 Openstack, LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -87,8 +87,8 @@ class ZoneState(object): def _call_novatools(zone): """Call novatools. Broken out for testing purposes.""" - os = novatools.OpenStack(zone.username, zone.password, zone.api_url) - return os.zones.info()._info + client = novatools.OpenStack(zone.username, zone.password, zone.api_url) + return client.zones.info()._info def _poll_zone(zone): @@ -105,6 +105,7 @@ class ZoneManager(object): def __init__(self): self.last_zone_db_check = datetime.min self.zone_states = {} + self.green_pool = GreenPool() def get_zone_list(self): """Return the list of zones we know about.""" @@ -123,20 +124,20 @@ class ZoneManager(object): self.zone_states[zone.id].update_credentials(zone) # Cleanup zones removed from db ... - for zone_id in self.zone_states.keys(): + keys = self.zone_states.keys() # since we're deleting + for zone_id in keys: if zone_id not in db_keys: del self.zone_states[zone_id] def _poll_zones(self, context): """Try to connect to each child zone and get update.""" - green_pool = GreenPool() - green_pool.imap(_poll_zone, self.zone_states.values()) + self.green_pool.imap(_poll_zone, self.zone_states.values()) def ping(self, context=None): """Ping should be called periodically to update zone status.""" diff = datetime.now() - self.last_zone_db_check if diff.seconds >= FLAGS.zone_db_check_interval: - logging.debug("Updating zone cache from db.") + logging.debug(_("Updating zone cache from db.")) self.last_zone_db_check = datetime.now() self._refresh_from_db(context) self._poll_zones(context) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 65cc1c023..4df7c7feb 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -1,4 +1,4 @@ -# Copyright 2010 OpenStack LLC. +# Copyright 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -24,6 +24,7 @@ from nova import context from nova import flags from nova.api.openstack import zones from nova.tests.api.openstack import fakes +from nova.scheduler.api import API FLAGS = flags.FLAGS @@ -31,7 +32,7 @@ FLAGS.verbose = True def zone_get(context, zone_id): - return dict(id=1, api_url='http://foo.com', username='bob', + return dict(id=1, api_url='http://example.com', username='bob', password='xxx') @@ -42,7 +43,7 @@ def zone_create(context, values): def zone_update(context, zone_id, values): - zone = dict(id=zone_id, api_url='http://foo.com', username='bob', + zone = dict(id=zone_id, api_url='http://example.com', username='bob', password='xxx') zone.update(values) return zone @@ -52,24 +53,24 @@ def zone_delete(context, zone_id): pass -def zone_get_all_scheduler(x, y, z): +def zone_get_all_scheduler(*args): return [ - dict(id=1, api_url='http://foo.com', username='bob', + dict(id=1, api_url='http://example.com', username='bob', password='xxx'), - dict(id=2, api_url='http://blah.com', username='alice', + dict(id=2, api_url='http://example.org', username='alice', password='qwerty') ] -def zone_get_all_scheduler_empty(x, y, z): +def zone_get_all_scheduler_empty(*args): return [] def zone_get_all_db(context): return [ - dict(id=1, api_url='http://foo.com', username='bob', + dict(id=1, api_url='http://example.com', username='bob', password='xxx'), - dict(id=2, api_url='http://blah.com', username='alice', + dict(id=2, api_url='http://example.org', username='alice', password='qwerty') ] @@ -96,8 +97,7 @@ class ZonesTest(unittest.TestCase): FLAGS.allow_admin_api = self.allow_admin def test_get_zone_list_scheduler(self): - self.stubs.Set(zones.Controller, '_call_scheduler', - zone_get_all_scheduler) + self.stubs.Set(API, '_call_scheduler', zone_get_all_scheduler) req = webob.Request.blank('/v1.0/zones') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) @@ -106,8 +106,7 @@ class ZonesTest(unittest.TestCase): self.assertEqual(len(res_dict['zones']), 2) def test_get_zone_list_db(self): - self.stubs.Set(zones.Controller, '_call_scheduler', - zone_get_all_scheduler_empty) + self.stubs.Set(API, '_call_scheduler', zone_get_all_scheduler_empty) self.stubs.Set(nova.db, 'zone_get_all', zone_get_all_db) req = webob.Request.blank('/v1.0/zones') res = req.get_response(fakes.wsgi_app()) @@ -122,7 +121,7 @@ class ZonesTest(unittest.TestCase): res_dict = json.loads(res.body) self.assertEqual(res_dict['zone']['id'], 1) - self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') + self.assertEqual(res_dict['zone']['api_url'], 'http://example.com') self.assertFalse('password' in res_dict['zone']) self.assertEqual(res.status_int, 200) @@ -133,7 +132,7 @@ class ZonesTest(unittest.TestCase): self.assertEqual(res.status_int, 200) def test_zone_create(self): - body = dict(zone=dict(api_url='http://blah.zoo', username='fred', + body = dict(zone=dict(api_url='http://example.com', username='fred', password='fubar')) req = webob.Request.blank('/v1.0/zones') req.method = 'POST' @@ -144,7 +143,7 @@ class ZonesTest(unittest.TestCase): self.assertEqual(res.status_int, 200) self.assertEqual(res_dict['zone']['id'], 1) - self.assertEqual(res_dict['zone']['api_url'], 'http://blah.zoo') + self.assertEqual(res_dict['zone']['api_url'], 'http://example.com') self.assertFalse('username' in res_dict['zone']) def test_zone_update(self): @@ -158,7 +157,7 @@ class ZonesTest(unittest.TestCase): self.assertEqual(res.status_int, 200) self.assertEqual(res_dict['zone']['id'], 1) - self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') + self.assertEqual(res_dict['zone']['api_url'], 'http://example.com') self.assertFalse('username' in res_dict['zone']) -- cgit From cd533e160e9c98a0c14b4e0bc32a6e94c7ab8657 Mon Sep 17 00:00:00 2001 From: Nirmal Ranganathan Date: Fri, 18 Feb 2011 11:44:38 -0600 Subject: Added Author and tests --- Authors | 1 + nova/tests/test_compute.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/Authors b/Authors index 494e614a0..1d2316cf5 100644 --- a/Authors +++ b/Authors @@ -46,6 +46,7 @@ MORITA Kazutaka Muneyuki Noguchi Nachi Ueno Naveed Massjouni +Nirmal Ranganathan Paul Voccio Ricardo Carrillo Cruz Rick Clark diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index b049ac943..e338a7cfa 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -30,6 +30,7 @@ from nova import log as logging from nova import test from nova import utils from nova.auth import manager +from nova.compute import instance_types LOG = logging.getLogger('nova.tests.compute') @@ -266,3 +267,11 @@ class ComputeTestCase(test.TestCase): self.assertEqual(ret_val, None) self.compute.terminate_instance(self.context, instance_id) + + def test_get_by_flavor_id(self): + type = instance_types.get_by_flavor_id(1) + self.assertEqual(type, 'm1.tiny') + + type = instance_types.get_by_flavor_id("1") + self.assertEqual(type, 'm1.tiny') + -- cgit From bef44d7621db516a0f5d407655f5e76adfd5c06d Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 18 Feb 2011 10:14:56 -0800 Subject: Rename migration 004 => 005 --- .../versions/004_add_instance_metadata.py | 78 ---------------------- .../versions/005_add_instance_metadata.py | 78 ++++++++++++++++++++++ 2 files changed, 78 insertions(+), 78 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_metadata.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/005_add_instance_metadata.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_metadata.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_metadata.py deleted file mode 100644 index 4cb07e0d8..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_metadata.py +++ /dev/null @@ -1,78 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Justin Santa Barbara -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - -quotas = Table('quotas', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -# -# New Tables -# - -instance_metadata_table = Table('instance_metadata', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('instance_id', - Integer(), - ForeignKey('instances.id'), - nullable=False), - Column('key', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('value', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False))) - - -# -# New columns -# -quota_metadata_items = Column('metadata_items', Integer()) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (instance_metadata_table, ): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise - - quotas.create_column(quota_metadata_items) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/005_add_instance_metadata.py b/nova/db/sqlalchemy/migrate_repo/versions/005_add_instance_metadata.py new file mode 100644 index 000000000..4cb07e0d8 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/005_add_instance_metadata.py @@ -0,0 +1,78 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Justin Santa Barbara +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + +quotas = Table('quotas', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +# +# New Tables +# + +instance_metadata_table = Table('instance_metadata', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('instance_id', + Integer(), + ForeignKey('instances.id'), + nullable=False), + Column('key', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('value', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False))) + + +# +# New columns +# +quota_metadata_items = Column('metadata_items', Integer()) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (instance_metadata_table, ): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise + + quotas.create_column(quota_metadata_items) -- cgit From f66840390bf6f4660a091663d306417e760735bc Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 18 Feb 2011 10:57:19 -0800 Subject: Support for HP SAN --- .../versions/006_add_provider_data_to_volumes.py | 72 ++++++ nova/db/sqlalchemy/models.py | 3 + nova/volume/driver.py | 144 +++++++++-- nova/volume/manager.py | 8 +- nova/volume/san.py | 287 ++++++++++++++++++--- 5 files changed, 459 insertions(+), 55 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/006_add_provider_data_to_volumes.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/006_add_provider_data_to_volumes.py b/nova/db/sqlalchemy/migrate_repo/versions/006_add_provider_data_to_volumes.py new file mode 100644 index 000000000..705fc8ff3 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/006_add_provider_data_to_volumes.py @@ -0,0 +1,72 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Justin Santa Barbara. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# Table stub-definitions +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +# +volumes = Table('volumes', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +# +# New Tables +# +# None + +# +# Tables to alter +# +# None + +# +# Columns to add to existing tables +# + +volumes_provider_location = Column('provider_location', + String(length=256, + convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)) + +volumes_provider_auth = Column('provider_auth', + String(length=256, + convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + + # Add columns to existing tables + volumes.create_column(volumes_provider_location) + volumes.create_column(volumes_provider_auth) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 40a96fc17..4485ee9e4 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -243,6 +243,9 @@ class Volume(BASE, NovaBase): display_name = Column(String(255)) display_description = Column(String(255)) + provider_location = Column(String(255)) + provider_auth = Column(String(255)) + class Quota(BASE, NovaBase): """Represents quota overrides for a project.""" diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 82f4c2f54..f172e2fdc 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -21,6 +21,7 @@ Drivers for volumes. """ import time +import os from nova import exception from nova import flags @@ -36,6 +37,8 @@ flags.DEFINE_string('aoe_eth_dev', 'eth0', 'Which device to export the volumes on') flags.DEFINE_string('num_shell_tries', 3, 'number of times to attempt to run flakey shell commands') +flags.DEFINE_string('num_iscsi_scan_tries', 3, + 'number of times to rescan iSCSI target to find volume') flags.DEFINE_integer('num_shelves', 100, 'Number of vblade shelves') @@ -294,40 +297,133 @@ class ISCSIDriver(VolumeDriver): self._execute("sudo ietadm --op delete --tid=%s" % iscsi_target) - def _get_name_and_portal(self, volume): - """Gets iscsi name and portal from volume name and host.""" + def _do_iscsi_discovery(self, volume): + #TODO(justinsb): Deprecate discovery and use stored info + #NOTE(justinsb): Discovery won't work with CHAP-secured targets (?) + LOG.warn(_("ISCSI provider_location not stored, using discovery")) + volume_name = volume['name'] - host = volume['host'] + (out, _err) = self._execute("sudo iscsiadm -m discovery -t " - "sendtargets -p %s" % host) + "sendtargets -p %s" % (volume['host'])) for target in out.splitlines(): if FLAGS.iscsi_ip_prefix in target and volume_name in target: - (location, _sep, iscsi_name) = target.partition(" ") - break - iscsi_portal = location.split(",")[0] - return (iscsi_name, iscsi_portal) + return target + return None + + def _get_iscsi_properties(self, volume): + """Gets iscsi configuration, ideally from saved information in the + volume entity, but falling back to discovery if need be.""" + + properties = {} + + location = volume['provider_location'] + + if location: + # provider_location is the same format as iSCSI discovery output + properties['target_discovered'] = False + else: + location = self._do_iscsi_discovery(volume) + + if not location: + raise exception.Error(_("Could not find iSCSI export " + " for volume %s") % + (volume['name'])) + + LOG.debug(_("ISCSI Discovery: Found %s") % (location)) + properties['target_discovered'] = True + + (iscsi_target, _sep, iscsi_name) = location.partition(" ") + + iscsi_portal = iscsi_target.split(",")[0] + + properties['target_iqn'] = iscsi_name + properties['target_portal'] = iscsi_portal + + auth = volume['provider_auth'] + + if auth: + (auth_method, auth_username, auth_secret) = auth.split() + + properties['auth_method'] = auth_method + properties['auth_username'] = auth_username + properties['auth_password'] = auth_secret + + return properties + + def _run_iscsiadm(self, iscsi_properties, iscsi_command): + command = ("sudo iscsiadm -m node -T %s -p %s %s" % + (iscsi_properties['target_iqn'], + iscsi_properties['target_portal'], + iscsi_command)) + (out, err) = self._execute(command) + LOG.debug("iscsiadm %s: stdout=%s stderr=%s" % + (iscsi_command, out, err)) + return (out, err) + + def _iscsiadm_update(self, iscsi_properties, property_key, property_value): + iscsi_command = ("--op update -n %s -v %s" % + (property_key, property_value)) + return self._run_iscsiadm(iscsi_properties, iscsi_command) def discover_volume(self, volume): """Discover volume on a remote host.""" - iscsi_name, iscsi_portal = self._get_name_and_portal(volume) - self._execute("sudo iscsiadm -m node -T %s -p %s --login" % - (iscsi_name, iscsi_portal)) - self._execute("sudo iscsiadm -m node -T %s -p %s --op update " - "-n node.startup -v automatic" % - (iscsi_name, iscsi_portal)) - return "/dev/disk/by-path/ip-%s-iscsi-%s-lun-0" % (iscsi_portal, - iscsi_name) + iscsi_properties = self._get_iscsi_properties(volume) + + if not iscsi_properties['target_discovered']: + self._run_iscsiadm(iscsi_properties, "--op new") + + if iscsi_properties.get('auth_method'): + self._iscsiadm_update(iscsi_properties, + "node.session.auth.authmethod", + iscsi_properties['auth_method']) + self._iscsiadm_update(iscsi_properties, + "node.session.auth.username", + iscsi_properties['auth_username']) + self._iscsiadm_update(iscsi_properties, + "node.session.auth.password", + iscsi_properties['auth_password']) + + self._run_iscsiadm(iscsi_properties, "--login") + + self._iscsiadm_update(iscsi_properties, "node.startup", "automatic") + + mount_device = ("/dev/disk/by-path/ip-%s-iscsi-%s-lun-0" % + (iscsi_properties['target_portal'], + iscsi_properties['target_iqn'])) + + # The /dev/disk/by-path/... node is not always present immediately + # TODO(justinsb): This retry-with-delay is a pattern, move to utils? + tries = 0 + while not os.path.exists(mount_device): + if tries >= FLAGS.num_iscsi_scan_tries: + raise exception.Error(_("iSCSI device not found at %s") % + (mount_device)) + + LOG.warn(_("ISCSI volume not yet found at: %(mount_device)s. " + "Will rescan & retry. Try number: %(tries)s") % + locals()) + + # The rescan isn't documented as being necessary(?), but it helps + self._run_iscsiadm(iscsi_properties, "--rescan") + + tries = tries + 1 + if not os.path.exists(mount_device): + time.sleep(tries ** 2) + + if tries != 0: + LOG.debug(_("Found iSCSI node %(mount_device)s " + "(after %(tries)s rescans)") % + locals()) + + return mount_device def undiscover_volume(self, volume): """Undiscover volume on a remote host.""" - iscsi_name, iscsi_portal = self._get_name_and_portal(volume) - self._execute("sudo iscsiadm -m node -T %s -p %s --op update " - "-n node.startup -v manual" % - (iscsi_name, iscsi_portal)) - self._execute("sudo iscsiadm -m node -T %s -p %s --logout " % - (iscsi_name, iscsi_portal)) - self._execute("sudo iscsiadm -m node --op delete " - "--targetname %s" % iscsi_name) + iscsi_properties = self._get_iscsi_properties(volume) + self._iscsiadm_update(iscsi_properties, "node.startup", "manual") + self._run_iscsiadm(iscsi_properties, "--logout") + self._run_iscsiadm(iscsi_properties, "--op delete") class FakeISCSIDriver(ISCSIDriver): diff --git a/nova/volume/manager.py b/nova/volume/manager.py index d2f02e4e0..7193ece14 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -107,10 +107,14 @@ class VolumeManager(manager.Manager): vol_size = volume_ref['size'] LOG.debug(_("volume %(vol_name)s: creating lv of" " size %(vol_size)sG") % locals()) - self.driver.create_volume(volume_ref) + db_update = self.driver.create_volume(volume_ref) + if db_update: + self.db.volume_update(context, volume_ref['id'], db_update) LOG.debug(_("volume %s: creating export"), volume_ref['name']) - self.driver.create_export(context, volume_ref) + db_update = self.driver.create_export(context, volume_ref) + if db_update: + self.db.volume_update(context, volume_ref['id'], db_update) except Exception: self.db.volume_update(context, volume_ref['id'], {'status': 'error'}) diff --git a/nova/volume/san.py b/nova/volume/san.py index 26d6125e7..911ad096f 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -23,6 +23,8 @@ The unique thing about a SAN is that we don't expect that we can run the volume import os import paramiko +from xml.etree import ElementTree + from nova import exception from nova import flags from nova import log as logging @@ -41,37 +43,15 @@ flags.DEFINE_string('san_password', '', 'Password for SAN controller') flags.DEFINE_string('san_privatekey', '', 'Filename of private key to use for SSH authentication') +flags.DEFINE_string('san_clustername', '', + 'Cluster name to use for creating volumes') +flags.DEFINE_integer('san_ssh_port', 22, + 'SSH port to use with SAN') class SanISCSIDriver(ISCSIDriver): """ Base class for SAN-style storage volumes (storage providers we access over SSH)""" - #Override because SAN ip != host ip - def _get_name_and_portal(self, volume): - """Gets iscsi name and portal from volume name and host.""" - volume_name = volume['name'] - - # TODO(justinsb): store in volume, remerge with generic iSCSI code - host = FLAGS.san_ip - - (out, _err) = self._execute("sudo iscsiadm -m discovery -t " - "sendtargets -p %s" % host) - - location = None - find_iscsi_name = self._build_iscsi_target_name(volume) - for target in out.splitlines(): - if find_iscsi_name in target: - (location, _sep, iscsi_name) = target.partition(" ") - break - if not location: - raise exception.Error(_("Could not find iSCSI export " - " for volume %s") % - volume_name) - - iscsi_portal = location.split(",")[0] - LOG.debug("iscsi_name=%s, iscsi_portal=%s" % - (iscsi_name, iscsi_portal)) - return (iscsi_name, iscsi_portal) def _build_iscsi_target_name(self, volume): return "%s%s" % (FLAGS.iscsi_target_prefix, volume['name']) @@ -85,6 +65,7 @@ class SanISCSIDriver(ISCSIDriver): ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) if FLAGS.san_password: ssh.connect(FLAGS.san_ip, + port=FLAGS.san_ssh_port, username=FLAGS.san_login, password=FLAGS.san_password) elif FLAGS.san_privatekey: @@ -92,10 +73,11 @@ class SanISCSIDriver(ISCSIDriver): # It sucks that paramiko doesn't support DSA keys privatekey = paramiko.RSAKey.from_private_key_file(privatekeyfile) ssh.connect(FLAGS.san_ip, + port=FLAGS.san_ssh_port, username=FLAGS.san_login, pkey=privatekey) else: - raise exception.Error("Specify san_password or san_privatekey") + raise exception.Error(_("Specify san_password or san_privatekey")) return ssh def _run_ssh(self, command, check_exit_code=True): @@ -124,10 +106,10 @@ class SanISCSIDriver(ISCSIDriver): def check_for_setup_error(self): """Returns an error if prerequisites aren't met""" if not (FLAGS.san_password or FLAGS.san_privatekey): - raise exception.Error("Specify san_password or san_privatekey") + raise exception.Error(_("Specify san_password or san_privatekey")) if not (FLAGS.san_ip): - raise exception.Error("san_ip must be set") + raise exception.Error(_("san_ip must be set")) def _collect_lines(data): @@ -306,6 +288,17 @@ class SolarisISCSIDriver(SanISCSIDriver): self._run_ssh("pfexec /usr/sbin/stmfadm add-view -t %s %s" % (target_group_name, luid)) + #TODO(justinsb): Is this always 1? Does it matter? + iscsi_portal_interface = '1' + iscsi_portal = FLAGS.san_ip + ":3260," + iscsi_portal_interface + + db_update = {} + db_update['provider_location'] = ("%s %s" % + (iscsi_portal, + iscsi_name)) + + return db_update + def remove_export(self, context, volume): """Removes an export for a logical volume.""" @@ -333,3 +326,239 @@ class SolarisISCSIDriver(SanISCSIDriver): if self._is_lu_created(volume): self._run_ssh("pfexec /usr/sbin/sbdadm delete-lu %s" % (luid)) + + +class HpSanISCSIDriver(SanISCSIDriver): + """Executes commands relating to HP/Lefthand SAN ISCSI volumes. + We use the CLIQ interface, over SSH. + + Rough overview of CLIQ commands used: + CLIQ createVolume (creates the volume) + CLIQ getVolumeInfo (to discover the IQN etc) + CLIQ getClusterInfo (to discover the iSCSI target IP address) + CLIQ assignVolumeChap (exports it with CHAP security) + + The 'trick' here is that the HP SAN enforces security by default, so + normally a volume mount would need both to configure the SAN in the volume + layer and do the mount on the compute layer. Multi-layer operations are + not catered for at the moment in the nova architecture, so instead we + share the volume using CHAP at volume creation time. Then the mount need + only use those CHAP credentials, so can take place exclusively in the + compute layer""" + + def _cliq_run(self, verb, cliq_args): + """Runs a CLIQ command over SSH, without doing any result parsing""" + cliq_arg_strings = [] + for k, v in cliq_args.items(): + cliq_arg_strings.append(" %s=%s" % (k, v)) + cmd = verb + ''.join(cliq_arg_strings) + + return self._run_ssh(cmd) + + def _cliq_run_xml(self, verb, cliq_args, check_cliq_result=True): + """Runs a CLIQ command over SSH, parsing and checking the output""" + cliq_args['output'] = 'XML' + (out, _err) = self._cliq_run(verb, cliq_args) + + LOG.debug(_("CLIQ command returned %s"), out) + + result_xml = ElementTree.fromstring(out) + if check_cliq_result: + response_node = result_xml.find("response") + if response_node is None: + msg = (_("Malformed response to CLIQ command " + "%(verb)s %(cliq_args)s. Result=%(out)s") % + locals()) + raise exception.Error(msg) + + result_code = response_node.attrib.get("result") + + if result_code != "0": + msg = (_("Error running CLIQ command %(verb)s %(cliq_args)s. " + " Result=%(out)s") % + locals()) + raise exception.Error(msg) + + return result_xml + + def _cliq_get_cluster_info(self, cluster_name): + """Queries for info about the cluster (including IP)""" + cliq_args = {} + cliq_args['clusterName'] = cluster_name + cliq_args['searchDepth'] = '1' + cliq_args['verbose'] = '0' + + result_xml = self._cliq_run_xml("getClusterInfo", cliq_args) + + return result_xml + + def _cliq_get_cluster_vip(self, cluster_name): + """Gets the IP on which a cluster shares iSCSI volumes""" + cluster_xml = self._cliq_get_cluster_info(cluster_name) + + vips = [] + for vip in cluster_xml.findall("response/cluster/vip"): + vips.append(vip.attrib.get('ipAddress')) + + if len(vips) == 1: + return vips[0] + + _xml = ElementTree.tostring(cluster_xml) + msg = (_("Unexpected number of virtual ips for cluster " + " %(cluster_name)s. Result=%(_xml)s") % + locals()) + raise exception.Error(msg) + + def _cliq_get_volume_info(self, volume_name): + """Gets the volume info, including IQN""" + cliq_args = {} + cliq_args['volumeName'] = volume_name + result_xml = self._cliq_run_xml("getVolumeInfo", cliq_args) + + # Result looks like this: + # + # + # + # + # + # + # + # + + # Flatten the nodes into a dictionary; use prefixes to avoid collisions + volume_attributes = {} + + volume_node = result_xml.find("response/volume") + for k, v in volume_node.attrib.items(): + volume_attributes["volume." + k] = v + + status_node = volume_node.find("status") + if not status_node is None: + for k, v in status_node.attrib.items(): + volume_attributes["status." + k] = v + + # We only consider the first permission node + permission_node = volume_node.find("permission") + if not permission_node is None: + for k, v in status_node.attrib.items(): + volume_attributes["permission." + k] = v + + LOG.debug(_("Volume info: %(volume_name)s => %(volume_attributes)s") % + locals()) + return volume_attributes + + def create_volume(self, volume): + """Creates a volume.""" + cliq_args = {} + cliq_args['clusterName'] = FLAGS.san_clustername + #TODO(justinsb): Should we default to inheriting thinProvision? + cliq_args['thinProvision'] = '1' if FLAGS.san_thin_provision else '0' + cliq_args['volumeName'] = volume['name'] + if int(volume['size']) == 0: + cliq_args['size'] = '100MB' + else: + cliq_args['size'] = '%sGB' % volume['size'] + + self._cliq_run_xml("createVolume", cliq_args) + + volume_info = self._cliq_get_volume_info(volume['name']) + cluster_name = volume_info['volume.clusterName'] + iscsi_iqn = volume_info['volume.iscsiIqn'] + + #TODO(justinsb): Is this always 1? Does it matter? + cluster_interface = '1' + + cluster_vip = self._cliq_get_cluster_vip(cluster_name) + iscsi_portal = cluster_vip + ":3260," + cluster_interface + + db_update = {} + db_update['provider_location'] = ("%s %s" % + (iscsi_portal, + iscsi_iqn)) + + return db_update + + def delete_volume(self, volume): + """Deletes a volume.""" + cliq_args = {} + cliq_args['volumeName'] = volume['name'] + cliq_args['prompt'] = 'false' # Don't confirm + + self._cliq_run_xml("deleteVolume", cliq_args) + + def local_path(self, volume): + # TODO(justinsb): Is this needed here? + raise exception.Error(_("local_path not supported")) + + def ensure_export(self, context, volume): + """Synchronously recreates an export for a logical volume.""" + return self._do_export(context, volume, force_create=False) + + def create_export(self, context, volume): + return self._do_export(context, volume, force_create=True) + + def _do_export(self, context, volume, force_create): + """Supports ensure_export and create_export""" + volume_info = self._cliq_get_volume_info(volume['name']) + + is_shared = 'permission.authGroup' in volume_info + + db_update = {} + + should_export = False + + if force_create or not is_shared: + should_export = True + # Check that we have a project_id + project_id = volume['project_id'] + if not project_id: + project_id = context.project_id + + if project_id: + #TODO(justinsb): Use a real per-project password here + chap_username = 'proj_' + project_id + # HP/Lefthand requires that the password be >= 12 characters + chap_password = 'project_secret_' + project_id + else: + msg = (_("Could not determine project for volume %s, " + "can't export") % + (volume['name'])) + if force_create: + raise exception.Error(msg) + else: + LOG.warn(msg) + should_export = False + + if should_export: + cliq_args = {} + cliq_args['volumeName'] = volume['name'] + cliq_args['chapName'] = chap_username + cliq_args['targetSecret'] = chap_password + + self._cliq_run_xml("assignVolumeChap", cliq_args) + + db_update['provider_auth'] = ("CHAP %s %s" % + (chap_username, chap_password)) + + return db_update + + def remove_export(self, context, volume): + """Removes an export for a logical volume.""" + cliq_args = {} + cliq_args['volumeName'] = volume['name'] + + self._cliq_run_xml("unassignVolume", cliq_args) -- cgit From bb5624258200f027320327a38c524c389979c97a Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Fri, 18 Feb 2011 19:04:57 +0000 Subject: Resize compute tests --- nova/tests/test_xenapi.py | 34 ++++++++++++++++++++++++++++++++++ nova/tests/xenapi/stubs.py | 25 +++++++++++++++++++++++-- nova/virt/xenapi/fake.py | 2 +- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 6b8efc9d8..ee4c68e6c 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -336,3 +336,37 @@ class XenAPIDiffieHellmanTestCase(test.TestCase): def tearDown(self): super(XenAPIDiffieHellmanTestCase, self).tearDown() + +class XenAPIMigrateInstance(test.TestCase): + """ + Unit test for verifying migration-related actions + """ + def setUp(self): + super(XenAPIMigrateInstance, self).setUp() + self.stubs = stubout.StubOutForTesting() + FLAGS.target_host = '127.0.0.1' + FLAGS.xenapi_connection_url = 'test_url' + FLAGS.xenapi_connection_password = 'test_pass' + db_fakes.stub_out_db_instance_api(self.stubs) + stubs.stub_out_get_target(self.stubs) + xenapi_fake.reset() + self.values = {'name': 1, 'id': 1, + 'project_id': 'fake', + 'user_id': 'fake', + 'image_id': 1, + 'kernel_id': 2, + 'ramdisk_id': 3, + 'instance_type': 'm1.large', + 'mac_address': 'aa:bb:cc:dd:ee:ff', + } + stubs.stub_out_migration_methods(self.stubs) + + def test_migrate_disk_and_power_off(self): + FLAGS.target_host = '127.0.0.1' + FLAGS.xenapi_connection_url = 'test_url' + FLAGS.xenapi_connection_password = 'test_pass' + destination = '127.0.0.1' + instance = db.instance_create(self.values) + stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) + conn = xenapi_conn.get_connection(False) + conn.migrate_disk_and_power_off(instance, destination) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 624995ada..d1c367475 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -20,6 +20,7 @@ from nova.virt import xenapi_conn from nova.virt.xenapi import fake from nova.virt.xenapi import volume_utils from nova.virt.xenapi import vm_utils +from nova.virt.xenapi import vmops def stubout_instance_snapshot(stubs): @@ -170,8 +171,8 @@ class FakeSessionForVMTests(fake.SessionBase): def VM_destroy(self, session_ref, vm_ref): fake.destroy_vm(vm_ref) - - + + class FakeSessionForVolumeTests(fake.SessionBase): """ Stubs out a XenAPISession for Volume tests """ def __init__(self, uri): @@ -205,3 +206,23 @@ class FakeSessionForVolumeFailedTests(FakeSessionForVolumeTests): def SR_forget(self, _1, ref): pass + +class FakeSessionForMigrationTests(fake.SessionBase): + """ Stubs out a XenAPISession for Migration tests """ + def __init__(self, uri): + super(FakeSessionForMigrationTests, self).__init__(uri) + + +class FakeSnapshot(vmops.VMOps): + def __getattr__(self, key): + return 'fake' + + def __exit__(self, type, value, traceback) + pass + +def fake_get_snapshot(self, instance): + return FakeSnapshot() + +def stub_out_migration_methods(stubs): + stubs.Set(vmops.VMOps, '_get_snapshot', + fake_get_snapshot) diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 018d0dcd3..e1ae03e70 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -401,7 +401,7 @@ class SessionBase(object): field in _db_content[cls][ref]): return _db_content[cls][ref][field] - LOG.debuug(_('Raising NotImplemented')) + LOG.debug(_('Raising NotImplemented')) raise NotImplementedError( _('xenapi.fake does not have an implementation for %s or it has ' 'been called with the wrong number of arguments') % name) -- cgit From 5812a95736b9a16733b99700e8664dd29ae34def Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 18 Feb 2011 22:10:06 +0100 Subject: Introduce IptablesManager in linux_net. Port every use of iptables in linux_net to it. --- nova/network/linux_net.py | 287 ++++++++++++++++++++++++++++----------------- nova/tests/test_network.py | 59 ++++++++++ nova/utils.py | 61 ++++++---- nova/virt/libvirt_conn.py | 28 +++-- 4 files changed, 287 insertions(+), 148 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index c1cbff7d8..3d267d941 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -17,6 +17,7 @@ Implements vlans, bridges, and iptables rules using linux utilities. """ +import inspect import os from nova import db @@ -25,7 +26,6 @@ from nova import flags from nova import log as logging from nova import utils - LOG = logging.getLogger("nova.linux_net") @@ -63,73 +63,168 @@ flags.DEFINE_string('dmz_cidr', '10.128.0.0/24', 'dmz range that should be accepted') +binary_name = os.path.basename(inspect.stack()[-1][1]) + + +class IptablesRule(object): + def __init__(self, chain, rule, wrap=True): + self.chain = chain + self.rule = rule + self.wrap = wrap + + def __eq__(self, other): + return ((self.chain == other.chain) and + (self.rule == other.rule) and + (self.wrap == other.wrap)) + + def __ne__(self, other): + return ((self.chain != other.chain) or + (self.rule != other.rule) or + (self.wrap != other.wrap)) + + def __str__(self): + if self.wrap: + chain = '%s-%s' % (binary_name, self.chain) + else: + chain = self.chain + return '-A %s %s' % (chain, self.rule) + + +class IptablesTable(object): + def __init__(self): + self.rules = [] + self.chains = set() + + def add_chain(self, name): + self.chains.add(name) + + def remove_chain(self, name): + self.chains.remove(name) + + def add_rule(self, chain, rule, wrap=True): + if wrap and chain not in self.chains: + raise ValueError(_("Unknown chain: %r") % chain) + + self.rules.append(IptablesRule(chain, rule, wrap)) + + def remove_rule(self, chain, rule): + self.rules.remove(IptablesRule(chain, rule)) + +class IptablesManager(object): + def __init__(self, execute=None): + if not execute: + if FLAGS.fake_network: + self.execute = lambda *args, **kwargs: ('', '') + else: + self.execute = utils.execute + else: + self.execute = execute + + self.ipv4 = { 'filter': IptablesTable(), + 'nat': IptablesTable() } + self.ipv6 = { 'filter': IptablesTable(), + 'nat': IptablesTable() } + + self.ipv4['nat'].add_chain('SNATTING') + self.ipv4['nat'].add_rule('POSTROUTING', + '-j %s-SNATTING' % (binary_name,), + wrap=False) + + self.ipv4['filter'].add_chain('local') + self.ipv4['filter'].add_rule('FORWARD', + '-j %s-local' % (binary_name,), + wrap=False) + + # Wrap the builtin chains + builtin_chains = {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], + 'nat': ['PREROUTING', 'INPUT', + 'OUTPUT', 'POSTROUTING']} + + for table, chains in builtin_chains.iteritems(): + for chain in chains: + self.ipv4[table].add_chain(chain) + self.ipv4[table].add_rule(chain, + '-j %s-%s' % (binary_name, chain), + wrap=False) + + + def apply(self): + s = [('iptables', self.ipv4)] + if FLAGS.use_ipv6: + s += [('ip6tables', self.ipv6)] + + for cmd, tables in s: + for table in tables: + current_filter, _ = self.execute('sudo %s-save -t %s' % + (cmd, table), attempts=5) + current_lines = current_filter.split('\n') + new_filter = self.modify_rules(current_lines, tables[table]) + self.execute('sudo %s-restore' % (cmd,), + process_input='\n'.join(new_filter), + attempts=5) + + def modify_rules(self, current_lines, table, binary=None): + + chains = table.chains + rules = table.rules + + # Remove any trace of our rules + new_filter = filter(lambda l: '%s' % binary not in l, current_lines) + + seen_chains = False + for rules_index in range(len(new_filter)): + if not seen_chains: + if new_filter[rules_index].startswith(':'): + seen_chains = True + elif seen_chains == 1: + if not new_filter[rules_index].startswith(':'): + break + + new_filter[rules_index:rules_index] = [str(rule) for rule in rules] + new_filter[rules_index:rules_index] = [':%s-%s - [0:0]' % \ + (binary_name, name,) \ + for name in chains] + + return new_filter + + +iptables_manager = IptablesManager() + + def metadata_forward(): """Create forwarding rule for metadata""" - _confirm_rule("PREROUTING", "-t nat -s 0.0.0.0/0 " - "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT " - "--to-destination %s:%s" % (FLAGS.ec2_dmz_host, FLAGS.ec2_port)) + iptables_manager.ipv4['nat'].add_rule("PREROUTING", + "-s 0.0.0.0/0 -d 169.254.169.254/32 " + "-p tcp -m tcp --dport 80 -j DNAT " + "--to-destination %s:%s" % \ + (FLAGS.ec2_dmz_host, FLAGS.ec2_port)) + iptables_manager.apply() def init_host(): """Basic networking setup goes here""" - if FLAGS.use_nova_chains: - _execute("sudo iptables -N nova_input", check_exit_code=False) - _execute("sudo iptables -D %s -j nova_input" % FLAGS.input_chain, - check_exit_code=False) - _execute("sudo iptables -A %s -j nova_input" % FLAGS.input_chain) - - _execute("sudo iptables -N nova_forward", check_exit_code=False) - _execute("sudo iptables -D FORWARD -j nova_forward", - check_exit_code=False) - _execute("sudo iptables -A FORWARD -j nova_forward") - - _execute("sudo iptables -N nova_output", check_exit_code=False) - _execute("sudo iptables -D OUTPUT -j nova_output", - check_exit_code=False) - _execute("sudo iptables -A OUTPUT -j nova_output") - - _execute("sudo iptables -t nat -N nova_prerouting", - check_exit_code=False) - _execute("sudo iptables -t nat -D PREROUTING -j nova_prerouting", - check_exit_code=False) - _execute("sudo iptables -t nat -A PREROUTING -j nova_prerouting") - - _execute("sudo iptables -t nat -N nova_postrouting", - check_exit_code=False) - _execute("sudo iptables -t nat -D POSTROUTING -j nova_postrouting", - check_exit_code=False) - _execute("sudo iptables -t nat -A POSTROUTING -j nova_postrouting") - - _execute("sudo iptables -t nat -N nova_snatting", - check_exit_code=False) - _execute("sudo iptables -t nat -D POSTROUTING -j nova_snatting", - check_exit_code=False) - _execute("sudo iptables -t nat -A POSTROUTING -j nova_snatting") - - _execute("sudo iptables -t nat -N nova_output", check_exit_code=False) - _execute("sudo iptables -t nat -D OUTPUT -j nova_output", - check_exit_code=False) - _execute("sudo iptables -t nat -A OUTPUT -j nova_output") - else: - # NOTE(vish): This makes it easy to ensure snatting rules always - # come after the accept rules in the postrouting chain - _execute("sudo iptables -t nat -N SNATTING", - check_exit_code=False) - _execute("sudo iptables -t nat -D POSTROUTING -j SNATTING", - check_exit_code=False) - _execute("sudo iptables -t nat -A POSTROUTING -j SNATTING") - # NOTE(devcamcar): Cloud public SNAT entries and the default # SNAT rule for outbound traffic. - _confirm_rule("SNATTING", "-t nat -s %s " - "-j SNAT --to-source %s" - % (FLAGS.fixed_range, FLAGS.routing_source_ip), append=True) + iptables_manager.ipv4['nat'].add_rule("SNATTING", + "-s %s -j SNAT --to-source %s" % \ + (FLAGS.fixed_range, + FLAGS.routing_source_ip)) + + iptables_manager.ipv4['nat'].add_rule("POSTROUTING", + "-s %s -j SNAT --to-source %s" % \ + (FLAGS.fixed_range, + FLAGS.routing_source_ip)) - _confirm_rule("POSTROUTING", "-t nat -s %s -d %s -j ACCEPT" % - (FLAGS.fixed_range, FLAGS.dmz_cidr)) - _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" % - {'range': FLAGS.fixed_range}) + iptables_manager.ipv4['nat'].add_rule("POSTROUTING", + "-s %s -d %s -j ACCEPT" % \ + (FLAGS.fixed_range, FLAGS.dmz_cidr)) + + iptables_manager.ipv4['nat'].add_rule("POSTROUTING", + "-s %(range)s -d %(range)s " + "-j ACCEPT" % \ + {'range': FLAGS.fixed_range}) + iptables_manager.apply() def bind_floating_ip(floating_ip, check_exit_code=True): @@ -147,32 +242,33 @@ def unbind_floating_ip(floating_ip): def ensure_vlan_forward(public_ip, port, private_ip): """Sets up forwarding rules for vlan""" - _confirm_rule("FORWARD", "-d %s -p udp --dport 1194 -j ACCEPT" % - private_ip) - _confirm_rule("PREROUTING", - "-t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" - % (public_ip, port, private_ip)) + iptables_manager.ipv4['filter'].add_rule("FORWARD", + "-d %s -p udp " + "--dport 1194 " + "-j ACCEPT" % private_ip) + iptables_manager.ipv4['nat'].add_rule("PREROUTING", + "-d %s -p udp " + "--dport %s -j DNAT --to %s:1194" % + (public_ip, port, private_ip)) + iptables_manager.apply() def ensure_floating_forward(floating_ip, fixed_ip): """Ensure floating ip forwarding rule""" - _confirm_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s" - % (floating_ip, fixed_ip)) - _confirm_rule("OUTPUT", "-t nat -d %s -j DNAT --to %s" - % (floating_ip, fixed_ip)) - _confirm_rule("SNATTING", "-t nat -s %s -j SNAT --to %s" - % (fixed_ip, floating_ip)) - + for chain, rule in floating_forward_rules(floating_ip, fixed_ip): + iptables_manager.ipv4['nat'].add_rule(chain, rule) + iptables_manager.apply() def remove_floating_forward(floating_ip, fixed_ip): """Remove forwarding for floating ip""" - _remove_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s" - % (floating_ip, fixed_ip)) - _remove_rule("OUTPUT", "-t nat -d %s -j DNAT --to %s" - % (floating_ip, fixed_ip)) - _remove_rule("SNATTING", "-t nat -s %s -j SNAT --to %s" - % (fixed_ip, floating_ip)) + for chain, rule in floating_forward_rules(floating_ip, fixed_ip): + iptables_manager.ipv4['nat'].remove_rule(chain, rule) + iptables_manager.apply() +def floating_forward_rules(floating_ip, fixed_ip): + return [("PREROUTING", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)), + ("OUTPUT", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)), + ("SNATTING", "-d %s -j DNAT --to %s" % (fixed_ip, floating_ip))] def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): """Create a vlan and bridge unless they already exist""" @@ -258,19 +354,12 @@ def ensure_bridge(bridge, interface, net_attrs=None): "enslave it to bridge %s.\n" % (interface, bridge)): raise exception.Error("Failed to add interface: %s" % err) - if FLAGS.use_nova_chains: - (out, err) = _execute("sudo iptables -N nova_forward", - check_exit_code=False) - if err != 'iptables: Chain already exists.\n': - # NOTE(vish): chain didn't exist link chain - _execute("sudo iptables -D FORWARD -j nova_forward", - check_exit_code=False) - _execute("sudo iptables -A FORWARD -j nova_forward") - - _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) - _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge) - _execute("sudo iptables -N nova-local", check_exit_code=False) - _confirm_rule("FORWARD", "-j nova-local") + iptables_manager.ipv4['filter'].add_rule("FORWARD", + "--in-interface %s -j ACCEPT" % \ + bridge) + iptables_manager.ipv4['filter'].add_rule("FORWARD", + "--out-interface %s -j ACCEPT" % \ + bridge) def get_dhcp_hosts(context, network_id): @@ -390,26 +479,6 @@ def _device_exists(device): return not err -def _confirm_rule(chain, cmd, append=False): - """Delete and re-add iptables rule""" - if FLAGS.use_nova_chains: - chain = "nova_%s" % chain.lower() - if append: - loc = "-A" - else: - loc = "-I" - _execute("sudo iptables --delete %s %s" % (chain, cmd), - check_exit_code=False) - _execute("sudo iptables %s %s %s" % (loc, chain, cmd)) - - -def _remove_rule(chain, cmd): - """Remove iptables rule""" - if FLAGS.use_nova_chains: - chain = "%s" % chain.lower() - _execute("sudo iptables --delete %s %s" % (chain, cmd)) - - def _dnsmasq_cmd(net): """Builds dnsmasq command""" cmd = ['sudo -E dnsmasq', diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 00f9323f3..b28d64245 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -29,11 +29,70 @@ from nova import log as logging from nova import test from nova import utils from nova.auth import manager +from nova.network import linux_net FLAGS = flags.FLAGS LOG = logging.getLogger('nova.tests.network') +class IptablesManagerTestCase(test.TestCase): + sample_filter = """# Completed on Fri Feb 18 15:17:05 2011 +# Generated by iptables-save v1.4.10 on Fri Feb 18 15:17:05 2011 +*filter +:INPUT ACCEPT [2223527:305688874] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [2172501:140856656] +-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT +-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT +-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT +-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT +-A FORWARD -d 192.168.122.0/24 -o virbr0 -m state --state RELATED,ESTABLISHED -j ACCEPT +-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT +-A FORWARD -i virbr0 -o virbr0 -j ACCEPT +-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable +-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable +COMMIT +# Completed on Fri Feb 18 15:17:05 2011""" + + def setUp(self): + super(IptablesManagerTestCase, self).setUp() + self.manager = linux_net.IptablesManager() + + def test_rules_are_wrapped(self): + current_lines = self.sample_filter.split('\n') + + table = self.manager.ipv4['filter'] + table.add_rule('FORWARD', '-s 1.2.3.4/5 -j DROP') + new_lines = self.manager.modify_rules(current_lines, table) + self.assertTrue('-A run_tests.py-FORWARD ' + '-s 1.2.3.4/5 -j DROP' in new_lines) + + table.remove_rule('FORWARD', '-s 1.2.3.4/5 -j DROP') + new_lines = self.manager.modify_rules(current_lines, table) + self.assertTrue('-A run_tests.py-FORWARD ' + '-s 1.2.3.4/5 -j DROP' not in new_lines) + + def test_wrapper_rules_in_place(self): + current_lines = self.sample_filter.split('\n') + + # TODO(soren): Add stuff for ipv6 + check_matrix = {4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], + 'nat': ['PREROUTING', 'INPUT', + 'OUTPUT', 'POSTROUTING']} } + + for ip_version in check_matrix: + ip = getattr(self.manager, 'ipv%d' % ip_version) + for table_name in ip: + table = ip[table_name] + new_lines = self.manager.modify_rules(current_lines, table) + for chain in check_matrix[ip_version][table_name]: + self.assertTrue(':run_tests.py-%s - [0:0]' % \ + (chain,) in new_lines) + self.assertTrue('-A %s -j run_tests.py-%s' % \ + (chain, chain) in new_lines) + print '\n'.join(new_lines) + + class NetworkTestCase(test.TestCase): """Test cases for network code""" def setUp(self): diff --git a/nova/utils.py b/nova/utils.py index ba71ebf39..bf3a4b098 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -124,32 +124,41 @@ def fetchfile(url, target): execute("curl --fail %s -o %s" % (url, target)) -def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): - LOG.debug(_("Running cmd (subprocess): %s"), cmd) - env = os.environ.copy() - if addl_env: - env.update(addl_env) - obj = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) - result = None - if process_input != None: - result = obj.communicate(process_input) - else: - result = obj.communicate() - obj.stdin.close() - if obj.returncode: - LOG.debug(_("Result was %s") % obj.returncode) - if check_exit_code and obj.returncode != 0: - (stdout, stderr) = result - raise ProcessExecutionError(exit_code=obj.returncode, - stdout=stdout, - stderr=stderr, - cmd=cmd) - # NOTE(termie): this appears to be necessary to let the subprocess call - # clean something up in between calls, without it two - # execute calls in a row hangs the second one - greenthread.sleep(0) - return result +def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, attempts=1): + while attempts > 0: + attempts -= 1 + try: + LOG.debug(_("Running cmd (subprocess): %s"), cmd) + env = os.environ.copy() + if addl_env: + env.update(addl_env) + obj = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) + result = None + if process_input != None: + result = obj.communicate(process_input) + else: + result = obj.communicate() + obj.stdin.close() + if obj.returncode: + LOG.debug(_("Result was %s") % obj.returncode) + if check_exit_code and obj.returncode != 0: + (stdout, stderr) = result + raise ProcessExecutionError(exit_code=obj.returncode, + stdout=stdout, + stderr=stderr, + cmd=cmd) + # NOTE(termie): this appears to be necessary to let the subprocess call + # clean something up in between calls, without it two + # execute calls in a row hangs the second one + greenthread.sleep(0) + return result + except ProcessExecutionError: + if not attempts: + raise + else: + greenthread.sleep(random.randint(50,300)/100) + pass def ssh_execute(ssh, cmd, process_input=None, diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 7548fff63..11b3acbf6 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -64,7 +64,6 @@ from nova.compute import power_state from nova.virt import disk from nova.virt import images -libvirt_semaphore = semaphore.Semaphore() libvirt = None libxml2 = None Template = None @@ -1239,19 +1238,22 @@ class IptablesFirewallDriver(FirewallDriver): self.apply_ruleset() def apply_ruleset(self): - with libvirt_semaphore: - current_filter, _ = self.execute('sudo iptables-save -t filter') + current_filter, _ = self.execute('sudo iptables-save -t filter', + attempts=5) + current_lines = current_filter.split('\n') + new_filter = self.modify_rules(current_lines, 4) + self.execute('sudo iptables-restore', + process_input='\n'.join(new_filter), + attempts=5) + if(FLAGS.use_ipv6): + current_filter, _ = self.execute('sudo ip6tables-save ' + '-t filter', + attempts=5) current_lines = current_filter.split('\n') - new_filter = self.modify_rules(current_lines, 4) - self.execute('sudo iptables-restore', - process_input='\n'.join(new_filter)) - if(FLAGS.use_ipv6): - current_filter, _ = self.execute('sudo ip6tables-save ' - '-t filter') - current_lines = current_filter.split('\n') - new_filter = self.modify_rules(current_lines, 6) - self.execute('sudo ip6tables-restore', - process_input='\n'.join(new_filter)) + new_filter = self.modify_rules(current_lines, 6) + self.execute('sudo ip6tables-restore', + process_input='\n'.join(new_filter), + attempts=5) def modify_rules(self, current_lines, ip_version=4): ctxt = context.get_admin_context() -- cgit From 62b3eb71384581e900b061e65caa6418c4452fa9 Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Fri, 18 Feb 2011 21:37:57 +0000 Subject: XenAPI tests --- nova/tests/test_xenapi.py | 12 +++++++----- nova/tests/xenapi/stubs.py | 38 +++++++++++++++++++++++++++++--------- nova/virt/xenapi/fake.py | 3 +++ 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index ee4c68e6c..3cbc01e5c 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -362,11 +362,13 @@ class XenAPIMigrateInstance(test.TestCase): stubs.stub_out_migration_methods(self.stubs) def test_migrate_disk_and_power_off(self): - FLAGS.target_host = '127.0.0.1' - FLAGS.xenapi_connection_url = 'test_url' - FLAGS.xenapi_connection_password = 'test_pass' - destination = '127.0.0.1' instance = db.instance_create(self.values) stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) conn = xenapi_conn.get_connection(False) - conn.migrate_disk_and_power_off(instance, destination) + conn.migrate_disk_and_power_off(instance, '127.0.0.1') + + def test_attach_disk(self): + instance = db.instance_create(self.values) + stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) + conn = xenapi_conn.get_connection(False) + conn.attach_disk(instance, {'base_copy': 'hurr', 'cow': 'durr'}) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index d1c367475..054fc434b 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -213,16 +213,36 @@ class FakeSessionForMigrationTests(fake.SessionBase): super(FakeSessionForMigrationTests, self).__init__(uri) -class FakeSnapshot(vmops.VMOps): - def __getattr__(self, key): - return 'fake' +def stub_out_migration_methods(stubs): + class FakeSnapshot(object): + def __getattr__(self, key): + return str(key) + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + pass + + def fake_get_snapshot(self, instance): + return FakeSnapshot() - def __exit__(self, type, value, traceback) + @classmethod + def fake_get_vdi(cls, session, vm_ref): + vdi_ref = fake.create_vdi(name_label='derp', read_only=False, + sr_ref='herp', sharable=False) + vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) + return vdi_ref, {'uuid': vdi_rec['uuid']} + + def fake_shutdown(self, inst, vm, method='clean'): pass -def fake_get_snapshot(self, instance): - return FakeSnapshot() + @classmethod + def fake_scan_sr(cls, session): + pass -def stub_out_migration_methods(stubs): - stubs.Set(vmops.VMOps, '_get_snapshot', - fake_get_snapshot) + stubs.Set(vm_utils.VMHelper, 'scan_sr', fake_scan_sr) + stubs.Set(vmops.VMOps, '_get_snapshot', fake_get_snapshot) + stubs.Set(vm_utils.VMHelper, 'get_vdi_for_vm_safely', fake_get_vdi) + stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', lambda x,y,z: None) + stubs.Set(vmops.VMOps, '_shutdown', fake_shutdown) diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index e1ae03e70..ba12d4d3a 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -290,6 +290,9 @@ class SessionBase(object): #Always return 12GB available return 12 * 1024 * 1024 * 1024 + def host_call_plugin(*args): + return 'herp' + def xenapi_request(self, methodname, params): if methodname.startswith('login'): self._login(methodname, params) -- cgit From 8916442e7f2920938a317777de71f75faf463005 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 18 Feb 2011 21:42:04 +0000 Subject: Typo fix --- nova/virt/xenapi/vm_utils.py | 2 ++ nova/virt/xenapi/vmops.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 9027d58c4..88a205d2f 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -451,6 +451,8 @@ class VMHelper(HelperBase): else: return ImageType.DISK_RAW + # FIXME(sirp): can we unify the ImageService and xenapi_image_service + # abstractions? if FLAGS.xenapi_image_service == 'glance': image_type = determine_from_glance() else: diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 8d1c79c0b..09abd5861 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -394,7 +394,7 @@ class VMOps(object): """ Three situations can occur: - 1. We have netiher a ramdisk or a kernel, in which case we are a + 1. We have neither a ramdisk nor a kernel, in which case we are a RAW image and can omit this step 2. We have one or the other, in which case, we should flag as an -- cgit From 18e573a14414838f11e772edca3eb5510f852c94 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Fri, 18 Feb 2011 17:45:57 -0400 Subject: sandy y u no read hacking guide and import classes? --- nova/api/openstack/zones.py | 5 ++--- nova/scheduler/manager.py | 4 ++-- nova/scheduler/zone_manager.py | 4 ++-- nova/tests/api/openstack/test_zones.py | 7 ++++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index 24a4444f7..99be0ba02 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -19,8 +19,7 @@ import logging from nova import flags from nova import wsgi from nova import db -from nova import rpc -from nova.scheduler.api import API +from nova.scheduler import api FLAGS = flags.FLAGS @@ -54,7 +53,7 @@ class Controller(wsgi.Controller): """Return all zones in brief""" # Ask the ZoneManager in the Scheduler for most recent data, # or fall-back to the database ... - items = API().get_zone_list(req.environ['nova.context']) + items = api.API().get_zone_list(req.environ['nova.context']) if not items: items = db.zone_get_all(req.environ['nova.context']) diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 7ced33b9c..c94397210 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -29,7 +29,7 @@ from nova import log as logging from nova import manager from nova import rpc from nova import utils -from nova.scheduler.zone_manager import ZoneManager +from nova.scheduler import zone_manager LOG = logging.getLogger('nova.scheduler.manager') FLAGS = flags.FLAGS @@ -44,7 +44,7 @@ class SchedulerManager(manager.Manager): if not scheduler_driver: scheduler_driver = FLAGS.scheduler_driver self.driver = utils.import_object(scheduler_driver) - self.zone_manager = ZoneManager() + self.zone_manager = zone_manager.ZoneManager() super(SchedulerManager, self).__init__(*args, **kwargs) def __getattr__(self, key): diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index 783783d06..758c5e3db 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -22,7 +22,7 @@ import thread import traceback from datetime import datetime -from eventlet.greenpool import GreenPool +from eventlet import greenpool from nova import db from nova import flags @@ -105,7 +105,7 @@ class ZoneManager(object): def __init__(self): self.last_zone_db_check = datetime.min self.zone_states = {} - self.green_pool = GreenPool() + self.green_pool = greenpool.GreenPool() def get_zone_list(self): """Return the list of zones we know about.""" diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 4df7c7feb..4374cd457 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -24,7 +24,7 @@ from nova import context from nova import flags from nova.api.openstack import zones from nova.tests.api.openstack import fakes -from nova.scheduler.api import API +from nova.scheduler import api FLAGS = flags.FLAGS @@ -97,7 +97,7 @@ class ZonesTest(unittest.TestCase): FLAGS.allow_admin_api = self.allow_admin def test_get_zone_list_scheduler(self): - self.stubs.Set(API, '_call_scheduler', zone_get_all_scheduler) + self.stubs.Set(api.API, '_call_scheduler', zone_get_all_scheduler) req = webob.Request.blank('/v1.0/zones') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) @@ -106,7 +106,8 @@ class ZonesTest(unittest.TestCase): self.assertEqual(len(res_dict['zones']), 2) def test_get_zone_list_db(self): - self.stubs.Set(API, '_call_scheduler', zone_get_all_scheduler_empty) + self.stubs.Set(api.API, '_call_scheduler', + zone_get_all_scheduler_empty) self.stubs.Set(nova.db, 'zone_get_all', zone_get_all_db) req = webob.Request.blank('/v1.0/zones') res = req.get_response(fakes.wsgi_app()) -- cgit From fa29dc0433384d5aa47f5ac069a8dc650e23ccae Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 18 Feb 2011 15:48:49 -0600 Subject: moved creating vifs to its own function, moved inject network to its own function --- nova/virt/xenapi/vmops.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index d1ef95c6c..76b88a8bd 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -135,6 +135,8 @@ class VMOps(object): timer.f = _wait_for_boot # call to reset network to inject network info and configure + networks = self.inject_network_info(instance) + self.create_vifs(instance, networks) self.reset_network(instance) return timer.start(interval=0.5, now=True) @@ -414,8 +416,9 @@ class VMOps(object): vm_opaque_ref) admin_context = context.get_admin_context() IPs = db.fixed_ip_get_all_by_instance(admin_context, instance['id']) - for network in db.network_get_all_by_instance(admin_context, - instance['id']): + networks = db.network_get_all_by_instance(admin_context, + instance['id']) + for network in networks: network_IPs = [ip for ip in IPs if ip.network_id == network.id] def ip_dict(ip): @@ -438,11 +441,23 @@ class VMOps(object): # catch KeyError for domid if instance isn't running pass - # TODO(tr3buchet) - remove comment in multi-nic - # this bit here about creating the vifs will be updated - # in multi-nic to handle multiple IPs on the same network - # and multiple networks - # for now it works as there is only one of each + return networks + + def create_vifs(self, instance, networks=None): + """ + Creates vifs for an instance + + """ + logging.debug(_("creating vif(s) for vm: |%s|"), vm_opaque_ref) + if networks is None: + networks = db.network_get_all_by_instance(admin_context, + instance['id']) + # TODO(tr3buchet) - remove comment in multi-nic + # this bit here about creating the vifs will be updated + # in multi-nic to handle multiple IPs on the same network + # and multiple networks + # for now it works as there is only one of each + for network in networks: bridge = network['bridge'] network_ref = \ NetworkHelper.find_network_with_bridge(self._session, bridge) @@ -456,7 +471,6 @@ class VMOps(object): Creates uuid arg to pass to make_agent_call and calls it. """ - self.inject_network_info(instance) args = {'id': str(uuid.uuid4())} resp = self._make_agent_call('resetnetwork', instance, '', args) -- cgit From a43c5929de7ebf58eb9ecb8416ce3cf4194c176a Mon Sep 17 00:00:00 2001 From: Cerberus Date: Fri, 18 Feb 2011 16:13:34 -0600 Subject: Pep8 cleanup --- nova/api/openstack/servers.py | 13 ++-- nova/compute/api.py | 12 ++- nova/compute/manager.py | 85 +++++++++++----------- nova/db/api.py | 5 +- nova/db/sqlalchemy/api.py | 6 +- .../versions/004_add_instance_migrations.py | 3 +- nova/db/sqlalchemy/models.py | 5 +- nova/tests/api/openstack/common.py | 7 +- nova/tests/api/openstack/test_servers.py | 24 +++--- nova/tests/test_compute.py | 6 +- nova/tests/test_xenapi.py | 1 + nova/tests/xenapi/stubs.py | 11 +-- nova/virt/xenapi/vm_utils.py | 13 ++-- nova/virt/xenapi/vmops.py | 5 +- nova/virt/xenapi_conn.py | 6 +- 15 files changed, 104 insertions(+), 98 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index a719f5e15..f68c97323 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -209,12 +209,12 @@ class Controller(wsgi.Controller): resize a server """ actions = { - 'reboot':self._action_reboot, - 'resize':self._action_resize, - 'confirmResize':self._action_confirm_resize, - 'revertResize':self._action_revert_resize, - 'rebuild':self._action_rebuild - } + 'reboot': self._action_reboot, + 'resize': self._action_resize, + 'confirmResize': self._action_confirm_resize, + 'revertResize': self._action_revert_resize, + 'rebuild': self._action_rebuild, + } input_dict = self._deserialize(req.body, req) for key in actions.keys(): @@ -256,7 +256,6 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPBadRequest()) return faults.Fault(exc.HTTPAccepted()) - def _action_reboot(self, input_dict, req, id): try: reboot_type = input_dict['reboot']['type'] diff --git a/nova/compute/api.py b/nova/compute/api.py index 371cbae5f..2eb0e0743 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -408,7 +408,7 @@ class API(base.Base): raise exception.Error(_("No finished migrations found for \ instance")) - params = { 'migration_id': migration_ref['id'] } + params = {'migration_id': migration_ref['id']} self._cast_compute_message('revert_resize', context, instance_id, migration_ref['dest_compute'], params=params) @@ -422,14 +422,12 @@ class API(base.Base): raise exception.Error(_("No finished migrations found for \ instance")) instance_ref = self.db.instance_get(context, instance_id) - - params = { 'migration_id': migration_ref['id'] } + params = {'migration_id': migration_ref['id']} self._cast_compute_message('confirm_resize', context, instance_id, migration_ref['source_compute'], params=params) - self.db.migration_update(context, migration_id, - { 'status': 'confirmed' }) - + self.db.migration_update(context, migration_id, + {'status': 'confirmed'}) self.db.instance_update(context, instance_id, {'host': migration_ref['dest_compute'], }) @@ -439,7 +437,7 @@ class API(base.Base): {"method": "prep_resize", "args": {"topic": FLAGS.compute_topic, "instance_id": instance_id, }},) - + def pause(self, context, instance_id): """Pause the given instance.""" self._cast_compute_message('pause_instance', context, instance_id) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 1e1c44663..3f6c359ba 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -379,7 +379,7 @@ class ComputeManager(manager.Manager): def _update_state_callback(self, context, instance_id, result): """Update instance state when async task completes.""" self._update_state(context, instance_id) - + @exception.wrap_exception @checks_instance_lock def confirm_resize(self, context, instance_id, migration_id): @@ -392,8 +392,8 @@ class ComputeManager(manager.Manager): @exception.wrap_exception @checks_instance_lock def revert_resize(self, context, instance_id, migration_id): - """Destroys the new instance on the destination machine, - reverts the model changes, and powers on the old + """Destroys the new instance on the destination machine, + reverts the model changes, and powers on the old instance on the source machine""" instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_get(context, migration_id) @@ -401,24 +401,23 @@ class ComputeManager(manager.Manager): #TODO(mdietz): we may want to split these into separate methods. if migration_ref['source_compute'] == FLAGS.host: self.driver.power_on(instance_ref) - self.db.migration_update(context, migration_id, - { 'status': 'reverted' }) + self.db.migration_update(context, migration_id, + {'status': 'reverted'}) else: self.driver.destroy(instance_ref) - topic = self.db.queue_get_for(context, FLAGS.compute_topic, + topic = self.db.queue_get_for(context, FLAGS.compute_topic, instance_ref['host']) - rpc.cast(context, topic, - { 'method': 'revert_resize', - 'args': { - 'migration_id': migration_ref['id'], - 'instance_id': instance_id, - }, + rpc.cast(context, topic, + {'method': 'revert_resize', + 'args': { + 'migration_id': migration_ref['id'], + 'instance_id': instance_id, }, }) @exception.wrap_exception @checks_instance_lock def prep_resize(self, context, instance_id): - """Initiates the process of moving a running instance to another + """Initiates the process of moving a running instance to another host, possibly changing the RAM and disk size in the process""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) @@ -426,21 +425,21 @@ class ComputeManager(manager.Manager): raise exception.Error(_( 'Migration error: destination same as source!')) - migration_ref = self.db.migration_create(context, - { 'instance_id': instance_id, - 'source_compute': instance_ref['host'], - 'dest_compute': FLAGS.host, - 'dest_host': self.driver.get_host_ip_addr(), - 'status': 'pre-migrating' }) - LOG.audit(_('instance %s: migrating to '), instance_id, context=context) - topic = self.db.queue_get_for(context, FLAGS.compute_topic, + migration_ref = self.db.migration_create(context, + {'instance_id': instance_id, + 'source_compute': instance_ref['host'], + 'dest_compute': FLAGS.host, + 'dest_host': self.driver.get_host_ip_addr(), + 'status': 'pre-migrating'}) + LOG.audit(_('instance %s: migrating to '), instance_id, + context=context) + topic = self.db.queue_get_for(context, FLAGS.compute_topic, instance_ref['host']) - rpc.cast(context, topic, - { 'method': 'resize_instance', - 'args': { - 'migration_id': migration_ref['id'], - 'instance_id': instance_id, - }, + rpc.cast(context, topic, + {'method': 'resize_instance', + 'args': { + 'migration_id': migration_ref['id'], + 'instance_id': instance_id, }, }) @exception.wrap_exception @@ -449,28 +448,26 @@ class ComputeManager(manager.Manager): """Starts the migration of a running instance to another host""" migration_ref = self.db.migration_get(context, migration_id) instance_ref = self.db.instance_get(context, instance_id) - self.db.migration_update(context, migration_id, - { 'status': 'migrating', }) + self.db.migration_update(context, migration_id, + {'status': 'migrating', }) - disk_info = self.driver.migrate_disk_and_power_off(instance_ref, + disk_info = self.driver.migrate_disk_and_power_off(instance_ref, migration_ref['dest_host']) - - self.db.migration_update(context, migration_id, - { 'status': 'post-migrating', }) + self.db.migration_update(context, migration_id, + {'status': 'post-migrating', }) - #TODO(mdietz): This is where we would update the VM record + #TODO(mdietz): This is where we would update the VM record #after resizing service = self.db.service_get_by_host_and_topic(context, migration_ref['dest_compute'], FLAGS.compute_topic) - topic = self.db.queue_get_for(context, FLAGS.compute_topic, + topic = self.db.queue_get_for(context, FLAGS.compute_topic, migration_ref['dest_compute']) - rpc.cast(context, topic, - { 'method': 'finish_resize', - 'args': { - 'migration_id': migration_id, - 'instance_id': instance_id, - 'disk_info': disk_info, - }, + rpc.cast(context, topic, + {'method': 'finish_resize', + 'args': { + 'migration_id': migration_id, + 'instance_id': instance_id, + 'disk_info': disk_info, }, }) @exception.wrap_exception @@ -486,8 +483,8 @@ class ComputeManager(manager.Manager): new_disk_info = self.driver.attach_disk(instance_ref, disk_info) self.driver.spawn(instance_ref, disk=new_disk_info) - self.db.migration_update(context, migration_id, - {'status': 'finished', }) + self.db.migration_update(context, migration_id, + {'status': 'finished', }) @exception.wrap_exception @checks_instance_lock diff --git a/nova/db/api.py b/nova/db/api.py index 5a9d49374..8706ef3d6 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -260,17 +260,20 @@ def floating_ip_get_by_address(context, address): #################### def migration_update(context, id, values): - """Update a migration instance""" + """Update a migration instance""" return IMPL.migration_update(context, id, values) + def migration_create(context, values): """Create a migration record""" return IMPL.migration_create(context, values) + def migration_get(context, migration_id): """Finds a migration by the id""" return IMPL.migration_get(context, migration_id) + def migration_get_by_instance_and_status(context, instance_id, status): """Finds a migration by the instance id its migrating""" return IMPL.migration_get_by_instance_and_status(context, instance_id, diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 210b53296..facb46b8b 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -156,6 +156,7 @@ def service_get_all_by_topic(context, topic): filter_by(topic=topic).\ all() + @require_admin_context def service_get_by_host_and_topic(context, host, topic): session = get_session() @@ -166,6 +167,7 @@ def service_get_by_host_and_topic(context, host, topic): filter_by(topic=topic).\ first() + @require_admin_context def service_get_all_by_host(context, host): session = get_session() @@ -1996,7 +1998,7 @@ def migration_get(context, id, session=None): result = session.query(models.Migration).\ filter_by(id=id).first() if not result: - raise exception.NotFound(_("No migration found with id %s") + raise exception.NotFound(_("No migration found with id %s") % migration_id) return result @@ -2008,7 +2010,7 @@ def migration_get_by_instance_and_status(context, instance_id, status): filter_by(instance_id=instance_id).\ filter_by(status=status).first() if not result: - raise exception.NotFound(_("No migration found with instance id %s") + raise exception.NotFound(_("No migration found with instance id %s") % migration_id) return result diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py index 4aab5bdc6..4fda525f1 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py @@ -44,9 +44,10 @@ migrations = Table('migrations', meta, Column('dest_host', String(255)), Column('instance_id', Integer, ForeignKey('instances.id'), nullable=True), - Column('status', String(255)) + Column('status', String(255)), ) + def upgrade(migrate_engine): # Upgrade operations go here. Don't create your own engine; # bind migrate_engine to your metadata diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 0140fbeab..b05f134b7 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -374,7 +374,8 @@ class Migration(BASE, NovaBase): dest_compute = Column(String(255)) dest_host = Column(String(255)) instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True) - status = Column(String(255)) #TODO(_cerberus_): enum + #TODO(_cerberus_): enum + status = Column(String(255)) class Network(BASE, NovaBase): @@ -559,7 +560,7 @@ def register_models(): Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, - Project, Certificate, ConsolePool, Console, + Project, Certificate, ConsolePool, Console, Migration) # , Image, Host engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: diff --git a/nova/tests/api/openstack/common.py b/nova/tests/api/openstack/common.py index 66207cddc..3f9c7d3cf 100644 --- a/nova/tests/api/openstack/common.py +++ b/nova/tests/api/openstack/common.py @@ -19,14 +19,17 @@ import json import webob + def webob_factory(url): + """Factory for removing duplicate webob code from tests""" + base_url = url + def web_request(url, method=None, body=None): - req = webob.Request.blank("%s%s" % (base_url, url)) + req = webob.Request.blank("%s%s" % (base_url, url)) if method: req.method = method if body: req.body = json.dumps(body) return req return web_request - diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 665551c55..4eb4a3c62 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -419,8 +419,9 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(resize=dict(flavorId=3))) self.resize_called = False + def resize_mock(*args): - self.resize_called = True + self.resize_called = True self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) @@ -432,8 +433,9 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(resize=dict(derp=3))) self.resize_called = False + def resize_mock(*args): - self.resize_called = True + self.resize_called = True self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) @@ -445,7 +447,7 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(resize=dict(flavorId=3))) def resize_mock(*args): - raise Exception, 'hurr durr' + raise Exception('hurr durr') self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) @@ -456,10 +458,11 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) self.resize_called = False + def confirm_resize_mock(*args): - self.resize_called = True + self.resize_called = True - self.stubs.Set(nova.compute.api.API, 'confirm_resize', + self.stubs.Set(nova.compute.api.API, 'confirm_resize', confirm_resize_mock) res = req.get_response(fakes.wsgi_app()) @@ -470,9 +473,9 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) def confirm_resize_mock(*args): - raise Exception, 'hurr durr' + raise Exception('hurr durr') - self.stubs.Set(nova.compute.api.API, 'confirm_resize', + self.stubs.Set(nova.compute.api.API, 'confirm_resize', confirm_resize_mock) res = req.get_response(fakes.wsgi_app()) @@ -482,10 +485,11 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(revertResize=None)) self.resize_called = False + def revert_resize_mock(*args): self.resize_called = True - self.stubs.Set(nova.compute.api.API, 'revert_resize', + self.stubs.Set(nova.compute.api.API, 'revert_resize', revert_resize_mock) res = req.get_response(fakes.wsgi_app()) @@ -496,9 +500,9 @@ class ServersTest(unittest.TestCase): req = self.webreq('/1/action', 'POST', dict(revertResize=None)) def revert_resize_mock(*args): - raise Exception, 'hurr durr' + raise Exception('hurr durr') - self.stubs.Set(nova.compute.api.API, 'revert_resize', + self.stubs.Set(nova.compute.api.API, 'revert_resize', revert_resize_mock) res = req.get_response(fakes.wsgi_app()) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 3f2e64c87..5fd1ddaec 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -265,8 +265,7 @@ class ComputeTestCase(test.TestCase): instance_id = self._create_instance() context = self.context.elevated() self.compute.run_instance(self.context, instance_id) - db.instance_update(self.context, instance_id, {'host':'foo'}) - + db.instance_update(self.context, instance_id, {'host': 'foo'}) self.compute.prep_resize(context, instance_id) migration_ref = db.migration_get_by_instance_and_status(context, instance_id, 'pre-migrating') @@ -279,7 +278,6 @@ class ComputeTestCase(test.TestCase): the same host""" instance_id = self._create_instance() self.compute.run_instance(self.context, instance_id) - self.assertRaises(exception.Error, self.compute.prep_resize, + self.assertRaises(exception.Error, self.compute.prep_resize, self.context, instance_id) - self.compute.terminate_instance(self.context, instance_id) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 3cbc01e5c..cb9b6620a 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -337,6 +337,7 @@ class XenAPIDiffieHellmanTestCase(test.TestCase): def tearDown(self): super(XenAPIDiffieHellmanTestCase, self).tearDown() + class XenAPIMigrateInstance(test.TestCase): """ Unit test for verifying migration-related actions diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 054fc434b..303c37eb9 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -171,8 +171,8 @@ class FakeSessionForVMTests(fake.SessionBase): def VM_destroy(self, session_ref, vm_ref): fake.destroy_vm(vm_ref) - - + + class FakeSessionForVolumeTests(fake.SessionBase): """ Stubs out a XenAPISession for Volume tests """ def __init__(self, uri): @@ -207,6 +207,7 @@ class FakeSessionForVolumeFailedTests(FakeSessionForVolumeTests): def SR_forget(self, _1, ref): pass + class FakeSessionForMigrationTests(fake.SessionBase): """ Stubs out a XenAPISession for Migration tests """ def __init__(self, uri): @@ -232,8 +233,8 @@ def stub_out_migration_methods(stubs): vdi_ref = fake.create_vdi(name_label='derp', read_only=False, sr_ref='herp', sharable=False) vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) - return vdi_ref, {'uuid': vdi_rec['uuid']} - + return vdi_ref, {'uuid': vdi_rec['uuid'], } + def fake_shutdown(self, inst, vm, method='clean'): pass @@ -244,5 +245,5 @@ def stub_out_migration_methods(stubs): stubs.Set(vm_utils.VMHelper, 'scan_sr', fake_scan_sr) stubs.Set(vmops.VMOps, '_get_snapshot', fake_get_snapshot) stubs.Set(vm_utils.VMHelper, 'get_vdi_for_vm_safely', fake_get_vdi) - stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', lambda x,y,z: None) + stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', lambda x, y, z: None) stubs.Set(vmops.VMOps, '_shutdown', fake_shutdown) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 91e7339b1..436c88023 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -256,21 +256,18 @@ class VMHelper(HelperBase): else: num_vdis = len(vdi_refs) if num_vdis != 1: - raise Exception(_("Unexpected number of VDIs (%(num_vdis)s) found" + raise Exception( + _("Unexpected number of VDIs (%(num_vdis)s) found" " for VM %(vm_ref)s") % locals()) vdi_ref = vdi_refs[0] vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) return vdi_ref, vdi_rec - - - @classmethod def create_snapshot(cls, session, instance_id, vm_ref, label): """ Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, - Snapshot VHD - """ + Snapshot VHD """ #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added LOG.debug(_("Snapshotting VM %(vm_ref)s with label '%(label)s'...") @@ -284,7 +281,7 @@ class VMHelper(HelperBase): task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) template_vm_ref = session.wait_for_task(instance_id, task) - template_vdi_rec = cls.get_vdi_for_vm_safely(session, + template_vdi_rec = cls.get_vdi_for_vm_safely(session, template_vm_ref)[1] template_vdi_uuid = template_vdi_rec["uuid"] @@ -299,7 +296,7 @@ class VMHelper(HelperBase): @classmethod def get_sr(cls, session, sr_label='slices'): - """ Finds the SR named by the given name label and returns + """ Finds the SR named by the given name label and returns the UUID """ return session.call_xenapi('SR.get_by_name_label', sr_label)[0] diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 552c2ddd1..d457f2e3f 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -345,7 +345,7 @@ class VMOps(object): 'new_base_copy_uuid': new_base_copy_uuid, 'new_cow_uuid': new_cow_uuid, } - task = self._session.async_call_plugin('migration', + task = self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) self._session.wait_for_task(instance.id, task) @@ -469,7 +469,8 @@ class VMOps(object): vm = VMHelper.lookup(self._session, instance.name) return self._destroy(instance, vm, shutdown=True) - def _destroy(self, instance, vm, shutdown=True, destroy_kernel_ramdisk=True): + def _destroy(self, instance, vm, shutdown=True, + destroy_kernel_ramdisk=True): """ Destroys VM instance by performing: diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index be018b47f..e1c5dcc7c 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -192,7 +192,7 @@ class XenAPIConnection(object): def attach_disk(self, instance, disk_info): """Moves the copied VDIs into the SR""" return self._vmops.attach_disk(instance, disk_info) - + def suspend(self, instance, callback): """suspend the specified instance""" self._vmops.suspend(instance, callback) @@ -220,9 +220,9 @@ class XenAPIConnection(object): def get_ajax_console(self, instance): """Return link to instance's ajax console""" return self._vmops.get_ajax_console(instance) - + def get_host_ip_addr(self): - xs_url = urlparse.urlparse(FLAGS.xenapi_connection_url) + xs_url = urlparse.urlparse(FLAGS.xenapi_connection_url) return xs_url.netloc def attach_volume(self, instance_name, device_path, mountpoint): -- cgit From eefc8938d8a8010052affab9a5c0d010778d9780 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 18 Feb 2011 22:25:19 +0000 Subject: Changing type -> image_type --- nova/virt/xenapi/vm_utils.py | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 88a205d2f..2114bfa42 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -305,9 +305,10 @@ class VMHelper(HelperBase): session.wait_for_task(instance_id, task) @classmethod - def fetch_image(cls, session, instance_id, image, user, project, type): + def fetch_image(cls, session, instance_id, image, user, project, + image_type): """ - type is interpreted as an ImageType instance + image_type is interpreted as an ImageType instance Related flags: xenapi_image_service = ['glance', 'objectstore'] glance_address = 'address for glance services' @@ -317,14 +318,15 @@ class VMHelper(HelperBase): if FLAGS.xenapi_image_service == 'glance': return cls._fetch_image_glance(session, instance_id, image, - access, type) + access, image_type) else: return cls._fetch_image_objectstore(session, instance_id, image, - access, user.secret, type) + access, user.secret, + image_type) @classmethod def _fetch_image_glance_vhd(cls, session, instance_id, image, access, - type): + image_type): LOG.debug(_("Asking xapi to fetch vhd image %(image)s") % locals()) @@ -358,7 +360,7 @@ class VMHelper(HelperBase): @classmethod def _fetch_image_glance_disk(cls, session, instance_id, image, access, - type): + image_type): """Fetch the image from Glance NOTE: @@ -378,7 +380,7 @@ class VMHelper(HelperBase): vdi_size = virtual_size LOG.debug(_("Size for image %(image)s:%(virtual_size)d") % locals()) - if type == ImageType.DISK: + if image_type == ImageType.DISK: # Make room for MBR. vdi_size += MBR_SIZE_BYTES @@ -387,9 +389,9 @@ class VMHelper(HelperBase): with_vdi_attached_here(session, vdi, False, lambda dev: - _stream_disk(dev, type, + _stream_disk(dev, image_type, virtual_size, image_file)) - if (type == ImageType.KERNEL_RAMDISK): + if image_type == ImageType.KERNEL_RAMDISK: #we need to invoke a plugin for copying VDI's #content into proper path LOG.debug(_("Copying VDI %s to /boot/guest on dom0"), vdi) @@ -462,29 +464,33 @@ class VMHelper(HelperBase): return image_type @classmethod - def _fetch_image_glance(cls, session, instance_id, image, access, type): - if type == ImageType.DISK_VHD: + def _fetch_image_glance(cls, session, instance_id, image, access, + image_type): + if image_type == ImageType.DISK_VHD: return cls._fetch_image_glance_vhd( - session, instance_id, image, access, type) + session, instance_id, image, access, image_type) else: return cls._fetch_image_glance_disk( - session, instance_id, image, access, type) + session, instance_id, image, access, image_type) @classmethod def _fetch_image_objectstore(cls, session, instance_id, image, access, - secret, type): + secret, image_type): url = images.image_url(image) LOG.debug(_("Asking xapi to fetch %(url)s as %(access)s") % locals()) - fn = (type != ImageType.KERNEL_RAMDISK) and 'get_vdi' or 'get_kernel' + if image_type == ImageType.KERNEL_RAMDISK: + fn = 'get_kernel' + else: + fn = 'get_vdi' args = {} args['src_url'] = url args['username'] = access args['password'] = secret args['add_partition'] = 'false' args['raw'] = 'false' - if type != ImageType.KERNEL_RAMDISK: + if image_type != ImageType.KERNEL_RAMDISK: args['add_partition'] = 'true' - if type == ImageType.DISK_RAW: + if image_type == ImageType.DISK_RAW: args['raw'] = 'true' task = session.async_call_plugin('objectstore', fn, args) uuid = session.wait_for_task(instance_id, task) @@ -857,9 +863,9 @@ def get_this_vm_ref(session): return session.get_xenapi().VM.get_by_uuid(get_this_vm_uuid()) -def _stream_disk(dev, type, virtual_size, image_file): +def _stream_disk(dev, image_type, virtual_size, image_file): offset = 0 - if type == ImageType.DISK: + if image_type == ImageType.DISK: offset = MBR_SIZE_BYTES _write_partition(virtual_size, dev) -- cgit From be898f26984da4ee92da1a027e47775cd816fed5 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 18 Feb 2011 14:35:12 -0800 Subject: Fix typo (?) in authentication logic --- nova/api/openstack/auth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 473071738..0b6ef8fc5 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -121,8 +121,8 @@ class AuthMiddleware(wsgi.Middleware): req - webob.Request object """ ctxt = context.get_admin_context() - user = self.auth.get_user_from_access_key(key) - if user and user.name == username: + user = self.auth.get_user_from_access_key(username) + if user and user.secret == key: token_hash = hashlib.sha1('%s%s%f' % (username, key, time.time())).hexdigest() token_dict = {} -- cgit From b4c67400324df02480b171b84ba73cfe8a6d044e Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 18 Feb 2011 14:36:01 -0800 Subject: If there are no keypairs, output a useful error message --- nova/api/openstack/servers.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 486eca508..ce9601ecb 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -162,8 +162,12 @@ class Controller(wsgi.Controller): if not env: return faults.Fault(exc.HTTPUnprocessableEntity()) - key_pair = auth_manager.AuthManager.get_key_pairs( - req.environ['nova.context'])[0] + key_pairs = auth_manager.AuthManager.get_key_pairs( + req.environ['nova.context']) + if not key_pairs: + raise exception.NotFound(_("No keypairs defined")) + key_pair = key_pairs[0] + image_id = common.get_image_id_from_image_hash(self._image_service, req.environ['nova.context'], env['server']['imageId']) kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image( -- cgit From e369f2842446876505ce528c5bb56a3d41215f8f Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 18 Feb 2011 16:42:26 -0600 Subject: added admin api call for injecting network info, added api test for inject network info --- nova/api/openstack/__init__.py | 1 + nova/api/openstack/servers.py | 14 ++++++++++++++ nova/compute/api.py | 7 +++++++ nova/compute/manager.py | 12 ++++++++++++ nova/tests/api/openstack/test_servers.py | 12 ++++++++++++ nova/virt/xenapi_conn.py | 4 ++++ 6 files changed, 50 insertions(+) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index dc3738d4a..cfa2da486 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -80,6 +80,7 @@ class APIRouter(wsgi.Router): server_members['suspend'] = 'POST' server_members['resume'] = 'POST' server_members['reset_network'] = 'POST' + server_members['inject_network_info'] = 'POST' mapper.resource("server", "servers", controller=servers.Controller(), collection={'detail': 'GET'}, diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 33cc3bbde..55fdb765b 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -263,6 +263,20 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + def inject_network_info(self, req, id): + """ + Inject network info for an instance (admin only). + + """ + context = req.environ['nova.context'] + try: + self.compute_api.inject_network_info(context, id) + except: + readable = traceback.format_exc() + LOG.exception(_("Compute.api::inject_network_info %s"), readable) + return faults.Fault(exc.HTTPUnprocessableEntity()) + return exc.HTTPAccepted() + def pause(self, req, id): """ Permit Admins to Pause the server. """ ctxt = req.environ['nova.context'] diff --git a/nova/compute/api.py b/nova/compute/api.py index ed6f0e34a..3e5cd495e 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -473,6 +473,13 @@ class API(base.Base): """ self._cast_compute_message('reset_network', context, instance_id) + def inject_network_info(self, context, instance_id): + """ + Inject network info for the instance. + + """ + self._cast_compute_message('inject_network_info', context, instance_id) + def attach_volume(self, context, instance_id, volume_id, device): if not re.match("^/dev/[a-z]d[a-z]+$", device): raise exception.ApiError(_("Invalid device specified: %s. " diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6fab1a41c..ae3ab519f 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -510,6 +510,18 @@ class ComputeManager(manager.Manager): context=context) self.driver.reset_network(instance_ref) + @checks_instance_lock + def inject_network_info(self, context, instance_id): + """ + Inject network info for the instance. + + """ + context = context.elevated() + instance_ref = self.db.instance_get(context, instance_id) + LOG.debug(_('instance %s: inject network info'), instance_id, + context=context) + self.driver.inject_network_info(instance_ref) + @exception.wrap_exception def get_console_output(self, context, instance_id): """Send the console output for an instance.""" diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 89e192eed..58da12dcc 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -293,6 +293,18 @@ class ServersTest(unittest.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) + def test_server_inject_network_info(self): + FLAGS.allow_admin_api = True + body = dict(server=dict( + name='server_test', imageId=2, flavorId=2, metadata={}, + personality={})) + req = webob.Request.blank('/v1.0/servers/1/inject_network_info') + req.method = 'POST' + req.content_type = 'application/json' + req.body = json.dumps(body) + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 202) + def test_server_diagnostics(self): req = webob.Request.blank("/v1.0/servers/1/diagnostics") req.method = "GET" diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 2720d175f..f72a12380 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -192,6 +192,10 @@ class XenAPIConnection(object): """reset networking for specified instance""" self._vmops.reset_network(instance) + def inject_network_info(self, instance): + """inject network info for specified instance""" + self._vmops.inject_network_info(instance) + def get_info(self, instance_id): """Return data about VM instance""" return self._vmops.get_info(instance_id) -- cgit From 05d135be0c0d8a90a97d62005a101345964800cf Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Fri, 18 Feb 2011 22:50:13 +0000 Subject: Typo fix --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 3b5cedda7..097670b58 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -215,7 +215,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): 'x-image-meta-property-kernel-id': 'nokernel', 'x-image-meta-property-ramdisk-id': 'noramdisk', 'x-image-meta-property-container-format': 'tarball', - 'transfer-encoding': "chunked", + 'transfer-encoding': 'chunked', 'content-type': 'application/octet-stream', } for header, value in headers.iteritems(): -- cgit From cfd6d4e403dcb2405cd7ff48bad3083a02159d2c Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Sat, 19 Feb 2011 00:14:08 +0100 Subject: Port libvirt_conn.IptablesDriver over to use linux_net.IptablesManager --- nova/network/linux_net.py | 17 +++- nova/tests/test_virt.py | 55 ++++++++---- nova/virt/libvirt_conn.py | 215 ++++++++++++++++++++-------------------------- 3 files changed, 145 insertions(+), 142 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 3d267d941..c11d34922 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -100,13 +100,23 @@ class IptablesTable(object): def remove_chain(self, name): self.chains.remove(name) + self.rules = filter(lambda r: r.chain != name, self.rules) def add_rule(self, chain, rule, wrap=True): if wrap and chain not in self.chains: raise ValueError(_("Unknown chain: %r") % chain) + if '$' in rule: + rule = ' '.join(map(self._wrap_target_chain, rule.split(' '))) + + print 'Adding rule: %r' % rule self.rules.append(IptablesRule(chain, rule, wrap)) + def _wrap_target_chain(self, s): + if s.startswith('$'): + return '%s-%s' % (binary_name, s[1:]) + return s + def remove_rule(self, chain, rule): self.rules.remove(IptablesRule(chain, rule)) @@ -122,8 +132,7 @@ class IptablesManager(object): self.ipv4 = { 'filter': IptablesTable(), 'nat': IptablesTable() } - self.ipv6 = { 'filter': IptablesTable(), - 'nat': IptablesTable() } + self.ipv6 = { 'filter': IptablesTable() } self.ipv4['nat'].add_chain('SNATTING') self.ipv4['nat'].add_rule('POSTROUTING', @@ -135,6 +144,10 @@ class IptablesManager(object): '-j %s-local' % (binary_name,), wrap=False) + self.ipv4['filter'].add_rule('OUTPUT', + '-j %s-local' % (binary_name,), + wrap=False) + # Wrap the builtin chains builtin_chains = {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], 'nat': ['PREROUTING', 'INPUT', diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 6e5a0114b..a88e01818 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -14,6 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. +import re from xml.etree.ElementTree import fromstring as xml_to_tree from xml.dom.minidom import parseString as xml_to_dom @@ -233,16 +234,22 @@ class IptablesFirewallTestCase(test.TestCase): self.manager.delete_user(self.user) super(IptablesFirewallTestCase, self).tearDown() - in_rules = [ + in_nat_rules = [ + '# Generated by iptables-save v1.4.10 on Sat Feb 19 00:03:19 2011', + '*nat', + ':PREROUTING ACCEPT [1170:189210]', + ':INPUT ACCEPT [844:71028]', + ':OUTPUT ACCEPT [5149:405186]', + ':POSTROUTING ACCEPT [5063:386098]' + ] + + in_filter_rules = [ '# Generated by iptables-save v1.4.4 on Mon Dec 6 11:54:13 2010', '*filter', ':INPUT ACCEPT [969615:281627771]', ':FORWARD ACCEPT [0:0]', ':OUTPUT ACCEPT [915599:63811649]', ':nova-block-ipv4 - [0:0]', - '-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT ', - '-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT ', - '-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT ', '-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT ', '-A FORWARD -d 192.168.122.0/24 -o virbr0 -m state --state RELATED' ',ESTABLISHED -j ACCEPT ', @@ -254,7 +261,7 @@ class IptablesFirewallTestCase(test.TestCase): '# Completed on Mon Dec 6 11:54:13 2010', ] - in6_rules = [ + in6_filter_rules = [ '# Generated by ip6tables-save v1.4.4 on Tue Jan 18 23:47:56 2011', '*filter', ':INPUT ACCEPT [349155:75810423]', @@ -314,23 +321,31 @@ class IptablesFirewallTestCase(test.TestCase): instance_ref = db.instance_get(admin_ctxt, instance_ref['id']) # self.fw.add_instance(instance_ref) - def fake_iptables_execute(cmd, process_input=None): + def fake_iptables_execute(cmd, process_input=None, attempts=5): if cmd == 'sudo ip6tables-save -t filter': - return '\n'.join(self.in6_rules), None + return '\n'.join(self.in6_filter_rules), None if cmd == 'sudo iptables-save -t filter': - return '\n'.join(self.in_rules), None + return '\n'.join(self.in_filter_rules), None + if cmd == 'sudo iptables-save -t nat': + return '\n'.join(self.in_nat_rules), None if cmd == 'sudo iptables-restore': - self.out_rules = process_input.split('\n') + lines = process_input.split('\n') + if '*filter' in lines: + self.out_rules = lines return '', '' if cmd == 'sudo ip6tables-restore': - self.out6_rules = process_input.split('\n') + lines = process_input.split('\n') + if '*filter' in lines: + self.out6_rules = lines return '', '' - self.fw.execute = fake_iptables_execute + + from nova.network import linux_net + linux_net.iptables_manager.execute = fake_iptables_execute self.fw.prepare_instance_filter(instance_ref) self.fw.apply_instance_filter(instance_ref) - in_rules = filter(lambda l: not l.startswith('#'), self.in_rules) + in_rules = filter(lambda l: not l.startswith('#'), self.in_filter_rules) for rule in in_rules: if not 'nova' in rule: self.assertTrue(rule in self.out_rules, @@ -338,6 +353,7 @@ class IptablesFirewallTestCase(test.TestCase): instance_chain = None for rule in self.out_rules: + print rule # This is pretty crude, but it'll do for now if '-d 10.11.12.13 -j' in rule: instance_chain = rule.split(' ')[-1] @@ -353,17 +369,18 @@ class IptablesFirewallTestCase(test.TestCase): self.assertTrue(security_group_chain, "The security group chain wasn't added") - self.assertTrue('-A %s -p icmp -s 192.168.11.0/24 -j ACCEPT' % \ - security_group_chain in self.out_rules, + regex = re.compile('-A .* -p icmp -s 192.168.11.0/24 -j ACCEPT') + self.assertTrue(len(filter(regex.match, self.out_rules)) > 0, "ICMP acceptance rule wasn't added") - self.assertTrue('-A %s -p icmp -s 192.168.11.0/24 -m icmp --icmp-type ' - '8 -j ACCEPT' % security_group_chain in self.out_rules, + regex = re.compile('-A .* -p icmp -s 192.168.11.0/24 -m icmp ' + '--icmp-type 8 -j ACCEPT') + self.assertTrue(len(filter(regex.match, self.out_rules)) > 0, "ICMP Echo Request acceptance rule wasn't added") - self.assertTrue('-A %s -p tcp -s 192.168.10.0/24 -m multiport ' - '--dports 80:81 -j ACCEPT' % security_group_chain \ - in self.out_rules, + regex = re.compile('-A .* -p tcp -s 192.168.10.0/24 -m multiport ' + '--dports 80:81 -j ACCEPT') + self.assertTrue(len(filter(regex.match, self.out_rules)) > 0, "TCP port 80/81 acceptance rule wasn't added") diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 11b3acbf6..976ccaca5 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -57,7 +57,6 @@ from nova import exception from nova import flags from nova import log as logging from nova import utils -#from nova.api import context from nova.auth import manager from nova.compute import instance_types from nova.compute import power_state @@ -1207,10 +1206,14 @@ class NWFilterFirewall(FirewallDriver): class IptablesFirewallDriver(FirewallDriver): def __init__(self, execute=None, **kwargs): - self.execute = execute or utils.execute + from nova.network import linux_net + self.iptables = linux_net.iptables_manager self.instances = {} self.nwfilter = NWFilterFirewall(kwargs['get_connection']) + self.iptables.ipv4['filter'].add_chain('sg-fallback') + self.iptables.ipv4['filter'].add_rule('sg-fallback', '-j DROP') + def setup_basic_filtering(self, instance): """Use NWFilter from libvirt for this.""" return self.nwfilter.setup_basic_filtering(instance) @@ -1226,124 +1229,92 @@ class IptablesFirewallDriver(FirewallDriver): LOG.info(_('Attempted to unfilter instance %s which is not ' 'filtered'), instance['id']) - def add_instance(self, instance): + def prepare_instance_filter(self, instance): self.instances[instance['id']] = instance + chain_name = self._instance_chain_name(instance) + + self.iptables.ipv4['filter'].add_chain(chain_name) + ipv4_address = self._ip_for_instance(instance) + self.iptables.ipv4['filter'].add_rule('local', + '-d %s -j $%s' % + (ipv4_address, chain_name)) + + if FLAGS.use_ipv6: + self.iptables.ipv6['filter'].add_chain(chain_name) + ipv6_address = self._ip_for_instance_v6(instance) + self.iptables.ipv4['filter'].add_rule('local', + '-d %s -j $%s' % + (ipv6_address, + chain_name)) + + ipv4_rules, ipv6_rules = self.instance_rules(instance) + + for rule in ipv4_rules: + self.iptables.ipv4['filter'].add_rule(chain_name, rule) + + if FLAGS.use_ipv6: + for rule in ipv6_rules: + self.iptables.ipv6['filter'].add_rule(chain_name, rule) + + self.iptables.apply() + def unfilter_instance(self, instance): - self.remove_instance(instance) - self.apply_ruleset() + chain_name = self._instance_chain_name(instance) - def prepare_instance_filter(self, instance): - self.add_instance(instance) - self.apply_ruleset() + self.iptables.ipv4['filter'].remove_chain(chain_name) + if FLAGS.use_ipv6: + self.iptables.ipv6['filter'].remove_chain(chain_name) - def apply_ruleset(self): - current_filter, _ = self.execute('sudo iptables-save -t filter', - attempts=5) - current_lines = current_filter.split('\n') - new_filter = self.modify_rules(current_lines, 4) - self.execute('sudo iptables-restore', - process_input='\n'.join(new_filter), - attempts=5) - if(FLAGS.use_ipv6): - current_filter, _ = self.execute('sudo ip6tables-save ' - '-t filter', - attempts=5) - current_lines = current_filter.split('\n') - new_filter = self.modify_rules(current_lines, 6) - self.execute('sudo ip6tables-restore', - process_input='\n'.join(new_filter), - attempts=5) - - def modify_rules(self, current_lines, ip_version=4): + self.iptables.apply() + + + def instance_rules(self, instance): ctxt = context.get_admin_context() - # Remove any trace of nova rules. - new_filter = filter(lambda l: 'nova-' not in l, current_lines) - - seen_chains = False - for rules_index in range(len(new_filter)): - if not seen_chains: - if new_filter[rules_index].startswith(':'): - seen_chains = True - elif seen_chains == 1: - if not new_filter[rules_index].startswith(':'): - break - our_chains = [':nova-fallback - [0:0]'] - our_rules = ['-A nova-fallback -j DROP'] - - our_chains += [':nova-local - [0:0]'] - our_rules += ['-A FORWARD -j nova-local'] - our_rules += ['-A OUTPUT -j nova-local'] - - security_groups = {} - # Add our chains - # First, we add instance chains and rules - for instance_id in self.instances: - instance = self.instances[instance_id] - chain_name = self._instance_chain_name(instance) - if(ip_version == 4): - ip_address = self._ip_for_instance(instance) - elif(ip_version == 6): - ip_address = self._ip_for_instance_v6(instance) - - our_chains += [':%s - [0:0]' % chain_name] - - # Jump to the per-instance chain - our_rules += ['-A nova-local -d %s -j %s' % (ip_address, - chain_name)] - - # Always drop invalid packets - our_rules += ['-A %s -m state --state ' - 'INVALID -j DROP' % (chain_name,)] - - # Allow established connections - our_rules += ['-A %s -m state --state ' - 'ESTABLISHED,RELATED -j ACCEPT' % (chain_name,)] - - # Jump to each security group chain in turn - for security_group in \ - db.security_group_get_by_instance(ctxt, - instance['id']): - security_groups[security_group['id']] = security_group - - sg_chain_name = self._security_group_chain_name( - security_group['id']) + ipv4_rules = [] + ipv6_rules = [] - our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)] - - if(ip_version == 4): - # Allow DHCP responses - dhcp_server = self._dhcp_server_for_instance(instance) - our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68 ' - '-j ACCEPT ' % (chain_name, dhcp_server)] - #Allow project network traffic - if (FLAGS.allow_project_net_traffic): - cidr = self._project_cidr_for_instance(instance) - our_rules += ['-A %s -s %s -j ACCEPT' % (chain_name, cidr)] - elif(ip_version == 6): - # Allow RA responses - ra_server = self._ra_server_for_instance(instance) - if ra_server: - our_rules += ['-A %s -s %s -p icmpv6 -j ACCEPT' % - (chain_name, ra_server + "/128")] - #Allow project network traffic - if (FLAGS.allow_project_net_traffic): - cidrv6 = self._project_cidrv6_for_instance(instance) - our_rules += ['-A %s -s %s -j ACCEPT' % - (chain_name, cidrv6)] - - # If nothing matches, jump to the fallback chain - our_rules += ['-A %s -j nova-fallback' % (chain_name,)] + # Always drop invalid packets + ipv4_rules += ['-m state --state ' 'INVALID -j DROP'] + ipv6_rules += ['-m state --state ' 'INVALID -j DROP'] - # then, security group chains and rules - for security_group_id in security_groups: - chain_name = self._security_group_chain_name(security_group_id) - our_chains += [':%s - [0:0]' % chain_name] + # Allow established connections + ipv4_rules += ['-m state --state ESTABLISHED,RELATED -j ACCEPT'] + ipv6_rules += ['-m state --state ESTABLISHED,RELATED -j ACCEPT'] + + dhcp_server = self._dhcp_server_for_instance(instance) + ipv4_rules += ['-s %s -p udp --sport 67 --dport 68 ' + '-j ACCEPT' % (dhcp_server,)] + + #Allow project network traffic + if FLAGS.allow_project_net_traffic: + cidr = self._project_cidr_for_instance(instance) + ipv4_rules += ['-s %s -j ACCEPT' % (cidr,)] + + # We wrap these in FLAGS.use_ipv6 because they might cause + # a DB lookup. The other ones are just list operations, so + # they're not worth the clutter. + if FLAGS.use_ipv6: + # Allow RA responses + ra_server = self._ra_server_for_instance(instance) + if ra_server: + ipv6_rules += ['-s %s/128 -p icmpv6 -j ACCEPT' % (ra_server,)] - rules = \ - db.security_group_rule_get_by_security_group(ctxt, - security_group_id) + #Allow project network traffic + if FLAGS.allow_project_net_traffic: + cidrv6 = self._project_cidrv6_for_instance(instance) + ipv6_rules += ['-s %s -j ACCEPT' % (cidrv6,)] + + + security_groups = db.security_group_get_by_instance(ctxt, + instance['id']) + + + # then, security group chains and rules + for security_group in security_groups: + rules = db.security_group_rule_get_by_security_group(ctxt, + security_group['id']) for rule in rules: logging.info('%r', rule) @@ -1354,14 +1325,16 @@ class IptablesFirewallDriver(FirewallDriver): continue version = _get_ip_version(rule.cidr) - if version != ip_version: - continue + if version == 4: + rules = ipv4_rules + else: + rules = ipv6_rules protocol = rule.protocol if version == 6 and rule.protocol == 'icmp': protocol = 'icmpv6' - args = ['-A', chain_name, '-p', protocol, '-s', rule.cidr] + args = ['-p', protocol, '-s', rule.cidr] if rule.protocol in ['udp', 'tcp']: if rule.from_port == rule.to_port: @@ -1382,20 +1355,20 @@ class IptablesFirewallDriver(FirewallDriver): icmp_type_arg += '/%s' % icmp_code if icmp_type_arg: - if(ip_version == 4): + if version == 4: args += ['-m', 'icmp', '--icmp-type', icmp_type_arg] - elif(ip_version == 6): + elif version == 6: args += ['-m', 'icmp6', '--icmpv6-type', icmp_type_arg] args += ['-j ACCEPT'] - our_rules += [' '.join(args)] + rules += [' '.join(args)] + + ipv4_rules += ['-j $fallback'] + ipv6_rules += ['-j $fallback'] - new_filter[rules_index:rules_index] = our_rules - new_filter[rules_index:rules_index] = our_chains - logging.info('new_filter: %s', '\n'.join(new_filter)) - return new_filter + return ipv4_rules, ipv6_rules def refresh_security_group_members(self, security_group): pass @@ -1407,7 +1380,7 @@ class IptablesFirewallDriver(FirewallDriver): return 'nova-sg-%s' % (security_group_id,) def _instance_chain_name(self, instance): - return 'nova-inst-%s' % (instance['id'],) + return 'inst-%s' % (instance['id'],) def _ip_for_instance(self, instance): return db.instance_get_fixed_address(context.get_admin_context(), -- cgit From 99760bd7a51371b29cf0f76134187dc81e7545d0 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Sat, 19 Feb 2011 00:30:44 +0100 Subject: Rename a few things for more clarity. --- nova/network/linux_net.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index c11d34922..b657ab4bc 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -109,7 +109,6 @@ class IptablesTable(object): if '$' in rule: rule = ' '.join(map(self._wrap_target_chain, rule.split(' '))) - print 'Adding rule: %r' % rule self.rules.append(IptablesRule(chain, rule, wrap)) def _wrap_target_chain(self, s): @@ -168,9 +167,9 @@ class IptablesManager(object): for cmd, tables in s: for table in tables: - current_filter, _ = self.execute('sudo %s-save -t %s' % - (cmd, table), attempts=5) - current_lines = current_filter.split('\n') + current_table, _ = self.execute('sudo %s-save -t %s' % + (cmd, table), attempts=5) + current_lines = current_table.split('\n') new_filter = self.modify_rules(current_lines, tables[table]) self.execute('sudo %s-restore' % (cmd,), process_input='\n'.join(new_filter), @@ -182,7 +181,7 @@ class IptablesManager(object): rules = table.rules # Remove any trace of our rules - new_filter = filter(lambda l: '%s' % binary not in l, current_lines) + new_filter = filter(lambda l: binary_name not in l, current_lines) seen_chains = False for rules_index in range(len(new_filter)): -- cgit From 23729c543350ce4ce563077522f18d0bedd1e61b Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Sat, 19 Feb 2011 00:36:34 +0100 Subject: Security group fallback is named sg-fallback. --- nova/virt/libvirt_conn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 976ccaca5..0ddf889a1 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1365,8 +1365,8 @@ class IptablesFirewallDriver(FirewallDriver): args += ['-j ACCEPT'] rules += [' '.join(args)] - ipv4_rules += ['-j $fallback'] - ipv6_rules += ['-j $fallback'] + ipv4_rules += ['-j $sg-fallback'] + ipv6_rules += ['-j $sg-fallback'] return ipv4_rules, ipv6_rules -- cgit From fcd31c4d7c3855cb95ac75d6966b377eca8bbe7d Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 18 Feb 2011 15:59:42 -0800 Subject: added instance types purge test --- nova/tests/test_instance_types.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 0d54cc283..fe052110f 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -49,12 +49,16 @@ class InstanceTypeTestCase(test.TestCase): new = instance_types.get_all_types() self.assertNotEqual(len(starting_inst_list), len(new), - 'instance was not created') + 'instance type was not created') instance_types.destroy(self.name) self.assertEqual(1, instance_types.get_instance_type(self.name)["deleted"]) self.assertEqual(starting_inst_list, instance_types.get_all_types()) db.instance_type_purge(context.get_admin_context(), self.name) + self.assertEqual(len(starting_inst_list), + len(instance_types.get_all_types()), + 'instance type not purged') + def test_get_all_instance_types(self): """Ensures that all instance types can be retrieved""" -- cgit From 3609f1da7e1383b76295c6e8bd1d1dc0d798aa63 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 18 Feb 2011 16:00:22 -0800 Subject: pep8 --- nova/tests/test_instance_types.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index fe052110f..705144cb0 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -59,7 +59,6 @@ class InstanceTypeTestCase(test.TestCase): len(instance_types.get_all_types()), 'instance type not purged') - def test_get_all_instance_types(self): """Ensures that all instance types can be retrieved""" session = get_session() -- cgit From d0733621758985bdd621a05c7c8a53fe27aa62f2 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Sat, 19 Feb 2011 01:28:26 +0100 Subject: Wrap iptables calls in a semaphore. --- nova/network/linux_net.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index b81a704bf..ecda450bf 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -20,6 +20,8 @@ Implements vlans, bridges, and iptables rules using linux utilities. import inspect import os +from eventlet import semaphore + from nova import db from nova import exception from nova import flags @@ -149,8 +151,7 @@ class IptablesManager(object): # Wrap the builtin chains builtin_chains = {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], - 'nat': ['PREROUTING', 'INPUT', - 'OUTPUT', 'POSTROUTING']} + 'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']} for table, chains in builtin_chains.iteritems(): for chain in chains: @@ -158,22 +159,24 @@ class IptablesManager(object): self.ipv4[table].add_rule(chain, '-j %s-%s' % (binary_name, chain), wrap=False) + self.semaphore = semaphore.Semaphore() def apply(self): - s = [('iptables', self.ipv4)] - if FLAGS.use_ipv6: - s += [('ip6tables', self.ipv6)] - - for cmd, tables in s: - for table in tables: - current_table, _ = self.execute('sudo %s-save -t %s' % - (cmd, table), attempts=5) - current_lines = current_table.split('\n') - new_filter = self.modify_rules(current_lines, tables[table]) - self.execute('sudo %s-restore' % (cmd,), - process_input='\n'.join(new_filter), - attempts=5) + with self.semaphore: + s = [('iptables', self.ipv4)] + if FLAGS.use_ipv6: + s += [('ip6tables', self.ipv6)] + + for cmd, tables in s: + for table in tables: + current_table, _ = self.execute('sudo %s-save -t %s' % + (cmd, table), attempts=5) + current_lines = current_table.split('\n') + new_filter = self.modify_rules(current_lines, tables[table]) + self.execute('sudo %s-restore' % (cmd,), + process_input='\n'.join(new_filter), + attempts=5) def modify_rules(self, current_lines, table, binary=None): -- cgit From 53784c1afaa12d0a8b22248093ec1e623a1a913d Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Fri, 18 Feb 2011 17:17:47 -0800 Subject: added purge option and tightened up testing --- bin/nova-manage | 15 ++++++---- nova/compute/instance_types.py | 13 ++++++++ nova/tests/test_instance_types.py | 2 +- nova/tests/test_nova_manage.py | 62 ++++++++++++++++++++------------------- 4 files changed, 56 insertions(+), 36 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 3318a593e..8ecf626a4 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -683,9 +683,9 @@ class InstanceTypeCommands(object): vcpus, local_gb, flavorid, - swap, - rxtx_quota, - rxtx_cap): + swap=0, + rxtx_quota=0, + rxtx_cap=0): """Creates instance types / flavors arguments: name memory vcpus local_gb flavorid swap rxtx_quota rxtx_cap @@ -719,13 +719,18 @@ class InstanceTypeCommands(object): try: if purge == "--purge": instance_types.purge(name) - verb = "deleted" + verb = "purged" else: instance_types.destroy(name) - verb = "purged" + verb = "deleted" except exception.ApiError: print "Valid instance type name is required" sys.exit(1) + except exception.DBError, e: + print "DB Error: %s" % e + sys.exit(2) + except: + sys.exit(3) else: print "%s %s" % (name, verb) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index a5e0a7baf..ce4b25964 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -81,6 +81,19 @@ def destroy(name): name) +def purge(name): + """Removes instance types / flavors from database + arguments: name""" + if name == None: + raise exception.InvalidInputException(_("No instance type specified")) + else: + try: + db.instance_type_purge(context.get_admin_context(), name) + except exception.NotFound: + raise exception.ApiError(_("Unknown instance type: %s"), + name) + + def get_all_types(inactive=0): """Retrieves non-deleted instance_types. Pass true as argument if you want deleted instance types returned also.""" diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 705144cb0..1b192ca28 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -54,7 +54,7 @@ class InstanceTypeTestCase(test.TestCase): self.assertEqual(1, instance_types.get_instance_type(self.name)["deleted"]) self.assertEqual(starting_inst_list, instance_types.get_all_types()) - db.instance_type_purge(context.get_admin_context(), self.name) + instance_types.purge(self.name) self.assertEqual(len(starting_inst_list), len(instance_types.get_all_types()), 'instance type not purged') diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 03b4e3fb8..03aea53c9 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -33,12 +33,13 @@ class NovaManageTestCase(test.TestCase): order_by("flavorid desc").first() self.flavorid = str(max_flavorid["flavorid"] + 1) self.name = str(int(time.time())) + self.fnull = open(os.devnull, 'w') def teardown(self): - fnull.close() + self.fnull.close() def test_create_and_delete_instance_types(self): - fnull = open(os.devnull, 'w') + myname = self.name + "create_and_delete" retcode = subprocess.call([ "bin/nova-manage", "instance_type", @@ -51,45 +52,44 @@ class NovaManageTestCase(test.TestCase): "2", "10", "10"], - stdout=fnull) + stdout=self.fnull) self.assertEqual(0, retcode) - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "delete", self.name], stdout=fnull) + retcode = subprocess.call(["bin/nova-manage", "instance_type", + "delete", self.name], stdout=self.fnull) + self.assertEqual(0, retcode) + retcode = subprocess.call(["bin/nova-manage", "instance_type", + "delete", self.name, "--purge"], + stdout=self.fnull) self.assertEqual(0, retcode) def test_list_instance_types_or_flavors(self): - fnull = open(os.devnull, 'w') for c in ["instance_type", "flavor"]: retcode = subprocess.call(["bin/nova-manage", c, \ - "list"], stdout=fnull) + "list"], stdout=self.fnull) self.assertEqual(0, retcode) def test_list_specific_instance_type(self): - fnull = open(os.devnull, 'w') retcode = subprocess.call(["bin/nova-manage", "instance_type", "list", - "m1.medium"], stdout=fnull) + "m1.medium"], stdout=self.fnull) self.assertEqual(0, retcode) def test_should_error_on_bad_create_args(self): - fnull = open(os.devnull, 'w') # shouldn't be able to create instance type with 0 vcpus - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", self.name, "256", "0",\ - "120", self.flavorid], stdout=fnull) + retcode = subprocess.call(["bin/nova-manage", "instance_type", + "create", self.name + "bad_args", + "256", "0", "120", self.flavorid], + stdout=self.fnull) self.assertEqual(1, retcode) def test_should_fail_on_duplicate_flavorid(self): - fnull = open(os.devnull, 'w') # flavorid 1 is set in migration seed data retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", self.name, "256", "1",\ - "120", "1"], stdout=fnull) - self.assertEqual(1, retcode) + "create", self.name + "dupflavor", "256", + "1", "120", "1"], stdout=self.fnull) + self.assertEqual(3, retcode) def test_should_fail_on_duplicate_name(self): - # FIXME(ken-pepple) duplicate_name really needs to be unique - duplicate_name = "sdfsdfsafsdfsd" - fnull = open(os.devnull, 'w') + duplicate_name = self.name + "dup_name" retcode = subprocess.call([ "bin/nova-manage", "instance_type", @@ -102,27 +102,29 @@ class NovaManageTestCase(test.TestCase): "2", "10", "10"], - stdout=fnull) + stdout=self.fnull) + self.assertEqual(0, retcode) duplicate_retcode = subprocess.call([ "bin/nova-manage", "instance_type", "create", duplicate_name, - "256", + "512", "1", - "120", - self.flavorid, + "240", + str(int(self.flavorid) + 1), "2", "10", "10"], - stdout=fnull) + stdout=self.fnull) self.assertEqual(3, duplicate_retcode) - delete_retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "delete", duplicate_name], stdout=fnull) + delete_retcode = subprocess.call(["bin/nova-manage", "instance_type", + "delete", duplicate_name, "--purge"], + stdout=self.fnull) self.assertEqual(0, delete_retcode) def test_instance_type_delete_should_fail_without_valid_name(self): - fnull = open(os.devnull, 'w') - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "delete", "doesntexist"], stdout=fnull) + retcode = subprocess.call(["bin/nova-manage", "instance_type", + "delete", "doesntexist"], + stdout=self.fnull) self.assertEqual(1, retcode) -- cgit From 8684eb3aa638883ea82bbaf8eb59076f1d7e6a05 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 18 Feb 2011 17:17:51 -0800 Subject: ObjectStore doesn't use properties collection; kernel_id and ramdisk_id aren't required anyway --- nova/api/openstack/servers.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 486eca508..11a84687d 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -144,13 +144,11 @@ class Controller(wsgi.Controller): metadata stored in Glance as 'image_properties' """ def lookup(param): - _image_id = image_id - try: - return image['properties'][param] - except KeyError: - raise exception.NotFound( - _("%(param)s property not found for image %(_image_id)s") % - locals()) + properties = image.get('properties') + if properties: + return properties.get(param) + else: + return image.get(param) image_id = str(image_id) image = self._image_service.show(req.environ['nova.context'], image_id) -- cgit From aeab8eeb038ca1d1dde05705028144a78552c4f7 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 18 Feb 2011 17:27:25 -0800 Subject: Don't crash if there's no 'fixed_ip' attribute (was returning None, which was unsubscriptable) --- nova/api/openstack/servers.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 486eca508..b54e28c0c 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -63,20 +63,22 @@ def _translate_detail_keys(inst): inst_dict['status'] = power_mapping[inst_dict['status']] inst_dict['addresses'] = dict(public=[], private=[]) - # grab single private fixed ip - try: - private_ip = inst['fixed_ip']['address'] - if private_ip: - inst_dict['addresses']['private'].append(private_ip) - except KeyError: - LOG.debug(_("Failed to read private ip")) - - # grab all public floating ips - try: - for floating in inst['fixed_ip']['floating_ips']: - inst_dict['addresses']['public'].append(floating['address']) - except KeyError: - LOG.debug(_("Failed to read public ip(s)")) + fixed_ip = inst['fixed_ip'] + if fixed_ip: + # grab single private fixed ip + try: + private_ip = fixed_ip['address'] + if private_ip: + inst_dict['addresses']['private'].append(private_ip) + except KeyError: + LOG.debug(_("Failed to read private ip")) + + # grab all public floating ips + try: + for floating in fixed_ip['floating_ips']: + inst_dict['addresses']['public'].append(floating['address']) + except KeyError: + LOG.debug(_("Failed to read public ip(s)")) inst_dict['metadata'] = {} inst_dict['hostId'] = '' -- cgit From e21567404aa31c39bf1b14b8a8b2f02703fd5905 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 18 Feb 2011 21:00:58 -0800 Subject: remove the weird is_vpn logic in compute/api.py --- nova/compute/api.py | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 0d2690c72..81ea6dc53 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -100,25 +100,23 @@ class API(base.Base): "run %s more instances of this type.") % num_instances, "InstanceLimitExceeded") - is_vpn = image_id == FLAGS.vpn_image_id - if not is_vpn: - image = self.image_service.show(context, image_id) - if kernel_id is None: - kernel_id = image.get('kernel_id', None) - if ramdisk_id is None: - ramdisk_id = image.get('ramdisk_id', None) - # No kernel and ramdisk for raw images - if kernel_id == str(FLAGS.null_kernel): - kernel_id = None - ramdisk_id = None - LOG.debug(_("Creating a raw instance")) - # Make sure we have access to kernel and ramdisk (if not raw) - logging.debug("Using Kernel=%s, Ramdisk=%s" % - (kernel_id, ramdisk_id)) - if kernel_id: - self.image_service.show(context, kernel_id) - if ramdisk_id: - self.image_service.show(context, ramdisk_id) + image = self.image_service.show(context, image_id) + if kernel_id is None: + kernel_id = image.get('kernel_id', None) + if ramdisk_id is None: + ramdisk_id = image.get('ramdisk_id', None) + # No kernel and ramdisk for raw images + if kernel_id == str(FLAGS.null_kernel): + kernel_id = None + ramdisk_id = None + LOG.debug(_("Creating a raw instance")) + # Make sure we have access to kernel and ramdisk (if not raw) + logging.debug("Using Kernel=%s, Ramdisk=%s" % + (kernel_id, ramdisk_id)) + if kernel_id: + self.image_service.show(context, kernel_id) + if ramdisk_id: + self.image_service.show(context, ramdisk_id) if security_group is None: security_group = ['default'] -- cgit From e518ab4d16ec6166c0ea391af4c94aaf4d8aa2db Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 18 Feb 2011 22:49:13 -0800 Subject: replace context.user.is_admin() with context.is_admin because it is much faster --- nova/api/ec2/cloud.py | 12 ++++++------ nova/objectstore/bucket.py | 2 +- nova/objectstore/image.py | 2 +- nova/volume/api.py | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 6919cd8d2..3b8c60c31 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -290,7 +290,7 @@ class CloudController(object): for key_pair in key_pairs: # filter out the vpn keys suffix = FLAGS.vpn_key_suffix - if context.user.is_admin() or \ + if context..is_admin or \ not key_pair['name'].endswith(suffix): result.append({ 'keyName': key_pair['name'], @@ -318,7 +318,7 @@ class CloudController(object): def describe_security_groups(self, context, group_name=None, **kwargs): self.compute_api.ensure_default_security_group(context) - if context.user.is_admin(): + if context..is_admin: groups = db.security_group_get_all(context) else: groups = db.security_group_get_by_project(context, @@ -674,7 +674,7 @@ class CloudController(object): else: instances = self.compute_api.get_all(context, **kwargs) for instance in instances: - if not context.user.is_admin(): + if not context..is_admin: if instance['image_id'] == FLAGS.vpn_image_id: continue i = {} @@ -702,7 +702,7 @@ class CloudController(object): i['dnsName'] = i['publicDnsName'] or i['privateDnsName'] i['keyName'] = instance['key_name'] - if context.user.is_admin(): + if context..is_admin: i['keyName'] = '%s (%s, %s)' % (i['keyName'], instance['project_id'], instance['host']) @@ -736,7 +736,7 @@ class CloudController(object): def format_addresses(self, context): addresses = [] - if context.user.is_admin(): + if context..is_admin: iterator = db.floating_ip_get_all(context) else: iterator = db.floating_ip_get_all_by_project(context, @@ -750,7 +750,7 @@ class CloudController(object): ec2_id = id_to_ec2_id(instance_id) address_rv = {'public_ip': address, 'instance_id': ec2_id} - if context.user.is_admin(): + if context..is_admin: details = "%s (%s)" % (address_rv['instance_id'], floating_ip_ref['project_id']) address_rv['instance_id'] = details diff --git a/nova/objectstore/bucket.py b/nova/objectstore/bucket.py index 82767e52f..b213e18e8 100644 --- a/nova/objectstore/bucket.py +++ b/nova/objectstore/bucket.py @@ -107,7 +107,7 @@ class Bucket(object): def is_authorized(self, context): try: - return context.user.is_admin() or \ + return context.is_admin or \ self.owner_id == context.project_id except Exception, e: return False diff --git a/nova/objectstore/image.py b/nova/objectstore/image.py index 41e0abd80..27227e2ca 100644 --- a/nova/objectstore/image.py +++ b/nova/objectstore/image.py @@ -69,7 +69,7 @@ class Image(object): # but only modified by admin or owner. try: return (self.metadata['isPublic'] and readonly) or \ - context.user.is_admin() or \ + context.is_admin or \ self.metadata['imageOwnerId'] == context.project_id except: return False diff --git a/nova/volume/api.py b/nova/volume/api.py index 478c83486..5201c7d90 100644 --- a/nova/volume/api.py +++ b/nova/volume/api.py @@ -85,7 +85,7 @@ class API(base.Base): return self.db.volume_get(context, volume_id) def get_all(self, context): - if context.user.is_admin(): + if context.is_admin: return self.db.volume_get_all(context) return self.db.volume_get_all_by_project(context, context.project_id) -- cgit From c4a0f200b023ba96024d58bf731307483dcbe288 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 18 Feb 2011 23:00:28 -0800 Subject: remove extra . --- nova/api/ec2/cloud.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 3b8c60c31..05cdcc57e 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -290,7 +290,7 @@ class CloudController(object): for key_pair in key_pairs: # filter out the vpn keys suffix = FLAGS.vpn_key_suffix - if context..is_admin or \ + if context.is_admin or \ not key_pair['name'].endswith(suffix): result.append({ 'keyName': key_pair['name'], @@ -318,7 +318,7 @@ class CloudController(object): def describe_security_groups(self, context, group_name=None, **kwargs): self.compute_api.ensure_default_security_group(context) - if context..is_admin: + if context.is_admin: groups = db.security_group_get_all(context) else: groups = db.security_group_get_by_project(context, @@ -674,7 +674,7 @@ class CloudController(object): else: instances = self.compute_api.get_all(context, **kwargs) for instance in instances: - if not context..is_admin: + if not context.is_admin: if instance['image_id'] == FLAGS.vpn_image_id: continue i = {} @@ -702,7 +702,7 @@ class CloudController(object): i['dnsName'] = i['publicDnsName'] or i['privateDnsName'] i['keyName'] = instance['key_name'] - if context..is_admin: + if context.is_admin: i['keyName'] = '%s (%s, %s)' % (i['keyName'], instance['project_id'], instance['host']) @@ -736,7 +736,7 @@ class CloudController(object): def format_addresses(self, context): addresses = [] - if context..is_admin: + if context.is_admin: iterator = db.floating_ip_get_all(context) else: iterator = db.floating_ip_get_all_by_project(context, @@ -750,7 +750,7 @@ class CloudController(object): ec2_id = id_to_ec2_id(instance_id) address_rv = {'public_ip': address, 'instance_id': ec2_id} - if context..is_admin: + if context.is_admin: details = "%s (%s)" % (address_rv['instance_id'], floating_ip_ref['project_id']) address_rv['instance_id'] = details -- cgit From a60d4cb45f4298ce39cbc34ad3c0133ba344fa66 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 18 Feb 2011 23:15:42 -0800 Subject: more optimizations context.user.id to context.user_id --- nova/api/ec2/__init__.py | 2 +- nova/api/ec2/cloud.py | 8 ++++---- nova/volume/api.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 1a06b3f01..01ef6bf6d 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -294,7 +294,7 @@ class Authorizer(wsgi.Middleware): return True if 'none' in roles: return False - return any(context.project.has_role(context.user.id, role) + return any(context.project.has_role(context.user_id, role) for role in roles) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 05cdcc57e..882cdcfc9 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -282,7 +282,7 @@ class CloudController(object): 'description': 'fixme'}]} def describe_key_pairs(self, context, key_name=None, **kwargs): - key_pairs = db.key_pair_get_all_by_user(context, context.user.id) + key_pairs = db.key_pair_get_all_by_user(context, context.user_id) if not key_name is None: key_pairs = [x for x in key_pairs if x['name'] in key_name] @@ -301,7 +301,7 @@ class CloudController(object): def create_key_pair(self, context, key_name, **kwargs): LOG.audit(_("Create key pair %s"), key_name, context=context) - data = _gen_key(context, context.user.id, key_name) + data = _gen_key(context, context.user_id, key_name) return {'keyName': key_name, 'keyFingerprint': data['fingerprint'], 'keyMaterial': data['private_key']} @@ -310,7 +310,7 @@ class CloudController(object): def delete_key_pair(self, context, key_name, **kwargs): LOG.audit(_("Delete key pair %s"), key_name, context=context) try: - db.key_pair_destroy(context, context.user.id, key_name) + db.key_pair_destroy(context, context.user_id, key_name) except exception.NotFound: # aws returns true even if the key doesn't exist pass @@ -494,7 +494,7 @@ class CloudController(object): if db.security_group_exists(context, context.project_id, group_name): raise exception.ApiError(_('group %s already exists') % group_name) - group = {'user_id': context.user.id, + group = {'user_id': context.user_id, 'project_id': context.project_id, 'name': group_name, 'description': group_description} diff --git a/nova/volume/api.py b/nova/volume/api.py index 5201c7d90..2f4494845 100644 --- a/nova/volume/api.py +++ b/nova/volume/api.py @@ -49,7 +49,7 @@ class API(base.Base): options = { 'size': size, - 'user_id': context.user.id, + 'user_id': context.user_id, 'project_id': context.project_id, 'availability_zone': FLAGS.storage_availability_zone, 'status': "creating", -- cgit From 990a0fdce67971e81665aa2151e43b071d8bcb7c Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 18 Feb 2011 23:33:06 -0800 Subject: Fix FakeAuthManager so that unit tests pass; I believe it was matching the wrong field --- nova/tests/api/openstack/fakes.py | 8 ++++++-- nova/tests/api/openstack/test_auth.py | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index fb282f1c9..e0b7b8029 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -221,7 +221,8 @@ class FakeAuthDatabase(object): class FakeAuthManager(object): auth_data = {} - def add_user(self, key, user): + def add_user(self, user): + key = user.id FakeAuthManager.auth_data[key] = user def get_user(self, uid): @@ -234,7 +235,10 @@ class FakeAuthManager(object): return None def get_user_from_access_key(self, key): - return FakeAuthManager.auth_data.get(key, None) + for k, v in FakeAuthManager.auth_data.iteritems(): + if v.access == key: + return v + return None class FakeRateLimiter(object): diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py index 0dd65d321..eab78b50c 100644 --- a/nova/tests/api/openstack/test_auth.py +++ b/nova/tests/api/openstack/test_auth.py @@ -48,7 +48,7 @@ class Test(unittest.TestCase): def test_authorize_user(self): f = fakes.FakeAuthManager() - f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None)) + f.add_user(nova.auth.manager.User(1, 'herp', 'herp', 'derp', None)) req = webob.Request.blank('/v1.0/') req.headers['X-Auth-User'] = 'herp' @@ -62,7 +62,7 @@ class Test(unittest.TestCase): def test_authorize_token(self): f = fakes.FakeAuthManager() - f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None)) + f.add_user(nova.auth.manager.User(1, 'herp', 'herp', 'derp', None)) req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'}) req.headers['X-Auth-User'] = 'herp' @@ -144,7 +144,7 @@ class TestLimiter(unittest.TestCase): def test_authorize_token(self): f = fakes.FakeAuthManager() - f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None)) + f.add_user(nova.auth.manager.User(1, 'herp', 'herp', 'derp', None)) req = webob.Request.blank('/v1.0/') req.headers['X-Auth-User'] = 'herp' -- cgit From a3c6106f99085da69ab3c51b80135d3cedd81c4d Mon Sep 17 00:00:00 2001 From: Jesse Andrews Date: Sat, 19 Feb 2011 01:22:27 -0800 Subject: store time when RequestLogging starts instead of using context's time --- nova/api/ec2/__init__.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 1a06b3f01..4e7e3267d 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -56,6 +56,7 @@ class RequestLogging(wsgi.Middleware): @webob.dec.wsgify def __call__(self, req): + self.start = datetime.datetime.utcnow() rv = req.get_response(self.application) self.log_request_completion(rv, req) return rv @@ -66,13 +67,9 @@ class RequestLogging(wsgi.Middleware): controller = controller.__class__.__name__ action = request.environ.get('ec2.action', None) ctxt = request.environ.get('ec2.context', None) - seconds = 'X' - microseconds = 'X' - if ctxt: - delta = datetime.datetime.utcnow() - \ - ctxt.timestamp - seconds = delta.seconds - microseconds = delta.microseconds + delta = datetime.datetime.utcnow() - self.start + seconds = delta.seconds + microseconds = delta.microseconds LOG.info( "%s.%ss %s %s %s %s:%s %s [%s] %s %s", seconds, -- cgit From 86a858d076c62ddd7c27e04300aeb5d21111b986 Mon Sep 17 00:00:00 2001 From: Jesse Andrews Date: Sat, 19 Feb 2011 01:27:48 -0800 Subject: pass start time as a param instead of making it an attribute --- nova/api/ec2/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 4e7e3267d..cda34bc3a 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -56,18 +56,18 @@ class RequestLogging(wsgi.Middleware): @webob.dec.wsgify def __call__(self, req): - self.start = datetime.datetime.utcnow() + start = datetime.datetime.utcnow() rv = req.get_response(self.application) - self.log_request_completion(rv, req) + self.log_request_completion(rv, req, start) return rv - def log_request_completion(self, response, request): + def log_request_completion(self, response, request, start): controller = request.environ.get('ec2.controller', None) if controller: controller = controller.__class__.__name__ action = request.environ.get('ec2.action', None) ctxt = request.environ.get('ec2.context', None) - delta = datetime.datetime.utcnow() - self.start + delta = datetime.datetime.utcnow() - start seconds = delta.seconds microseconds = delta.microseconds LOG.info( -- cgit From d4a37dc28daf990d903ffd14607862cb2eafb1c8 Mon Sep 17 00:00:00 2001 From: Jesse Andrews Date: Sat, 19 Feb 2011 01:36:13 -0800 Subject: move from datetime.datetime.utcnow -> utils.utcnow --- nova/api/ec2/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index cda34bc3a..f892123fd 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -20,7 +20,6 @@ Starting point for routing EC2 requests. """ -import datetime import webob import webob.dec import webob.exc @@ -56,7 +55,7 @@ class RequestLogging(wsgi.Middleware): @webob.dec.wsgify def __call__(self, req): - start = datetime.datetime.utcnow() + start = utils.utcnow() rv = req.get_response(self.application) self.log_request_completion(rv, req, start) return rv @@ -67,7 +66,7 @@ class RequestLogging(wsgi.Middleware): controller = controller.__class__.__name__ action = request.environ.get('ec2.action', None) ctxt = request.environ.get('ec2.context', None) - delta = datetime.datetime.utcnow() - start + delta = utils.utcnow() - start seconds = delta.seconds microseconds = delta.microseconds LOG.info( -- cgit From 915d6e70106b30ed6919fa850749b8041c3e690d Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 19 Feb 2011 01:51:13 -0800 Subject: pep8 leftover --- nova/tests/api/openstack/test_zones.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 5542a1cf3..df497ef1b 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -57,8 +57,7 @@ def zone_get_all(context): dict(id=1, api_url='http://foo.com', username='bob', password='xxx'), dict(id=2, api_url='http://blah.com', username='alice', - password='qwerty') - ] + password='qwerty')] class ZonesTest(unittest.TestCase): -- cgit From 89a63f53116b04a8d0681265ba8ce71eeeb5be0b Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Sat, 19 Feb 2011 01:59:07 -0800 Subject: fix ec2 launchtime response not in iso format bug --- nova/api/ec2/apirequest.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py index 7e72d67fb..00b527d62 100644 --- a/nova/api/ec2/apirequest.py +++ b/nova/api/ec2/apirequest.py @@ -20,6 +20,7 @@ APIRequest class """ +import datetime import re # TODO(termie): replace minidom with etree from xml.dom import minidom @@ -171,6 +172,8 @@ class APIRequest(object): self._render_dict(xml, data_el, data.__dict__) elif isinstance(data, bool): data_el.appendChild(xml.createTextNode(str(data).lower())) + elif isinstance(data, datetime.datetime): + data_el.appendChild(xml.createTextNode(data.isoformat())) elif data != None: data_el.appendChild(xml.createTextNode(str(data))) -- cgit From a7eed42c57fe7eaf6f2981a88a74a81a6890198c Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Sun, 20 Feb 2011 20:56:14 +0100 Subject: puppet scripts only there as an example, should be moved to some other place if they are still necessary --- contrib/puppet/files/etc/default/nova-compute | 1 - contrib/puppet/files/etc/default/nova-volume | 1 - contrib/puppet/files/etc/issue | 5 - contrib/puppet/files/etc/libvirt/qemu.conf | 170 -------- contrib/puppet/files/etc/lvm/lvm.conf | 463 -------------------- contrib/puppet/files/etc/nova.conf | 28 -- contrib/puppet/files/production/boto.cfg | 3 - contrib/puppet/files/production/genvpn.sh | 35 -- .../files/production/libvirt.qemu.xml.template | 35 -- contrib/puppet/files/production/my.cnf | 137 ------ contrib/puppet/files/production/nova-iptables | 187 --------- contrib/puppet/files/production/nova-iscsi-dev.sh | 19 - contrib/puppet/files/production/setup_data.sh | 6 - contrib/puppet/files/production/slap.sh | 261 ------------ contrib/puppet/fileserver.conf | 8 - contrib/puppet/manifests/classes/apt.pp | 1 - contrib/puppet/manifests/classes/issue.pp | 14 - contrib/puppet/manifests/classes/kern_module.pp | 34 -- contrib/puppet/manifests/classes/loopback.pp | 6 - contrib/puppet/manifests/classes/lvm.pp | 8 - contrib/puppet/manifests/classes/lvmconf.pp | 8 - contrib/puppet/manifests/classes/nova.pp | 464 --------------------- contrib/puppet/manifests/classes/swift.pp | 7 - contrib/puppet/manifests/site.pp | 120 ------ contrib/puppet/manifests/templates.pp | 21 - contrib/puppet/puppet.conf | 11 - contrib/puppet/templates/haproxy.cfg.erb | 39 -- contrib/puppet/templates/monitrc-nova-api.erb | 138 ------ contrib/puppet/templates/nova-iptables.erb | 10 - .../templates/production/nova-common.conf.erb | 55 --- .../puppet/templates/production/nova-nova.conf.erb | 21 - 31 files changed, 2316 deletions(-) delete mode 100644 contrib/puppet/files/etc/default/nova-compute delete mode 100644 contrib/puppet/files/etc/default/nova-volume delete mode 100644 contrib/puppet/files/etc/issue delete mode 100644 contrib/puppet/files/etc/libvirt/qemu.conf delete mode 100644 contrib/puppet/files/etc/lvm/lvm.conf delete mode 100644 contrib/puppet/files/etc/nova.conf delete mode 100644 contrib/puppet/files/production/boto.cfg delete mode 100644 contrib/puppet/files/production/genvpn.sh delete mode 100644 contrib/puppet/files/production/libvirt.qemu.xml.template delete mode 100644 contrib/puppet/files/production/my.cnf delete mode 100755 contrib/puppet/files/production/nova-iptables delete mode 100644 contrib/puppet/files/production/nova-iscsi-dev.sh delete mode 100755 contrib/puppet/files/production/setup_data.sh delete mode 100755 contrib/puppet/files/production/slap.sh delete mode 100644 contrib/puppet/fileserver.conf delete mode 100644 contrib/puppet/manifests/classes/apt.pp delete mode 100644 contrib/puppet/manifests/classes/issue.pp delete mode 100644 contrib/puppet/manifests/classes/kern_module.pp delete mode 100644 contrib/puppet/manifests/classes/loopback.pp delete mode 100644 contrib/puppet/manifests/classes/lvm.pp delete mode 100644 contrib/puppet/manifests/classes/lvmconf.pp delete mode 100644 contrib/puppet/manifests/classes/nova.pp delete mode 100644 contrib/puppet/manifests/classes/swift.pp delete mode 100644 contrib/puppet/manifests/site.pp delete mode 100644 contrib/puppet/manifests/templates.pp delete mode 100644 contrib/puppet/puppet.conf delete mode 100644 contrib/puppet/templates/haproxy.cfg.erb delete mode 100644 contrib/puppet/templates/monitrc-nova-api.erb delete mode 100644 contrib/puppet/templates/nova-iptables.erb delete mode 100644 contrib/puppet/templates/production/nova-common.conf.erb delete mode 100644 contrib/puppet/templates/production/nova-nova.conf.erb diff --git a/contrib/puppet/files/etc/default/nova-compute b/contrib/puppet/files/etc/default/nova-compute deleted file mode 100644 index 8bd7d091c..000000000 --- a/contrib/puppet/files/etc/default/nova-compute +++ /dev/null @@ -1 +0,0 @@ -ENABLED=true diff --git a/contrib/puppet/files/etc/default/nova-volume b/contrib/puppet/files/etc/default/nova-volume deleted file mode 100644 index 8bd7d091c..000000000 --- a/contrib/puppet/files/etc/default/nova-volume +++ /dev/null @@ -1 +0,0 @@ -ENABLED=true diff --git a/contrib/puppet/files/etc/issue b/contrib/puppet/files/etc/issue deleted file mode 100644 index 8c567221b..000000000 --- a/contrib/puppet/files/etc/issue +++ /dev/null @@ -1,5 +0,0 @@ ------------------------------------------------ - - Welcome to your OpenStack installation! - ------------------------------------------------ diff --git a/contrib/puppet/files/etc/libvirt/qemu.conf b/contrib/puppet/files/etc/libvirt/qemu.conf deleted file mode 100644 index 7839f12e5..000000000 --- a/contrib/puppet/files/etc/libvirt/qemu.conf +++ /dev/null @@ -1,170 +0,0 @@ -# Master configuration file for the QEMU driver. -# All settings described here are optional - if omitted, sensible -# defaults are used. - -# VNC is configured to listen on 127.0.0.1 by default. -# To make it listen on all public interfaces, uncomment -# this next option. -# -# NB, strong recommendation to enable TLS + x509 certificate -# verification when allowing public access -# -# vnc_listen = "0.0.0.0" - - -# Enable use of TLS encryption on the VNC server. This requires -# a VNC client which supports the VeNCrypt protocol extension. -# Examples include vinagre, virt-viewer, virt-manager and vencrypt -# itself. UltraVNC, RealVNC, TightVNC do not support this -# -# It is necessary to setup CA and issue a server certificate -# before enabling this. -# -# vnc_tls = 1 - - -# Use of TLS requires that x509 certificates be issued. The -# default it to keep them in /etc/pki/libvirt-vnc. This directory -# must contain -# -# ca-cert.pem - the CA master certificate -# server-cert.pem - the server certificate signed with ca-cert.pem -# server-key.pem - the server private key -# -# This option allows the certificate directory to be changed -# -# vnc_tls_x509_cert_dir = "/etc/pki/libvirt-vnc" - - -# The default TLS configuration only uses certificates for the server -# allowing the client to verify the server's identity and establish -# and encrypted channel. -# -# It is possible to use x509 certificates for authentication too, by -# issuing a x509 certificate to every client who needs to connect. -# -# Enabling this option will reject any client who does not have a -# certificate signed by the CA in /etc/pki/libvirt-vnc/ca-cert.pem -# -# vnc_tls_x509_verify = 1 - - -# The default VNC password. Only 8 letters are significant for -# VNC passwords. This parameter is only used if the per-domain -# XML config does not already provide a password. To allow -# access without passwords, leave this commented out. An empty -# string will still enable passwords, but be rejected by QEMU -# effectively preventing any use of VNC. Obviously change this -# example here before you set this -# -# vnc_password = "XYZ12345" - - -# Enable use of SASL encryption on the VNC server. This requires -# a VNC client which supports the SASL protocol extension. -# Examples include vinagre, virt-viewer and virt-manager -# itself. UltraVNC, RealVNC, TightVNC do not support this -# -# It is necessary to configure /etc/sasl2/qemu.conf to choose -# the desired SASL plugin (eg, GSSPI for Kerberos) -# -# vnc_sasl = 1 - - -# The default SASL configuration file is located in /etc/sasl2/ -# When running libvirtd unprivileged, it may be desirable to -# override the configs in this location. Set this parameter to -# point to the directory, and create a qemu.conf in that location -# -# vnc_sasl_dir = "/some/directory/sasl2" - - - - -# The default security driver is SELinux. If SELinux is disabled -# on the host, then the security driver will automatically disable -# itself. If you wish to disable QEMU SELinux security driver while -# leaving SELinux enabled for the host in general, then set this -# to 'none' instead -# -# security_driver = "selinux" - - -# The user ID for QEMU processes run by the system instance -user = "root" - -# The group ID for QEMU processes run by the system instance -group = "root" - -# Whether libvirt should dynamically change file ownership -# to match the configured user/group above. Defaults to 1. -# Set to 0 to disable file ownership changes. -#dynamic_ownership = 1 - - -# What cgroup controllers to make use of with QEMU guests -# -# - 'cpu' - use for schedular tunables -# - 'devices' - use for device whitelisting -# -# NB, even if configured here, they won't be used unless -# the adminsitrator has mounted cgroups. eg -# -# mkdir /dev/cgroup -# mount -t cgroup -o devices,cpu none /dev/cgroup -# -# They can be mounted anywhere, and different controlers -# can be mounted in different locations. libvirt will detect -# where they are located. -# -# cgroup_controllers = [ "cpu", "devices" ] - -# This is the basic set of devices allowed / required by -# all virtual machines. -# -# As well as this, any configured block backed disks, -# all sound device, and all PTY devices are allowed. -# -# This will only need setting if newer QEMU suddenly -# wants some device we don't already know a bout. -# -#cgroup_device_acl = [ -# "/dev/null", "/dev/full", "/dev/zero", -# "/dev/random", "/dev/urandom", -# "/dev/ptmx", "/dev/kvm", "/dev/kqemu", -# "/dev/rtc", "/dev/hpet", "/dev/net/tun", -#] - -# The default format for Qemu/KVM guest save images is raw; that is, the -# memory from the domain is dumped out directly to a file. If you have -# guests with a large amount of memory, however, this can take up quite -# a bit of space. If you would like to compress the images while they -# are being saved to disk, you can also set "lzop", "gzip", "bzip2", or "xz" -# for save_image_format. Note that this means you slow down the process of -# saving a domain in order to save disk space; the list above is in descending -# order by performance and ascending order by compression ratio. -# -# save_image_format = "raw" - -# If provided by the host and a hugetlbfs mount point is configured, -# a guest may request huge page backing. When this mount point is -# unspecified here, determination of a host mount point in /proc/mounts -# will be attempted. Specifying an explicit mount overrides detection -# of the same in /proc/mounts. Setting the mount point to "" will -# disable guest hugepage backing. -# -# NB, within this mount point, guests will create memory backing files -# in a location of $MOUNTPOINT/libvirt/qemu - -# hugetlbfs_mount = "/dev/hugepages" - -# mac_filter enables MAC addressed based filtering on bridge ports. -# This currently requires ebtables to be installed. -# -# mac_filter = 1 - -# By default, PCI devices below non-ACS switch are not allowed to be assigned -# to guests. By setting relaxed_acs_check to 1 such devices will be allowed to -# be assigned to guests. -# -# relaxed_acs_check = 1 diff --git a/contrib/puppet/files/etc/lvm/lvm.conf b/contrib/puppet/files/etc/lvm/lvm.conf deleted file mode 100644 index 4e814ad49..000000000 --- a/contrib/puppet/files/etc/lvm/lvm.conf +++ /dev/null @@ -1,463 +0,0 @@ -# This is an example configuration file for the LVM2 system. -# It contains the default settings that would be used if there was no -# /etc/lvm/lvm.conf file. -# -# Refer to 'man lvm.conf' for further information including the file layout. -# -# To put this file in a different directory and override /etc/lvm set -# the environment variable LVM_SYSTEM_DIR before running the tools. - - -# This section allows you to configure which block devices should -# be used by the LVM system. -devices { - - # Where do you want your volume groups to appear ? - dir = "/dev" - - # An array of directories that contain the device nodes you wish - # to use with LVM2. - scan = [ "/dev" ] - - # If several entries in the scanned directories correspond to the - # same block device and the tools need to display a name for device, - # all the pathnames are matched against each item in the following - # list of regular expressions in turn and the first match is used. - preferred_names = [ ] - - # Try to avoid using undescriptive /dev/dm-N names, if present. - # preferred_names = [ "^/dev/mpath/", "^/dev/mapper/mpath", "^/dev/[hs]d" ] - - # A filter that tells LVM2 to only use a restricted set of devices. - # The filter consists of an array of regular expressions. These - # expressions can be delimited by a character of your choice, and - # prefixed with either an 'a' (for accept) or 'r' (for reject). - # The first expression found to match a device name determines if - # the device will be accepted or rejected (ignored). Devices that - # don't match any patterns are accepted. - - # Be careful if there there are symbolic links or multiple filesystem - # entries for the same device as each name is checked separately against - # the list of patterns. The effect is that if any name matches any 'a' - # pattern, the device is accepted; otherwise if any name matches any 'r' - # pattern it is rejected; otherwise it is accepted. - - # Don't have more than one filter line active at once: only one gets used. - - # Run vgscan after you change this parameter to ensure that - # the cache file gets regenerated (see below). - # If it doesn't do what you expect, check the output of 'vgscan -vvvv'. - - - # By default we accept every block device: - filter = [ "r|/dev/etherd/.*|", "r|/dev/block/.*|", "a/.*/" ] - - # Exclude the cdrom drive - # filter = [ "r|/dev/cdrom|" ] - - # When testing I like to work with just loopback devices: - # filter = [ "a/loop/", "r/.*/" ] - - # Or maybe all loops and ide drives except hdc: - # filter =[ "a|loop|", "r|/dev/hdc|", "a|/dev/ide|", "r|.*|" ] - - # Use anchors if you want to be really specific - # filter = [ "a|^/dev/hda8$|", "r/.*/" ] - - # The results of the filtering are cached on disk to avoid - # rescanning dud devices (which can take a very long time). - # By default this cache is stored in the /etc/lvm/cache directory - # in a file called '.cache'. - # It is safe to delete the contents: the tools regenerate it. - # (The old setting 'cache' is still respected if neither of - # these new ones is present.) - cache_dir = "/etc/lvm/cache" - cache_file_prefix = "" - - # You can turn off writing this cache file by setting this to 0. - write_cache_state = 1 - - # Advanced settings. - - # List of pairs of additional acceptable block device types found - # in /proc/devices with maximum (non-zero) number of partitions. - # types = [ "fd", 16 ] - - # If sysfs is mounted (2.6 kernels) restrict device scanning to - # the block devices it believes are valid. - # 1 enables; 0 disables. - sysfs_scan = 1 - - # By default, LVM2 will ignore devices used as components of - # software RAID (md) devices by looking for md superblocks. - # 1 enables; 0 disables. - md_component_detection = 1 - - # By default, if a PV is placed directly upon an md device, LVM2 - # will align its data blocks with the md device's stripe-width. - # 1 enables; 0 disables. - md_chunk_alignment = 1 - - # By default, the start of a PV's data area will be a multiple of - # the 'minimum_io_size' or 'optimal_io_size' exposed in sysfs. - # - minimum_io_size - the smallest request the device can perform - # w/o incurring a read-modify-write penalty (e.g. MD's chunk size) - # - optimal_io_size - the device's preferred unit of receiving I/O - # (e.g. MD's stripe width) - # minimum_io_size is used if optimal_io_size is undefined (0). - # If md_chunk_alignment is enabled, that detects the optimal_io_size. - # This setting takes precedence over md_chunk_alignment. - # 1 enables; 0 disables. - data_alignment_detection = 1 - - # Alignment (in KB) of start of data area when creating a new PV. - # If a PV is placed directly upon an md device and md_chunk_alignment or - # data_alignment_detection is enabled this parameter is ignored. - # Set to 0 for the default alignment of 64KB or page size, if larger. - data_alignment = 0 - - # By default, the start of the PV's aligned data area will be shifted by - # the 'alignment_offset' exposed in sysfs. This offset is often 0 but - # may be non-zero; e.g.: certain 4KB sector drives that compensate for - # windows partitioning will have an alignment_offset of 3584 bytes - # (sector 7 is the lowest aligned logical block, the 4KB sectors start - # at LBA -1, and consequently sector 63 is aligned on a 4KB boundary). - # 1 enables; 0 disables. - data_alignment_offset_detection = 1 - - # If, while scanning the system for PVs, LVM2 encounters a device-mapper - # device that has its I/O suspended, it waits for it to become accessible. - # Set this to 1 to skip such devices. This should only be needed - # in recovery situations. - ignore_suspended_devices = 0 -} - -# This section that allows you to configure the nature of the -# information that LVM2 reports. -log { - - # Controls the messages sent to stdout or stderr. - # There are three levels of verbosity, 3 being the most verbose. - verbose = 0 - - # Should we send log messages through syslog? - # 1 is yes; 0 is no. - syslog = 1 - - # Should we log error and debug messages to a file? - # By default there is no log file. - #file = "/var/log/lvm2.log" - - # Should we overwrite the log file each time the program is run? - # By default we append. - overwrite = 0 - - # What level of log messages should we send to the log file and/or syslog? - # There are 6 syslog-like log levels currently in use - 2 to 7 inclusive. - # 7 is the most verbose (LOG_DEBUG). - level = 0 - - # Format of output messages - # Whether or not (1 or 0) to indent messages according to their severity - indent = 1 - - # Whether or not (1 or 0) to display the command name on each line output - command_names = 0 - - # A prefix to use before the message text (but after the command name, - # if selected). Default is two spaces, so you can see/grep the severity - # of each message. - prefix = " " - - # To make the messages look similar to the original LVM tools use: - # indent = 0 - # command_names = 1 - # prefix = " -- " - - # Set this if you want log messages during activation. - # Don't use this in low memory situations (can deadlock). - # activation = 0 -} - -# Configuration of metadata backups and archiving. In LVM2 when we -# talk about a 'backup' we mean making a copy of the metadata for the -# *current* system. The 'archive' contains old metadata configurations. -# Backups are stored in a human readeable text format. -backup { - - # Should we maintain a backup of the current metadata configuration ? - # Use 1 for Yes; 0 for No. - # Think very hard before turning this off! - backup = 1 - - # Where shall we keep it ? - # Remember to back up this directory regularly! - backup_dir = "/etc/lvm/backup" - - # Should we maintain an archive of old metadata configurations. - # Use 1 for Yes; 0 for No. - # On by default. Think very hard before turning this off. - archive = 1 - - # Where should archived files go ? - # Remember to back up this directory regularly! - archive_dir = "/etc/lvm/archive" - - # What is the minimum number of archive files you wish to keep ? - retain_min = 10 - - # What is the minimum time you wish to keep an archive file for ? - retain_days = 30 -} - -# Settings for the running LVM2 in shell (readline) mode. -shell { - - # Number of lines of history to store in ~/.lvm_history - history_size = 100 -} - - -# Miscellaneous global LVM2 settings -global { - - # The file creation mask for any files and directories created. - # Interpreted as octal if the first digit is zero. - umask = 077 - - # Allow other users to read the files - #umask = 022 - - # Enabling test mode means that no changes to the on disk metadata - # will be made. Equivalent to having the -t option on every - # command. Defaults to off. - test = 0 - - # Default value for --units argument - units = "h" - - # Since version 2.02.54, the tools distinguish between powers of - # 1024 bytes (e.g. KiB, MiB, GiB) and powers of 1000 bytes (e.g. - # KB, MB, GB). - # If you have scripts that depend on the old behaviour, set this to 0 - # temporarily until you update them. - si_unit_consistency = 1 - - # Whether or not to communicate with the kernel device-mapper. - # Set to 0 if you want to use the tools to manipulate LVM metadata - # without activating any logical volumes. - # If the device-mapper kernel driver is not present in your kernel - # setting this to 0 should suppress the error messages. - activation = 1 - - # If we can't communicate with device-mapper, should we try running - # the LVM1 tools? - # This option only applies to 2.4 kernels and is provided to help you - # switch between device-mapper kernels and LVM1 kernels. - # The LVM1 tools need to be installed with .lvm1 suffices - # e.g. vgscan.lvm1 and they will stop working after you start using - # the new lvm2 on-disk metadata format. - # The default value is set when the tools are built. - # fallback_to_lvm1 = 0 - - # The default metadata format that commands should use - "lvm1" or "lvm2". - # The command line override is -M1 or -M2. - # Defaults to "lvm2". - # format = "lvm2" - - # Location of proc filesystem - proc = "/proc" - - # Type of locking to use. Defaults to local file-based locking (1). - # Turn locking off by setting to 0 (dangerous: risks metadata corruption - # if LVM2 commands get run concurrently). - # Type 2 uses the external shared library locking_library. - # Type 3 uses built-in clustered locking. - # Type 4 uses read-only locking which forbids any operations that might - # change metadata. - locking_type = 1 - - # Set to 0 to fail when a lock request cannot be satisfied immediately. - wait_for_locks = 1 - - # If using external locking (type 2) and initialisation fails, - # with this set to 1 an attempt will be made to use the built-in - # clustered locking. - # If you are using a customised locking_library you should set this to 0. - fallback_to_clustered_locking = 1 - - # If an attempt to initialise type 2 or type 3 locking failed, perhaps - # because cluster components such as clvmd are not running, with this set - # to 1 an attempt will be made to use local file-based locking (type 1). - # If this succeeds, only commands against local volume groups will proceed. - # Volume Groups marked as clustered will be ignored. - fallback_to_local_locking = 1 - - # Local non-LV directory that holds file-based locks while commands are - # in progress. A directory like /tmp that may get wiped on reboot is OK. - locking_dir = "/var/lock/lvm" - - # Whenever there are competing read-only and read-write access requests for - # a volume group's metadata, instead of always granting the read-only - # requests immediately, delay them to allow the read-write requests to be - # serviced. Without this setting, write access may be stalled by a high - # volume of read-only requests. - # NB. This option only affects locking_type = 1 viz. local file-based - # locking. - prioritise_write_locks = 1 - - # Other entries can go here to allow you to load shared libraries - # e.g. if support for LVM1 metadata was compiled as a shared library use - # format_libraries = "liblvm2format1.so" - # Full pathnames can be given. - - # Search this directory first for shared libraries. - # library_dir = "/lib/lvm2" - - # The external locking library to load if locking_type is set to 2. - # locking_library = "liblvm2clusterlock.so" -} - -activation { - # Set to 0 to disable udev syncronisation (if compiled into the binaries). - # Processes will not wait for notification from udev. - # They will continue irrespective of any possible udev processing - # in the background. You should only use this if udev is not running - # or has rules that ignore the devices LVM2 creates. - # The command line argument --nodevsync takes precedence over this setting. - # If set to 1 when udev is not running, and there are LVM2 processes - # waiting for udev, run 'dmsetup udevcomplete_all' manually to wake them up. - udev_sync = 1 - - # How to fill in missing stripes if activating an incomplete volume. - # Using "error" will make inaccessible parts of the device return - # I/O errors on access. You can instead use a device path, in which - # case, that device will be used to in place of missing stripes. - # But note that using anything other than "error" with mirrored - # or snapshotted volumes is likely to result in data corruption. - missing_stripe_filler = "error" - - # How much stack (in KB) to reserve for use while devices suspended - reserved_stack = 256 - - # How much memory (in KB) to reserve for use while devices suspended - reserved_memory = 8192 - - # Nice value used while devices suspended - process_priority = -18 - - # If volume_list is defined, each LV is only activated if there is a - # match against the list. - # "vgname" and "vgname/lvname" are matched exactly. - # "@tag" matches any tag set in the LV or VG. - # "@*" matches if any tag defined on the host is also set in the LV or VG - # - # volume_list = [ "vg1", "vg2/lvol1", "@tag1", "@*" ] - - # Size (in KB) of each copy operation when mirroring - mirror_region_size = 512 - - # Setting to use when there is no readahead value stored in the metadata. - # - # "none" - Disable readahead. - # "auto" - Use default value chosen by kernel. - readahead = "auto" - - # 'mirror_image_fault_policy' and 'mirror_log_fault_policy' define - # how a device failure affecting a mirror is handled. - # A mirror is composed of mirror images (copies) and a log. - # A disk log ensures that a mirror does not need to be re-synced - # (all copies made the same) every time a machine reboots or crashes. - # - # In the event of a failure, the specified policy will be used to determine - # what happens. This applies to automatic repairs (when the mirror is being - # monitored by dmeventd) and to manual lvconvert --repair when - # --use-policies is given. - # - # "remove" - Simply remove the faulty device and run without it. If - # the log device fails, the mirror would convert to using - # an in-memory log. This means the mirror will not - # remember its sync status across crashes/reboots and - # the entire mirror will be re-synced. If a - # mirror image fails, the mirror will convert to a - # non-mirrored device if there is only one remaining good - # copy. - # - # "allocate" - Remove the faulty device and try to allocate space on - # a new device to be a replacement for the failed device. - # Using this policy for the log is fast and maintains the - # ability to remember sync state through crashes/reboots. - # Using this policy for a mirror device is slow, as it - # requires the mirror to resynchronize the devices, but it - # will preserve the mirror characteristic of the device. - # This policy acts like "remove" if no suitable device and - # space can be allocated for the replacement. - # - # "allocate_anywhere" - Not yet implemented. Useful to place the log device - # temporarily on same physical volume as one of the mirror - # images. This policy is not recommended for mirror devices - # since it would break the redundant nature of the mirror. This - # policy acts like "remove" if no suitable device and space can - # be allocated for the replacement. - - mirror_log_fault_policy = "allocate" - mirror_device_fault_policy = "remove" -} - - -#################### -# Advanced section # -#################### - -# Metadata settings -# -# metadata { - # Default number of copies of metadata to hold on each PV. 0, 1 or 2. - # You might want to override it from the command line with 0 - # when running pvcreate on new PVs which are to be added to large VGs. - - # pvmetadatacopies = 1 - - # Approximate default size of on-disk metadata areas in sectors. - # You should increase this if you have large volume groups or - # you want to retain a large on-disk history of your metadata changes. - - # pvmetadatasize = 255 - - # List of directories holding live copies of text format metadata. - # These directories must not be on logical volumes! - # It's possible to use LVM2 with a couple of directories here, - # preferably on different (non-LV) filesystems, and with no other - # on-disk metadata (pvmetadatacopies = 0). Or this can be in - # addition to on-disk metadata areas. - # The feature was originally added to simplify testing and is not - # supported under low memory situations - the machine could lock up. - # - # Never edit any files in these directories by hand unless you - # you are absolutely sure you know what you are doing! Use - # the supplied toolset to make changes (e.g. vgcfgrestore). - - # dirs = [ "/etc/lvm/metadata", "/mnt/disk2/lvm/metadata2" ] -#} - -# Event daemon -# -dmeventd { - # mirror_library is the library used when monitoring a mirror device. - # - # "libdevmapper-event-lvm2mirror.so" attempts to recover from - # failures. It removes failed devices from a volume group and - # reconfigures a mirror as necessary. If no mirror library is - # provided, mirrors are not monitored through dmeventd. - - mirror_library = "libdevmapper-event-lvm2mirror.so" - - # snapshot_library is the library used when monitoring a snapshot device. - # - # "libdevmapper-event-lvm2snapshot.so" monitors the filling of - # snapshots and emits a warning through syslog, when the use of - # snapshot exceedes 80%. The warning is repeated when 85%, 90% and - # 95% of the snapshot are filled. - - snapshot_library = "libdevmapper-event-lvm2snapshot.so" -} diff --git a/contrib/puppet/files/etc/nova.conf b/contrib/puppet/files/etc/nova.conf deleted file mode 100644 index a0d64078c..000000000 --- a/contrib/puppet/files/etc/nova.conf +++ /dev/null @@ -1,28 +0,0 @@ ---ec2_url=http://192.168.255.1:8773/services/Cloud ---rabbit_host=192.168.255.1 ---redis_host=192.168.255.1 ---s3_host=192.168.255.1 ---vpn_ip=192.168.255.1 ---datastore_path=/var/lib/nova/keeper ---networks_path=/var/lib/nova/networks ---instances_path=/var/lib/nova/instances ---buckets_path=/var/lib/nova/objectstore/buckets ---images_path=/var/lib/nova/objectstore/images ---ca_path=/var/lib/nova/CA ---keys_path=/var/lib/nova/keys ---vlan_start=2000 ---vlan_end=3000 ---private_range=192.168.0.0/16 ---public_range=10.0.0.0/24 ---volume_group=vgdata ---storage_dev=/dev/sdc ---bridge_dev=eth2 ---aoe_eth_dev=eth2 ---public_interface=vlan0 ---default_kernel=aki-DEFAULT ---default_ramdisk=ari-DEFAULT ---vpn_image_id=ami-cloudpipe ---daemonize ---verbose ---syslog ---prefix=nova diff --git a/contrib/puppet/files/production/boto.cfg b/contrib/puppet/files/production/boto.cfg deleted file mode 100644 index f4a2de2b6..000000000 --- a/contrib/puppet/files/production/boto.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[Boto] -debug = 0 -num_retries = 1 diff --git a/contrib/puppet/files/production/genvpn.sh b/contrib/puppet/files/production/genvpn.sh deleted file mode 100644 index 538c3cd33..000000000 --- a/contrib/puppet/files/production/genvpn.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# 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. - -# This gets zipped and run on the cloudpipe-managed OpenVPN server -NAME=$1 -SUBJ=$2 - -mkdir -p projects/$NAME -cd projects/$NAME - -# generate a server priv key -openssl genrsa -out server.key 2048 - -# generate a server CSR -openssl req -new -key server.key -out server.csr -batch -subj "$SUBJ" - -if [ "`id -u`" != "`grep nova /etc/passwd | cut -d':' -f3`" ]; then - sudo chown -R nova:nogroup . -fi diff --git a/contrib/puppet/files/production/libvirt.qemu.xml.template b/contrib/puppet/files/production/libvirt.qemu.xml.template deleted file mode 100644 index 114dfdc01..000000000 --- a/contrib/puppet/files/production/libvirt.qemu.xml.template +++ /dev/null @@ -1,35 +0,0 @@ - - %(name)s - - hvm - %(basepath)s/kernel - %(basepath)s/ramdisk - root=/dev/vda1 console=ttyS0 - - - - - %(memory_kb)s - %(vcpus)s - - - - - - - - - - - - - - - - - diff --git a/contrib/puppet/files/production/my.cnf b/contrib/puppet/files/production/my.cnf deleted file mode 100644 index 8777bc480..000000000 --- a/contrib/puppet/files/production/my.cnf +++ /dev/null @@ -1,137 +0,0 @@ -# -# The MySQL database server configuration file. -# -# You can copy this to one of: -# - "/etc/mysql/my.cnf" to set global options, -# - "~/.my.cnf" to set user-specific options. -# -# One can use all long options that the program supports. -# Run program with --help to get a list of available options and with -# --print-defaults to see which it would actually understand and use. -# -# For explanations see -# http://dev.mysql.com/doc/mysql/en/server-system-variables.html - -# This will be passed to all mysql clients -# It has been reported that passwords should be enclosed with ticks/quotes -# escpecially if they contain "#" chars... -# Remember to edit /etc/mysql/debian.cnf when changing the socket location. -[client] -port = 3306 -socket = /var/run/mysqld/mysqld.sock - -# Here is entries for some specific programs -# The following values assume you have at least 32M ram - -# This was formally known as [safe_mysqld]. Both versions are currently parsed. -[mysqld_safe] -socket = /var/run/mysqld/mysqld.sock -nice = 0 - -[mysqld] -# -# * Basic Settings -# - -# -# * IMPORTANT -# If you make changes to these settings and your system uses apparmor, you may -# also need to also adjust /etc/apparmor.d/usr.sbin.mysqld. -# - -user = mysql -socket = /var/run/mysqld/mysqld.sock -port = 3306 -basedir = /usr -datadir = /var/lib/mysql -tmpdir = /tmp -skip-external-locking -# -# Instead of skip-networking the default is now to listen only on -# localhost which is more compatible and is not less secure. -# bind-address = 127.0.0.1 -# -# * Fine Tuning -# -innodb_buffer_pool_size = 12G -#innodb_log_file_size = 256M -innodb_log_buffer_size=4M -innodb_flush_log_at_trx_commit=2 -innodb_thread_concurrency=8 -innodb_flush_method=O_DIRECT -key_buffer = 128M -max_allowed_packet = 256M -thread_stack = 8196K -thread_cache_size = 32 -# This replaces the startup script and checks MyISAM tables if needed -# the first time they are touched -myisam-recover = BACKUP -max_connections = 1000 -table_cache = 1024 -#thread_concurrency = 10 -# -# * Query Cache Configuration -# -query_cache_limit = 32M -query_cache_size = 256M -# -# * Logging and Replication -# -# Both location gets rotated by the cronjob. -# Be aware that this log type is a performance killer. -# As of 5.1 you can enable the log at runtime! -#general_log_file = /var/log/mysql/mysql.log -#general_log = 1 - -log_error = /var/log/mysql/error.log - -# Here you can see queries with especially long duration -log_slow_queries = /var/log/mysql/mysql-slow.log -long_query_time = 2 -#log-queries-not-using-indexes -# -# The following can be used as easy to replay backup logs or for replication. -# note: if you are setting up a replication slave, see README.Debian about -# other settings you may need to change. -server-id = 1 -log_bin = /var/log/mysql/mysql-bin.log -expire_logs_days = 10 -max_binlog_size = 50M -#binlog_do_db = include_database_name -#binlog_ignore_db = include_database_name -# -# * InnoDB -# -sync_binlog=1 -# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. -# Read the manual for more InnoDB related options. There are many! -# -# * Security Features -# -# Read the manual, too, if you want chroot! -# chroot = /var/lib/mysql/ -# -# For generating SSL certificates I recommend the OpenSSL GUI "tinyca". -# -# ssl-ca=/etc/mysql/cacert.pem -# ssl-cert=/etc/mysql/server-cert.pem -# ssl-key=/etc/mysql/server-key.pem - - - -[mysqldump] -quick -quote-names -max_allowed_packet = 256M - -[mysql] -#no-auto-rehash # faster start of mysql but no tab completition - -[isamchk] -key_buffer = 128M - -# -# * IMPORTANT: Additional settings that can override those from this file! -# The files must end with '.cnf', otherwise they'll be ignored. -# -!includedir /etc/mysql/conf.d/ diff --git a/contrib/puppet/files/production/nova-iptables b/contrib/puppet/files/production/nova-iptables deleted file mode 100755 index 61e2ca2b9..000000000 --- a/contrib/puppet/files/production/nova-iptables +++ /dev/null @@ -1,187 +0,0 @@ -#! /bin/sh - -# 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. - -# NOTE(vish): This script sets up some reasonable defaults for iptables and -# creates nova-specific chains. If you use this script you should -# run nova-network and nova-compute with --use_nova_chains=True - - -# NOTE(vish): If you run public nova-api on a different port, make sure to -# change the port here - -if [ -f /etc/default/nova-iptables ] ; then - . /etc/default/nova-iptables -fi - -export LC_ALL=C - -API_PORT=${API_PORT:-"8773"} - -if [ ! -n "$IP" ]; then - # NOTE(vish): IP address is what address the services ALLOW on. - # This will just get the first ip in the list, so if you - # have more than one eth device set up, this will fail, and - # you should explicitly pass in the ip of the instance - IP=`ifconfig | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'` -fi - -if [ ! -n "$PRIVATE_RANGE" ]; then - #NOTE(vish): PRIVATE_RANGE: range is ALLOW to access DHCP - PRIVATE_RANGE="192.168.0.0/12" -fi - -if [ ! -n "$MGMT_IP" ]; then - # NOTE(vish): Management IP is the ip over which to allow ssh traffic. It - # will also allow traffic to nova-api - MGMT_IP="$IP" -fi - -if [ ! -n "$DMZ_IP" ]; then - # NOTE(vish): DMZ IP is the ip over which to allow api & objectstore access - DMZ_IP="$IP" -fi - -clear_nova_iptables() { - iptables -P INPUT ACCEPT - iptables -P FORWARD ACCEPT - iptables -P OUTPUT ACCEPT - iptables -F - iptables -t nat -F - iptables -F services - iptables -X services - # HACK: re-adding fail2ban rules :( - iptables -N fail2ban-ssh - iptables -A INPUT -p tcp -m multiport --dports 22 -j fail2ban-ssh - iptables -A fail2ban-ssh -j RETURN -} - -load_nova_iptables() { - - iptables -P INPUT DROP - iptables -A INPUT -m state --state INVALID -j DROP - iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - # NOTE(ja): allow localhost for everything - iptables -A INPUT -d 127.0.0.1/32 -j ACCEPT - # NOTE(ja): 22 only allowed MGMT_IP before, but we widened it to any - # address, since ssh should be listening only on internal - # before we re-add this rule we will need to add - # flexibility for RSYNC between omega/stingray - iptables -A INPUT -m tcp -p tcp --dport 22 -j ACCEPT - iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT - iptables -A INPUT -p icmp -j ACCEPT - iptables -N services - iptables -A INPUT -j services - iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset - iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable - - iptables -P FORWARD DROP - iptables -A FORWARD -m state --state INVALID -j DROP - iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - - # NOTE(vish): DROP on output is too restrictive for now. We need to add - # in a bunch of more specific output rules to use it. - # iptables -P OUTPUT DROP - iptables -A OUTPUT -m state --state INVALID -j DROP - iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - - if [ -n "$GANGLIA" ] || [ -n "$ALL" ]; then - iptables -A services -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT - iptables -A services -m udp -p udp -d $IP --dport 8649 -j ACCEPT - fi - - # if [ -n "$WEB" ] || [ -n "$ALL" ]; then - # # NOTE(vish): This opens up ports for web access, allowing web-based - # # dashboards to work. - # iptables -A services -m tcp -p tcp -d $IP --dport 80 -j ACCEPT - # iptables -A services -m tcp -p tcp -d $IP --dport 443 -j ACCEPT - # fi - - if [ -n "$OBJECTSTORE" ] || [ -n "$ALL" ]; then - # infrastructure - iptables -A services -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT - # clients - iptables -A services -m tcp -p tcp -d $DMZ_IP --dport 3333 -j ACCEPT - fi - - if [ -n "$API" ] || [ -n "$ALL" ]; then - iptables -A services -m tcp -p tcp -d $IP --dport $API_PORT -j ACCEPT - if [ "$IP" != "$DMZ_IP" ]; then - iptables -A services -m tcp -p tcp -d $DMZ_IP --dport $API_PORT -j ACCEPT - fi - if [ "$IP" != "$MGMT_IP" ] && [ "$DMZ_IP" != "$MGMT_IP" ]; then - iptables -A services -m tcp -p tcp -d $MGMT_IP --dport $API_PORT -j ACCEPT - fi - fi - - if [ -n "$REDIS" ] || [ -n "$ALL" ]; then - iptables -A services -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT - fi - - if [ -n "$MYSQL" ] || [ -n "$ALL" ]; then - iptables -A services -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT - fi - - if [ -n "$RABBITMQ" ] || [ -n "$ALL" ]; then - iptables -A services -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT - iptables -A services -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT - iptables -A services -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT - fi - - if [ -n "$DNSMASQ" ] || [ -n "$ALL" ]; then - # NOTE(vish): this could theoretically be setup per network - # for each host, but it seems like overkill - iptables -A services -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT - iptables -A services -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT - iptables -A services -m udp -p udp --dport 67 -j ACCEPT - fi - - if [ -n "$LDAP" ] || [ -n "$ALL" ]; then - iptables -A services -m tcp -p tcp -d $IP --dport 389 -j ACCEPT - fi - - if [ -n "$ISCSI" ] || [ -n "$ALL" ]; then - iptables -A services -m tcp -p tcp -d $IP --dport 3260 -j ACCEPT - iptables -A services -m tcp -p tcp -d 127.0.0.0/16 --dport 3260 -j ACCEPT - fi -} - - -case "$1" in - start) - echo "Starting nova-iptables: " - load_nova_iptables - ;; - stop) - echo "Clearing nova-iptables: " - clear_nova_iptables - ;; - restart) - echo "Restarting nova-iptables: " - clear_nova_iptables - load_nova_iptables - ;; - *) - echo "Usage: $NAME {start|stop|restart}" >&2 - exit 1 - ;; -esac - -exit 0 diff --git a/contrib/puppet/files/production/nova-iscsi-dev.sh b/contrib/puppet/files/production/nova-iscsi-dev.sh deleted file mode 100644 index 8eda10d2e..000000000 --- a/contrib/puppet/files/production/nova-iscsi-dev.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -# FILE: /etc/udev/scripts/iscsidev.sh - -BUS=${1} -HOST=${BUS%%:*} - -[ -e /sys/class/iscsi_host ] || exit 1 - -file="/sys/class/iscsi_host/host${HOST}/device/session*/iscsi_session*/session*/targetname" - -target_name=$(cat ${file}) - -# This is not an open-scsi drive -if [ -z "${target_name}" ]; then - exit 1 -fi - -echo "${target_name##*:}" diff --git a/contrib/puppet/files/production/setup_data.sh b/contrib/puppet/files/production/setup_data.sh deleted file mode 100755 index 1fbbac41c..000000000 --- a/contrib/puppet/files/production/setup_data.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -/root/slap.sh -mysql -e "DROP DATABASE nova" -mysql -e "CREATE DATABASE nova" -mysql -e "GRANT ALL on nova.* to nova@'%' identified by 'TODO:CHANGEME:CMON'" -touch /root/installed diff --git a/contrib/puppet/files/production/slap.sh b/contrib/puppet/files/production/slap.sh deleted file mode 100755 index f8ea16949..000000000 --- a/contrib/puppet/files/production/slap.sh +++ /dev/null @@ -1,261 +0,0 @@ -#!/usr/bin/env bash -# 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. -# LDAP INSTALL SCRIPT - SHOULD BE IDEMPOTENT, but it SCRUBS all USERS - -apt-get install -y slapd ldap-utils python-ldap - -cat >/etc/ldap/schema/openssh-lpk_openldap.schema < -# -# Based on the proposal of : Mark Ruijter -# - - -# octetString SYNTAX -attributetype ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' - DESC 'MANDATORY: OpenSSH Public key' - EQUALITY octetStringMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) - -# printableString SYNTAX yes|no -objectclass ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY - DESC 'MANDATORY: OpenSSH LPK objectclass' - MAY ( sshPublicKey $ uid ) - ) -LPK_SCHEMA_EOF - -cat >/etc/ldap/schema/nova.schema < -# -# - -# using internet experimental oid arc as per BP64 3.1 -objectidentifier novaSchema 1.3.6.1.3.1.666.666 -objectidentifier novaAttrs novaSchema:3 -objectidentifier novaOCs novaSchema:4 - -attributetype ( - novaAttrs:1 - NAME 'accessKey' - DESC 'Key for accessing data' - EQUALITY caseIgnoreMatch - SUBSTR caseIgnoreSubstringsMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 - SINGLE-VALUE - ) - -attributetype ( - novaAttrs:2 - NAME 'secretKey' - DESC 'Secret key' - EQUALITY caseIgnoreMatch - SUBSTR caseIgnoreSubstringsMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 - SINGLE-VALUE - ) - -attributetype ( - novaAttrs:3 - NAME 'keyFingerprint' - DESC 'Fingerprint of private key' - EQUALITY caseIgnoreMatch - SUBSTR caseIgnoreSubstringsMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 - SINGLE-VALUE - ) - -attributetype ( - novaAttrs:4 - NAME 'isAdmin' - DESC 'Is user an administrator?' - EQUALITY booleanMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 - SINGLE-VALUE - ) - -attributetype ( - novaAttrs:5 - NAME 'projectManager' - DESC 'Project Managers of a project' - SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 - ) - -objectClass ( - novaOCs:1 - NAME 'novaUser' - DESC 'access and secret keys' - AUXILIARY - MUST ( uid ) - MAY ( accessKey $ secretKey $ isAdmin ) - ) - -objectClass ( - novaOCs:2 - NAME 'novaKeyPair' - DESC 'Key pair for User' - SUP top - STRUCTURAL - MUST ( cn $ sshPublicKey $ keyFingerprint ) - ) - -objectClass ( - novaOCs:3 - NAME 'novaProject' - DESC 'Container for project' - SUP groupOfNames - STRUCTURAL - MUST ( cn $ projectManager ) - ) - -NOVA_SCHEMA_EOF - -mv /etc/ldap/slapd.conf /etc/ldap/slapd.conf.orig -cat >/etc/ldap/slapd.conf </etc/ldap/ldap.conf </etc/ldap/base.ldif < "/usr/bin/apt-get update" } diff --git a/contrib/puppet/manifests/classes/issue.pp b/contrib/puppet/manifests/classes/issue.pp deleted file mode 100644 index 8bb37ee3f..000000000 --- a/contrib/puppet/manifests/classes/issue.pp +++ /dev/null @@ -1,14 +0,0 @@ -class issue { - file { "/etc/issue": - owner => "root", - group => "root", - mode => 444, - source => "puppet://${puppet_server}/files/etc/issue", - } - file { "/etc/issue.net": - owner => "root", - group => "root", - mode => 444, - source => "puppet://${puppet_server}/files/etc/issue", - } -} diff --git a/contrib/puppet/manifests/classes/kern_module.pp b/contrib/puppet/manifests/classes/kern_module.pp deleted file mode 100644 index 00ec0636c..000000000 --- a/contrib/puppet/manifests/classes/kern_module.pp +++ /dev/null @@ -1,34 +0,0 @@ -# via http://projects.puppetlabs.com/projects/puppet/wiki/Kernel_Modules_Patterns - -define kern_module ($ensure) { - $modulesfile = $operatingsystem ? { ubuntu => "/etc/modules", redhat => "/etc/rc.modules" } - case $operatingsystem { - redhat: { file { "/etc/rc.modules": ensure => file, mode => 755 } } - } - case $ensure { - present: { - exec { "insert_module_${name}": - command => $operatingsystem ? { - ubuntu => "/bin/echo '${name}' >> '${modulesfile}'", - redhat => "/bin/echo '/sbin/modprobe ${name}' >> '${modulesfile}' " - }, - unless => "/bin/grep -qFx '${name}' '${modulesfile}'" - } - exec { "/sbin/modprobe ${name}": unless => "/bin/grep -q '^${name} ' '/proc/modules'" } - } - absent: { - exec { "/sbin/modprobe -r ${name}": onlyif => "/bin/grep -q '^${name} ' '/proc/modules'" } - exec { "remove_module_${name}": - command => $operatingsystem ? { - ubuntu => "/usr/bin/perl -ni -e 'print unless /^\\Q${name}\\E\$/' '${modulesfile}'", - redhat => "/usr/bin/perl -ni -e 'print unless /^\\Q/sbin/modprobe ${name}\\E\$/' '${modulesfile}'" - }, - onlyif => $operatingsystem ? { - ubuntu => "/bin/grep -qFx '${name}' '${modulesfile}'", - redhat => "/bin/grep -q '^/sbin/modprobe ${name}' '${modulesfile}'" - } - } - } - default: { err ( "unknown ensure value ${ensure}" ) } - } -} diff --git a/contrib/puppet/manifests/classes/loopback.pp b/contrib/puppet/manifests/classes/loopback.pp deleted file mode 100644 index e0fa9d541..000000000 --- a/contrib/puppet/manifests/classes/loopback.pp +++ /dev/null @@ -1,6 +0,0 @@ -define loopback($num) { - exec { "mknod -m 0660 /dev/loop${num} b 7 ${num}; chown root:disk /dev/loop${num}": - creates => "/dev/loop${num}", - path => ["/usr/bin", "/usr/sbin", "/bin"] - } -} diff --git a/contrib/puppet/manifests/classes/lvm.pp b/contrib/puppet/manifests/classes/lvm.pp deleted file mode 100644 index 5a407abcb..000000000 --- a/contrib/puppet/manifests/classes/lvm.pp +++ /dev/null @@ -1,8 +0,0 @@ -class lvm { - file { "/etc/lvm/lvm.conf": - owner => "root", - group => "root", - mode => 444, - source => "puppet://${puppet_server}/files/etc/lvm.conf", - } -} diff --git a/contrib/puppet/manifests/classes/lvmconf.pp b/contrib/puppet/manifests/classes/lvmconf.pp deleted file mode 100644 index 4aa7ddfdc..000000000 --- a/contrib/puppet/manifests/classes/lvmconf.pp +++ /dev/null @@ -1,8 +0,0 @@ -class lvmconf { - file { "/etc/lvm/lvm.conf": - owner => "root", group => "root", mode => 644, - source => "puppet://${puppet_server}/files/etc/lvm/lvm.conf", - ensure => present - } -} - diff --git a/contrib/puppet/manifests/classes/nova.pp b/contrib/puppet/manifests/classes/nova.pp deleted file mode 100644 index e942860f4..000000000 --- a/contrib/puppet/manifests/classes/nova.pp +++ /dev/null @@ -1,464 +0,0 @@ -import "kern_module" -import "apt" -import "loopback" - -#$head_node_ip = "undef" -#$rabbit_ip = "undef" -#$vpn_ip = "undef" -#$public_interface = "undef" -#$vlan_start = "5000" -#$vlan_end = "6000" -#$private_range = "10.0.0.0/16" -#$public_range = "192.168.177.0/24" - -define nova_iptables($services, $ip="", $private_range="", $mgmt_ip="", $dmz_ip="") { - file { "/etc/init.d/nova-iptables": - owner => "root", mode => 755, - source => "puppet://${puppet_server}/files/production/nova-iptables", - } - - file { "/etc/default/nova-iptables": - owner => "root", mode => 644, - content => template("nova-iptables.erb") - } -} - -define nova_conf_pointer($name) { - file { "/etc/nova/nova-${name}.conf": - owner => "nova", mode => 400, - content => "--flagfile=/etc/nova/nova.conf" - } -} - -class novaconf { - file { "/etc/nova/nova.conf": - owner => "nova", mode => 400, - content => template("production/nova-common.conf.erb", "production/nova-${cluster_name}.conf.erb") - } - nova_conf_pointer{'manage': name => 'manage'} -} - -class novadata { - package { "rabbitmq-server": ensure => present } - - file { "/etc/rabbitmq/rabbitmq.conf": - owner => "root", mode => 644, - content => "NODENAME=rabbit@localhost", - } - - service { "rabbitmq-server": - ensure => running, - enable => true, - hasstatus => true, - require => [ - File["/etc/rabbitmq/rabbitmq.conf"], - Package["rabbitmq-server"] - ] - } - - package { "mysql-server": ensure => present } - - file { "/etc/mysql/my.cnf": - owner => "root", mode => 644, - source => "puppet://${puppet_server}/files/production/my.cnf", - } - - service { "mysql": - ensure => running, - enable => true, - hasstatus => true, - require => [ - File["/etc/mysql/my.cnf"], - Package["mysql-server"] - ] - } - - file { "/root/slap.sh": - owner => "root", mode => 755, - source => "puppet://${puppet_server}/files/production/slap.sh", - } - - file { "/root/setup_data.sh": - owner => "root", mode => 755, - source => "puppet://${puppet_server}/files/production/setup_data.sh", - } - - # setup compute data - exec { "setup_data": - command => "/root/setup_data.sh", - path => "/usr/bin:/bin", - unless => "test -f /root/installed", - require => [ - Service["mysql"], - File["/root/slap.sh"], - File["/root/setup_data.sh"] - ] - } -} - -define nscheduler($version) { - package { "nova-scheduler": ensure => $version, require => Exec["update-apt"] } - nova_conf_pointer{'scheduler': name => 'scheduler'} - exec { "update-rc.d -f nova-scheduler remove; update-rc.d nova-scheduler defaults 50": - path => "/usr/bin:/usr/sbin:/bin", - onlyif => "test -f /etc/init.d/nova-scheduler", - unless => "test -f /etc/rc2.d/S50nova-scheduler" - } - service { "nova-scheduler": - ensure => running, - hasstatus => true, - subscribe => [ - Package["nova-scheduler"], - File["/etc/nova/nova.conf"], - File["/etc/nova/nova-scheduler.conf"] - ] - } - -} - -define napi($version, $api_servers, $api_base_port) { - file { "/etc/boto.cfg": - owner => "root", mode => 644, - source => "puppet://${puppet_server}/files/production/boto.cfg", - } - - file { "/var/lib/nova/CA/genvpn.sh": - owner => "nova", mode => 755, - source => "puppet://${puppet_server}/files/production/genvpn.sh", - } - - package { "python-greenlet": ensure => present } - package { "nova-api": ensure => $version, require => [Exec["update-apt"], Package["python-greenlet"]] } - nova_conf_pointer{'api': name => 'api'} - - exec { "update-rc.d -f nova-api remove; update-rc.d nova-api defaults 50": - path => "/usr/bin:/usr/sbin:/bin", - onlyif => "test -f /etc/init.d/nova-api", - unless => "test -f /etc/rc2.d/S50nova-api" - } - - service { "nova-netsync": - start => "/usr/bin/nova-netsync --pidfile=/var/run/nova/nova-netsync.pid --lockfile=/var/run/nova/nova-netsync.pid.lock start", - stop => "/usr/bin/nova-netsync --pidfile=/var/run/nova/nova-netsync.pid --lockfile=/var/run/nova/nova-netsync.pid.lock stop", - ensure => running, - hasstatus => false, - pattern => "nova-netsync", - require => Service["nova-api"], - subscribe => File["/etc/nova/nova.conf"] - } - service { "nova-api": - start => "monit start all -g nova_api", - stop => "monit stop all -g nova_api", - restart => "monit restart all -g nova_api", - # ensure => running, - # hasstatus => true, - require => Service["monit"], - subscribe => [ - Package["nova-objectstore"], - File["/etc/boto.cfg"], - File["/etc/nova/nova.conf"], - File["/etc/nova/nova-objectstore.conf"] - ] - } - - # the haproxy & monit's template use $api_servers and $api_base_port - - package { "haproxy": ensure => present } - file { "/etc/default/haproxy": - owner => "root", mode => 644, - content => "ENABLED=1", - require => Package['haproxy'] - } - file { "/etc/haproxy/haproxy.cfg": - owner => "root", mode => 644, - content => template("/srv/cloud/puppet/templates/haproxy.cfg.erb"), - require => Package['haproxy'] - } - service { "haproxy": - ensure => true, - enable => true, - hasstatus => true, - subscribe => [ - Package["haproxy"], - File["/etc/default/haproxy"], - File["/etc/haproxy/haproxy.cfg"], - ] - } - - package { "socat": ensure => present } - - file { "/usr/local/bin/gmetric_haproxy.sh": - owner => "root", mode => 755, - source => "puppet://${puppet_server}/files/production/ganglia/gmetric_scripts/gmetric_haproxy.sh", - } - - cron { "gmetric_haproxy": - command => "/usr/local/bin/gmetric_haproxy.sh", - user => root, - minute => "*/3", - } - - package { "monit": ensure => present } - - file { "/etc/default/monit": - owner => "root", mode => 644, - content => "startup=1", - require => Package['monit'] - } - file { "/etc/monit/monitrc": - owner => "root", mode => 600, - content => template("/srv/cloud/puppet/templates/monitrc-nova-api.erb"), - require => Package['monit'] - } - service { "monit": - ensure => true, - pattern => "sbin/monit", - subscribe => [ - Package["monit"], - File["/etc/default/monit"], - File["/etc/monit/monitrc"], - ] - } - -} - - -define nnetwork($version) { - # kill the default network added by the package - exec { "kill-libvirt-default-net": - command => "virsh net-destroy default; rm /etc/libvirt/qemu/networks/autostart/default.xml", - path => "/usr/bin:/bin", - onlyif => "test -f /etc/libvirt/qemu/networks/autostart/default.xml" - } - - # EVIL HACK: custom binary because dnsmasq 2.52 segfaulted accessing dereferenced object - file { "/usr/sbin/dnsmasq": - owner => "root", group => "root", - source => "puppet://${puppet_server}/files/production/dnsmasq", - } - - package { "nova-network": ensure => $version, require => Exec["update-apt"] } - nova_conf_pointer{'dhcpbridge': name => 'dhcpbridge'} - nova_conf_pointer{'network': name => "network" } - - exec { "update-rc.d -f nova-network remove; update-rc.d nova-network defaults 50": - path => "/usr/bin:/usr/sbin:/bin", - onlyif => "test -f /etc/init.d/nova-network", - unless => "test -f /etc/rc2.d/S50nova-network" - } - service { "nova-network": - ensure => running, - hasstatus => true, - subscribe => [ - Package["nova-network"], - File["/etc/nova/nova.conf"], - File["/etc/nova/nova-network.conf"] - ] - } -} - -define nobjectstore($version) { - package { "nova-objectstore": ensure => $version, require => Exec["update-apt"] } - nova_conf_pointer{'objectstore': name => 'objectstore'} - exec { "update-rc.d -f nova-objectstore remove; update-rc.d nova-objectstore defaults 50": - path => "/usr/bin:/usr/sbin:/bin", - onlyif => "test -f /etc/init.d/nova-objectstore", - unless => "test -f /etc/rc2.d/S50nova-objectstore" - } - service { "nova-objectstore": - ensure => running, - hasstatus => true, - subscribe => [ - Package["nova-objectstore"], - File["/etc/nova/nova.conf"], - File["/etc/nova/nova-objectstore.conf"] - ] - } -} - -define ncompute($version) { - include ganglia-python - include ganglia-compute - - # kill the default network added by the package - exec { "kill-libvirt-default-net": - command => "virsh net-destroy default; rm /etc/libvirt/qemu/networks/autostart/default.xml", - path => "/usr/bin:/bin", - onlyif => "test -f /etc/libvirt/qemu/networks/autostart/default.xml" - } - - - # LIBVIRT has to be restarted when ebtables / gawk is installed - service { "libvirt-bin": - ensure => running, - pattern => "sbin/libvirtd", - subscribe => [ - Package["ebtables"], - Kern_module["kvm_intel"] - ], - require => [ - Package["libvirt-bin"], - Package["ebtables"], - Package["gawk"], - Kern_module["kvm_intel"], - File["/dev/kvm"] - ] - } - - package { "libvirt-bin": ensure => "0.8.3-1ubuntu14~ppalucid2" } - package { "ebtables": ensure => present } - package { "gawk": ensure => present } - - # ensure proper permissions on /dev/kvm - file { "/dev/kvm": - owner => "root", - group => "kvm", - mode => 660 - } - - # require hardware virt - kern_module { "kvm_intel": - ensure => present, - } - - # increase loopback devices - file { "/etc/modprobe.d/loop.conf": - owner => "root", mode => 644, - content => "options loop max_loop=40" - } - - nova_conf_pointer{'compute': name => 'compute'} - - loopback{loop0: num => 0} - loopback{loop1: num => 1} - loopback{loop2: num => 2} - loopback{loop3: num => 3} - loopback{loop4: num => 4} - loopback{loop5: num => 5} - loopback{loop6: num => 6} - loopback{loop7: num => 7} - loopback{loop8: num => 8} - loopback{loop9: num => 9} - loopback{loop10: num => 10} - loopback{loop11: num => 11} - loopback{loop12: num => 12} - loopback{loop13: num => 13} - loopback{loop14: num => 14} - loopback{loop15: num => 15} - loopback{loop16: num => 16} - loopback{loop17: num => 17} - loopback{loop18: num => 18} - loopback{loop19: num => 19} - loopback{loop20: num => 20} - loopback{loop21: num => 21} - loopback{loop22: num => 22} - loopback{loop23: num => 23} - loopback{loop24: num => 24} - loopback{loop25: num => 25} - loopback{loop26: num => 26} - loopback{loop27: num => 27} - loopback{loop28: num => 28} - loopback{loop29: num => 29} - loopback{loop30: num => 30} - loopback{loop31: num => 31} - loopback{loop32: num => 32} - loopback{loop33: num => 33} - loopback{loop34: num => 34} - loopback{loop35: num => 35} - loopback{loop36: num => 36} - loopback{loop37: num => 37} - loopback{loop38: num => 38} - loopback{loop39: num => 39} - - package { "python-libvirt": ensure => "0.8.3-1ubuntu14~ppalucid2" } - - package { "nova-compute": - ensure => "$version", - require => Package["python-libvirt"] - } - - #file { "/usr/share/nova/libvirt.qemu.xml.template": - # owner => "nova", mode => 400, - # source => "puppet://${puppet_server}/files/production/libvirt.qemu.xml.template", - #} - - # fix runlevels: using enable => true adds it as 20, which is too early - exec { "update-rc.d -f nova-compute remove": - path => "/usr/bin:/usr/sbin:/bin", - onlyif => "test -f /etc/rc2.d/S??nova-compute" - } - service { "nova-compute": - ensure => running, - hasstatus => true, - subscribe => [ - Package["nova-compute"], - File["/etc/nova/nova.conf"], - File["/etc/nova/nova-compute.conf"], - #File["/usr/share/nova/libvirt.qemu.xml.template"], - Service["libvirt-bin"], - Kern_module["kvm_intel"] - ] - } -} - -define nvolume($version) { - - package { "nova-volume": ensure => $version, require => Exec["update-apt"] } - - nova_conf_pointer{'volume': name => 'volume'} - - # fix runlevels: using enable => true adds it as 20, which is too early - exec { "update-rc.d -f nova-volume remove": - path => "/usr/bin:/usr/sbin:/bin", - onlyif => "test -f /etc/rc2.d/S??nova-volume" - } - - file { "/etc/default/iscsitarget": - owner => "root", mode => 644, - content => "ISCSITARGET_ENABLE=true" - } - - package { "iscsitarget": ensure => present } - - file { "/dev/iscsi": ensure => directory } # FIXME(vish): owner / mode? - file { "/usr/sbin/nova-iscsi-dev.sh": - owner => "root", mode => 755, - source => "puppet://${puppet_server}/files/production/nova-iscsi-dev.sh" - } - file { "/etc/udev/rules.d/55-openiscsi.rules": - owner => "root", mode => 644, - content => 'KERNEL=="sd*", BUS=="scsi", PROGRAM="/usr/sbin/nova-iscsi-dev.sh %b",SYMLINK+="iscsi/%c%n"' - } - - service { "iscsitarget": - ensure => running, - enable => true, - hasstatus => true, - require => [ - File["/etc/default/iscsitarget"], - Package["iscsitarget"] - ] - } - - service { "nova-volume": - ensure => running, - hasstatus => true, - subscribe => [ - Package["nova-volume"], - File["/etc/nova/nova.conf"], - File["/etc/nova/nova-volume.conf"] - ] - } -} - -class novaspool { - # This isn't in release yet - #cron { logspool: - # command => "/usr/bin/nova-logspool /var/log/nova.log /var/lib/nova/spool", - # user => "nova" - #} - #cron { spoolsentry: - # command => "/usr/bin/nova-spoolsentry ${sentry_url} ${sentry_key} /var/lib/nova/spool", - # user => "nova" - #} -} diff --git a/contrib/puppet/manifests/classes/swift.pp b/contrib/puppet/manifests/classes/swift.pp deleted file mode 100644 index 64ffb6fa3..000000000 --- a/contrib/puppet/manifests/classes/swift.pp +++ /dev/null @@ -1,7 +0,0 @@ -class swift { - package { "memcached": ensure => present } - service { "memcached": require => Package['memcached'] } - - package { "swift-proxy": ensure => present } -} - diff --git a/contrib/puppet/manifests/site.pp b/contrib/puppet/manifests/site.pp deleted file mode 100644 index ca07a34ad..000000000 --- a/contrib/puppet/manifests/site.pp +++ /dev/null @@ -1,120 +0,0 @@ -# site.pp - -import "templates" -import "classes/*" - -node novabase inherits default { -# $puppet_server = "192.168.0.10" - $cluster_name = "openstack001" - $ganglia_udp_send_channel = "openstack001.example.com" - $syslog = "192.168.0.10" - - # THIS STUFF ISN'T IN RELEASE YET - #$sentry_url = "http://192.168.0.19/sentry/store/" - #$sentry_key = "TODO:SENTRYPASS" - - $local_network = "192.168.0.0/16" - $vpn_ip = "192.168.0.2" - $public_interface = "eth0" - include novanode -# include nova-common - include opsmetrics - -# non-nova stuff such as nova-dash inherit from novanode -# novaspool needs a better home -# include novaspool -} - -# Builder -node "nova000.example.com" inherits novabase { - $syslog = "server" - include ntp - include syslog-server -} - -# Non-Nova nodes - -node - "blog.example.com", - "wiki.example.com" -inherits novabase { - include ganglia-python - include ganglia-apache - include ganglia-mysql -} - - -node "nova001.example.com" -inherits novabase { - include novabase - - nova_iptables { nova: - services => [ - "ganglia", - "mysql", - "rabbitmq", - "ldap", - "api", - "objectstore", - "nrpe", - ], - ip => "192.168.0.10", - } - - nobjectstore { nova: version => "0.9.0" } - nscheduler { nova: version => "0.9.0" } - napi { nova: - version => "0.9.0", - api_servers => 10, - api_base_port => 8000 - } -} - -node "nova002.example.com" -inherits novabase { - include novaconf - - nova_iptables { nova: - services => [ - "ganglia", - "dnsmasq", - "nrpe" - ], - ip => "192.168.4.2", - private_range => "192.168.0.0/16", - } - - nnetwork { nova: version => "0.9.0" } -} - -node - "nova003.example.com", - "nova004.example.com", - "nova005.example.com", - "nova006.example.com", - "nova007.example.com", - "nova008.example.com", - "nova009.example.com", - "nova010.example.com", - "nova011.example.com", - "nova012.example.com", - "nova013.example.com", - "nova014.example.com", - "nova015.example.com", - "nova016.example.com", - "nova017.example.com", - "nova018.example.com", - "nova019.example.com", -inherits novabase { - include novaconf - ncompute { nova: version => "0.9.0" } - nvolume { nova: version => "0.9.0" } -} - -#node -# "nova020.example.com" -# "nova021.example.com" -#inherits novanode { -# include novaconf - #ncompute { nova: version => "0.9.0" } -#} diff --git a/contrib/puppet/manifests/templates.pp b/contrib/puppet/manifests/templates.pp deleted file mode 100644 index 90e433013..000000000 --- a/contrib/puppet/manifests/templates.pp +++ /dev/null @@ -1,21 +0,0 @@ -# templates.pp - -import "classes/*" - -class baseclass { -# include dns-client # FIXME: missing resolv.conf.erb?? - include issue -} - -node default { - $nova_site = "undef" - $nova_ns1 = "undef" - $nova_ns2 = "undef" -# include baseclass -} - -# novanode handles the system-level requirements for Nova/Swift nodes -class novanode { - include baseclass - include lvmconf -} diff --git a/contrib/puppet/puppet.conf b/contrib/puppet/puppet.conf deleted file mode 100644 index 92af920e3..000000000 --- a/contrib/puppet/puppet.conf +++ /dev/null @@ -1,11 +0,0 @@ -[main] -logdir=/var/log/puppet -vardir=/var/lib/puppet -ssldir=/var/lib/puppet/ssl -rundir=/var/run/puppet -factpath=$vardir/lib/facter -pluginsync=false - -[puppetmasterd] -templatedir=/var/lib/nova/contrib/puppet/templates -autosign=true diff --git a/contrib/puppet/templates/haproxy.cfg.erb b/contrib/puppet/templates/haproxy.cfg.erb deleted file mode 100644 index bd9991de7..000000000 --- a/contrib/puppet/templates/haproxy.cfg.erb +++ /dev/null @@ -1,39 +0,0 @@ -# this config needs haproxy-1.1.28 or haproxy-1.2.1 - -global - log 127.0.0.1 local0 - log 127.0.0.1 local1 notice - #log loghost local0 info - maxconn 4096 - #chroot /usr/share/haproxy - stats socket /var/run/haproxy.sock - user haproxy - group haproxy - daemon - #debug - #quiet - -defaults - log global - mode http - option httplog - option dontlognull - retries 3 - option redispatch - stats enable - stats uri /haproxy - maxconn 2000 - contimeout 5000 - clitimeout 50000 - srvtimeout 50000 - - -listen nova-api 0.0.0.0:8773 - option httpchk GET / HTTP/1.0\r\nHost:\ example.com - option forwardfor - reqidel ^X-Forwarded-For:.* - balance roundrobin -<% api_servers.to_i.times do |offset| %><% port = api_base_port.to_i + offset -%> - server api_<%= port %> 127.0.0.1:<%= port %> maxconn 1 check -<% end -%> - option httpclose # disable keep-alive diff --git a/contrib/puppet/templates/monitrc-nova-api.erb b/contrib/puppet/templates/monitrc-nova-api.erb deleted file mode 100644 index fe2626327..000000000 --- a/contrib/puppet/templates/monitrc-nova-api.erb +++ /dev/null @@ -1,138 +0,0 @@ -############################################################################### -## Monit control file -############################################################################### -## -## Comments begin with a '#' and extend through the end of the line. Keywords -## are case insensitive. All path's MUST BE FULLY QUALIFIED, starting with '/'. -## -## Below you will find examples of some frequently used statements. For -## information about the control file, a complete list of statements and -## options please have a look in the monit manual. -## -## -############################################################################### -## Global section -############################################################################### -## -## Start monit in the background (run as a daemon): -# -set daemon 60 # check services at 1-minute intervals - with start delay 30 # optional: delay the first check by half a minute - # (by default check immediately after monit start) - - -## Set syslog logging with the 'daemon' facility. If the FACILITY option is -## omitted, monit will use 'user' facility by default. If you want to log to -## a stand alone log file instead, specify the path to a log file -# -set logfile syslog facility log_daemon -# -# -### Set the location of monit id file which saves the unique id specific for -### given monit. The id is generated and stored on first monit start. -### By default the file is placed in $HOME/.monit.id. -# -# set idfile /var/.monit.id -# -### Set the location of monit state file which saves the monitoring state -### on each cycle. By default the file is placed in $HOME/.monit.state. If -### state file is stored on persistent filesystem, monit will recover the -### monitoring state across reboots. If it is on temporary filesystem, the -### state will be lost on reboot. -# -# set statefile /var/.monit.state -# -## Set the list of mail servers for alert delivery. Multiple servers may be -## specified using comma separator. By default monit uses port 25 - this -## is possible to override with the PORT option. -# -# set mailserver mail.bar.baz, # primary mailserver -# backup.bar.baz port 10025, # backup mailserver on port 10025 -# localhost # fallback relay -# -# -## By default monit will drop alert events if no mail servers are available. -## If you want to keep the alerts for a later delivery retry, you can use the -## EVENTQUEUE statement. The base directory where undelivered alerts will be -## stored is specified by the BASEDIR option. You can limit the maximal queue -## size using the SLOTS option (if omitted, the queue is limited by space -## available in the back end filesystem). -# -# set eventqueue -# basedir /var/monit # set the base directory where events will be stored -# slots 100 # optionaly limit the queue size -# -# -## Send status and events to M/Monit (Monit central management: for more -## informations about M/Monit see http://www.tildeslash.com/mmonit). -# -# set mmonit http://monit:monit@192.168.1.10:8080/collector -# -# -## Monit by default uses the following alert mail format: -## -## --8<-- -## From: monit@$HOST # sender -## Subject: monit alert -- $EVENT $SERVICE # subject -## -## $EVENT Service $SERVICE # -## # -## Date: $DATE # -## Action: $ACTION # -## Host: $HOST # body -## Description: $DESCRIPTION # -## # -## Your faithful employee, # -## monit # -## --8<-- -## -## You can override this message format or parts of it, such as subject -## or sender using the MAIL-FORMAT statement. Macros such as $DATE, etc. -## are expanded at runtime. For example, to override the sender: -# -# set mail-format { from: monit@foo.bar } -# -# -## You can set alert recipients here whom will receive alerts if/when a -## service defined in this file has errors. Alerts may be restricted on -## events by using a filter as in the second example below. -# -# set alert sysadm@foo.bar # receive all alerts -# set alert manager@foo.bar only on { timeout } # receive just service- -# # timeout alert -# -# -## Monit has an embedded web server which can be used to view status of -## services monitored, the current configuration, actual services parameters -## and manage services from a web interface. -# - set httpd port 2812 and - use address localhost # only accept connection from localhost - allow localhost # allow localhost to connect to the server and -# allow admin:monit # require user 'admin' with password 'monit' -# allow @monit # allow users of group 'monit' to connect (rw) -# allow @users readonly # allow users of group 'users' to connect readonly -# -# -############################################################################### -## Services -############################################################################### - -<% api_servers.to_i.times do |offset| %><% port = api_base_port.to_i + offset %> - -check process nova_api_<%= port %> with pidfile /var/run/nova/nova-api-<%= port %>.pid - group nova_api - start program = "/usr/bin/nova-api --flagfile=/etc/nova/nova.conf --pidfile=/var/run/nova/nova-api-<%= port %>.pid --api_listen_port=<%= port %> --lockfile=/var/run/nova/nova-api-<%= port %>.pid.lock start" - as uid nova - stop program = "/usr/bin/nova-api --flagfile=/etc/nova/nova.conf --pidfile=/var/run/nova/nova-api-<%= port %>.pid --api_listen_port=<%= port %> --lockfile=/var/run/nova/nova-api-<%= port %>.pid.lock stop" - as uid nova - if failed port <%= port %> protocol http - with timeout 15 seconds - for 4 cycles - then restart - if totalmem > 300 Mb then restart - if cpu is greater than 60% for 2 cycles then alert - if cpu > 80% for 3 cycles then restart - if 3 restarts within 5 cycles then timeout - -<% end %> diff --git a/contrib/puppet/templates/nova-iptables.erb b/contrib/puppet/templates/nova-iptables.erb deleted file mode 100644 index 2fc066305..000000000 --- a/contrib/puppet/templates/nova-iptables.erb +++ /dev/null @@ -1,10 +0,0 @@ -<% services.each do |service| -%> -<%= service.upcase %>=1 -<% end -%> -<% if ip && ip != "" %>IP="<%=ip%>"<% end %> -<% if private_range && private_range != "" %>PRIVATE_RANGE="<%=private_range%>"<% end %> -<% if mgmt_ip && mgmt_ip != "" %>MGMT_IP="<%=mgmt_ip%>"<% end %> -<% if dmz_ip && dmz_ip != "" %>DMZ_IP="<%=dmz_ip%>"<% end %> - -# warning: this file is auto-generated by puppet - diff --git a/contrib/puppet/templates/production/nova-common.conf.erb b/contrib/puppet/templates/production/nova-common.conf.erb deleted file mode 100644 index 23ee0c5e8..000000000 --- a/contrib/puppet/templates/production/nova-common.conf.erb +++ /dev/null @@ -1,55 +0,0 @@ -# global ---dmz_net=192.168.0.0 ---dmz_mask=255.255.0.0 ---dmz_cidr=192.168.0.0/16 ---ldap_user_dn=cn=Administrators,dc=example,dc=com ---ldap_user_unit=Users ---ldap_user_subtree=ou=Users,dc=example,dc=com ---ldap_project_subtree=ou=Groups,dc=example,dc=com ---role_project_subtree=ou=Groups,dc=example,dc=com ---ldap_cloudadmin=cn=NovaAdmins,ou=Groups,dc=example,dc=com ---ldap_itsec=cn=NovaSecurity,ou=Groups,dc=example,dc=com ---ldap_sysadmin=cn=Administrators,ou=Groups,dc=example,dc=com ---ldap_netadmin=cn=Administrators,ou=Groups,dc=example,dc=com ---ldap_developer=cn=developers,ou=Groups,dc=example,dc=com ---verbose ---daemonize ---syslog ---networks_path=/var/lib/nova/networks ---instances_path=/var/lib/nova/instances ---buckets_path=/var/lib/nova/objectstore/buckets ---images_path=/var/lib/nova/objectstore/images ---scheduler_driver=nova.scheduler.simple.SimpleScheduler ---libvirt_xml_template=/usr/share/nova/libvirt.qemu.xml.template ---credentials_template=/usr/share/nova/novarc.template ---boot_script_template=/usr/share/nova/bootscript.template ---vpn_client_template=/usr/share/nova/client.ovpn.template ---max_cores=40 ---max_gigabytes=2000 ---ca_path=/var/lib/nova/CA ---keys_path=/var/lib/nova/keys ---vpn_start=11000 ---volume_group=vgdata ---volume_manager=nova.volume.manager.ISCSIManager ---volume_driver=nova.volume.driver.ISCSIDriver ---default_kernel=aki-DEFAULT ---default_ramdisk=ari-DEFAULT ---dhcpbridge=/usr/bin/nova-dhcpbridge ---vpn_image_id=ami-cloudpipe ---dhcpbridge_flagfile=/etc/nova/nova.conf ---credential_cert_subject=/C=US/ST=Texas/L=Bexar/O=NovaDev/OU=NOVA/CN=%s-%s ---auth_driver=nova.auth.ldapdriver.LdapDriver ---quota_cores=17 ---quota_floating_ips=5 ---quota_instances=6 ---quota_volumes=10 ---quota_gigabytes=100 ---use_nova_chains=True ---input_chain=services ---use_project_ca=True ---fixed_ip_disassociate_timeout=300 ---api_max_requests=1 ---api_listen_ip=127.0.0.1 ---user_cert_subject=/C=US/ST=Texas/L=Bexar/O=NovaDev/OU=Nova/CN=%s-%s-%s ---project_cert_subject=/C=US/ST=Texas/L=Bexar/O=NovaDev/OU=Nova/CN=project-ca-%s-%s ---vpn_cert_subject=/C=US/ST=Texas/L=Bexar/O=NovaDev/OU=Nova/CN=project-vpn-%s-%s diff --git a/contrib/puppet/templates/production/nova-nova.conf.erb b/contrib/puppet/templates/production/nova-nova.conf.erb deleted file mode 100644 index 8683fefde..000000000 --- a/contrib/puppet/templates/production/nova-nova.conf.erb +++ /dev/null @@ -1,21 +0,0 @@ ---fixed_range=192.168.0.0/16 ---iscsi_ip_prefix=192.168.4 ---floating_range=10.0.0.0/24 ---rabbit_host=192.168.0.10 ---s3_host=192.168.0.10 ---cc_host=192.168.0.10 ---cc_dmz=192.168.24.10 ---s3_dmz=192.168.24.10 ---ec2_url=http://192.168.0.1:8773/services/Cloud ---vpn_ip=192.168.0.2 ---ldap_url=ldap://192.168.0.10 ---sql_connection=mysql://nova:TODO-MYPASS@192.168.0.10/nova ---other_sql_connection=mysql://nova:TODO-MYPASS@192.168.0.10/nova ---routing_source_ip=192.168.0.2 ---bridge_dev=eth1 ---public_interface=eth0 ---vlan_start=3100 ---num_networks=700 ---rabbit_userid=TODO:RABBIT ---rabbit_password=TODO:CHANGEME ---ldap_password=TODO:CHANGEME -- cgit From 106eb47eff0551c73b2e90385b9c3a910fec8633 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 20 Feb 2011 23:16:10 -0800 Subject: fixes for various logging errors and issues --- nova/api/openstack/auth.py | 1 - nova/api/openstack/backup_schedules.py | 1 - nova/api/openstack/images.py | 2 - nova/api/openstack/servers.py | 1 - nova/api/openstack/shared_ip_groups.py | 2 - nova/api/openstack/zones.py | 1 - nova/console/manager.py | 2 +- nova/console/xvp.py | 2 +- nova/flags.py | 4 ++ nova/log.py | 117 ++++++++++++++++++++------------- nova/test.py | 2 + nova/tests/fake_flags.py | 2 + nova/tests/test_auth.py | 9 --- nova/tests/test_console.py | 2 - nova/tests/test_direct.py | 1 - nova/tests/test_localization.py | 1 - nova/tests/test_log.py | 65 +++++++++--------- nova/utils.py | 2 +- run_tests.py | 5 +- 19 files changed, 117 insertions(+), 105 deletions(-) diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 473071738..1dfdd5318 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -19,7 +19,6 @@ import datetime import hashlib import json import time -import logging import webob.exc import webob.dec diff --git a/nova/api/openstack/backup_schedules.py b/nova/api/openstack/backup_schedules.py index 197125d86..7abb5f884 100644 --- a/nova/api/openstack/backup_schedules.py +++ b/nova/api/openstack/backup_schedules.py @@ -15,7 +15,6 @@ # License for the specific language governing permissions and limitations # under the License. -import logging import time from webob import exc diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 9d56bc508..cf85a496f 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -15,8 +15,6 @@ # License for the specific language governing permissions and limitations # under the License. -import logging - from webob import exc from nova import compute diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index ce9601ecb..0bac4c64d 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -33,7 +33,6 @@ import nova.api.openstack LOG = logging.getLogger('server') -LOG.setLevel(logging.DEBUG) FLAGS = flags.FLAGS diff --git a/nova/api/openstack/shared_ip_groups.py b/nova/api/openstack/shared_ip_groups.py index bd3cc23a8..5d78f9377 100644 --- a/nova/api/openstack/shared_ip_groups.py +++ b/nova/api/openstack/shared_ip_groups.py @@ -15,8 +15,6 @@ # License for the specific language governing permissions and limitations # under the License. -import logging - from webob import exc from nova import wsgi diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index 830464ffd..d5206da20 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -14,7 +14,6 @@ # under the License. import common -import logging from nova import flags from nova import wsgi diff --git a/nova/console/manager.py b/nova/console/manager.py index 5697e7cb1..57c75cf4f 100644 --- a/nova/console/manager.py +++ b/nova/console/manager.py @@ -20,11 +20,11 @@ Console Proxy Service """ import functools -import logging import socket from nova import exception from nova import flags +from nova import log as logging from nova import manager from nova import rpc from nova import utils diff --git a/nova/console/xvp.py b/nova/console/xvp.py index ee66dac46..cd257e0a6 100644 --- a/nova/console/xvp.py +++ b/nova/console/xvp.py @@ -20,7 +20,6 @@ XVP (Xenserver VNC Proxy) driver. """ import fcntl -import logging import os import signal import subprocess @@ -31,6 +30,7 @@ from nova import context from nova import db from nova import exception from nova import flags +from nova import log as logging from nova import utils flags.DEFINE_string('console_xvp_conf_template', diff --git a/nova/flags.py b/nova/flags.py index f64a62da9..2f3bdd675 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -38,6 +38,7 @@ class FlagValues(gflags.FlagValues): defined after the initial parsing. """ + initialized = False def __init__(self, extra_context=None): gflags.FlagValues.__init__(self) @@ -45,6 +46,8 @@ class FlagValues(gflags.FlagValues): self.__dict__['__was_already_parsed'] = False self.__dict__['__stored_argv'] = [] self.__dict__['__extra_context'] = extra_context + # NOTE(vish): force a pseudo flag to keep track of whether + # flags have been parsed already def __call__(self, argv): # We're doing some hacky stuff here so that we don't have to copy @@ -90,6 +93,7 @@ class FlagValues(gflags.FlagValues): self.__dict__['__stored_argv'] = original_argv self.__dict__['__was_already_parsed'] = True self.ClearDirty() + FlagValues.initialized = True return args def Reset(self): diff --git a/nova/log.py b/nova/log.py index 6b201ffcc..12b695a41 100644 --- a/nova/log.py +++ b/nova/log.py @@ -117,7 +117,22 @@ def _get_binary_name(): return os.path.basename(inspect.stack()[-1][1]) -def get_log_file_path(binary=None): +def _get_level_from_flags(name): + # if exactly "nova", or a child logger, honor the verbose flag + if (name == "nova" or name.startswith("nova.")) and FLAGS.verbose: + return 'DEBUG' + for pair in FLAGS.default_log_levels: + logger, _sep, level = pair.partition('=') + # NOTE(todd): if we set a.b, we want a.b.c to have the same level + # (but not a.bc, so we check the dot) + if name == logger: + return level + if name.startswith(logger) and name[len(logger)] == '.': + return level + return 'INFO' + + +def _get_log_file_path(binary=None): if FLAGS.logfile: return FLAGS.logfile if FLAGS.logdir: @@ -126,22 +141,13 @@ def get_log_file_path(binary=None): def basicConfig(): - logging.basicConfig() - for handler in logging.root.handlers: - handler.setFormatter(_formatter) - if FLAGS.verbose: - logging.root.setLevel(logging.DEBUG) - else: - logging.root.setLevel(logging.INFO) - if FLAGS.use_syslog: - syslog = SysLogHandler(address='/dev/log') - syslog.setFormatter(_formatter) - logging.root.addHandler(syslog) - logpath = get_log_file_path() - if logpath: - logfile = WatchedFileHandler(logpath) - logfile.setFormatter(_formatter) - logging.root.addHandler(logfile) + pass + + +logging.basicConfig = basicConfig +_syslog = SysLogHandler(address='/dev/log') +_filelog = None +_streamlog = StreamHandler() class NovaLogger(logging.Logger): @@ -151,23 +157,24 @@ class NovaLogger(logging.Logger): This becomes the class that is instanciated by logging.getLogger. """ def __init__(self, name, level=NOTSET): - level_name = self._get_level_from_flags(name, FLAGS) - level = globals()[level_name] logging.Logger.__init__(self, name, level) - - def _get_level_from_flags(self, name, FLAGS): - # if exactly "nova", or a child logger, honor the verbose flag - if (name == "nova" or name.startswith("nova.")) and FLAGS.verbose: - return 'DEBUG' - for pair in FLAGS.default_log_levels: - logger, _sep, level = pair.partition('=') - # NOTE(todd): if we set a.b, we want a.b.c to have the same level - # (but not a.bc, so we check the dot) - if name == logger: - return level - if name.startswith(logger) and name[len(logger)] == '.': - return level - return 'INFO' + self.initialized = False + if flags.FlagValues.initialized: + self._setup_from_flags() + + def _setup_from_flags(self): + """Setup logger from flags""" + level_name = _get_level_from_flags(self.name) + self.setLevel(globals()[level_name]) + self.initialized = True + if not logging.root.initialized: + logging.root._setup_from_flags() + + def isEnabledFor(self, level): + """Reset level after flags have been loaded""" + if not self.initialized and flags.FlagValues.initialized: + self._setup_from_flags() + return logging.Logger.isEnabledFor(self, level) def _log(self, level, msg, args, exc_info=None, extra=None, context=None): """Extract context from any log call""" @@ -176,12 +183,12 @@ class NovaLogger(logging.Logger): if context: extra.update(_dictify_context(context)) extra.update({"nova_version": version.version_string_with_vcs()}) - logging.Logger._log(self, level, msg, args, exc_info, extra) + return logging.Logger._log(self, level, msg, args, exc_info, extra) def addHandler(self, handler): """Each handler gets our custom formatter""" handler.setFormatter(_formatter) - logging.Logger.addHandler(self, handler) + return logging.Logger.addHandler(self, handler) def audit(self, msg, *args, **kwargs): """Shortcut for our AUDIT level""" @@ -192,7 +199,7 @@ class NovaLogger(logging.Logger): """Logging.exception doesn't handle kwargs, so breaks context""" if not kwargs.get('exc_info'): kwargs['exc_info'] = 1 - self.error(msg, *args, **kwargs) + return self.error(msg, *args, **kwargs) # NOTE(todd): does this really go here, or in _log ? extra = kwargs.get('extra') if not extra: @@ -209,6 +216,8 @@ class NovaLogger(logging.Logger): def handle_exception(type, value, tb): + if len(logging.root.handlers) == 0: + logging.root.addHandler(_streamlog) logging.root.critical(str(value), exc_info=(type, value, tb)) @@ -216,15 +225,6 @@ sys.excepthook = handle_exception logging.setLoggerClass(NovaLogger) -class NovaRootLogger(NovaLogger): - pass - -if not isinstance(logging.root, NovaRootLogger): - logging.root = NovaRootLogger("nova.root", WARNING) - NovaLogger.root = logging.root - NovaLogger.manager.root = logging.root - - class NovaFormatter(logging.Formatter): """ A nova.context.RequestContext aware formatter configured through flags. @@ -271,6 +271,33 @@ class NovaFormatter(logging.Formatter): _formatter = NovaFormatter() +class NovaRootLogger(NovaLogger): + def __init__(self, name, level=NOTSET): + NovaLogger.__init__(self, name, level) + self.addHandler(_streamlog) + + def _setup_from_flags(self): + """Setup logger from flags""" + global _filelog + if FLAGS.use_syslog: + self.addHandler(_syslog) + logpath = _get_log_file_path() + if logpath: + if not _filelog: + _filelog = WatchedFileHandler(logpath) + self.addHandler(_filelog) + self.removeHandler(_streamlog) + return NovaLogger._setup_from_flags(self) + + +if not isinstance(logging.root, NovaRootLogger): + for handler in logging.root.handlers: + logging.root.removeHandler(handler) + logging.root = NovaRootLogger("nova") + NovaLogger.root = logging.root + NovaLogger.manager.root = logging.root + + def audit(msg, *args, **kwargs): """Shortcut for logging to root log with sevrity 'AUDIT'.""" if len(logging.root.handlers) == 0: diff --git a/nova/test.py b/nova/test.py index a12cf9d32..a649b4c5f 100644 --- a/nova/test.py +++ b/nova/test.py @@ -60,6 +60,8 @@ class TestCase(unittest.TestCase): def setUp(self): """Run before each test method to initialize test environment""" super(TestCase, self).setUp() + # NOTE(vish): pretend like we've loaded flags from command line + flags.FlagValues.initialized = True # NOTE(vish): We need a better method for creating fixtures for tests # now that we have some required db setup for the system # to work properly. diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index 1097488ec..68b14a46e 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -41,3 +41,5 @@ FLAGS.iscsi_num_targets = 8 FLAGS.verbose = True FLAGS.sql_connection = 'sqlite:///nova.sqlite' FLAGS.use_ipv6 = True +FLAGS.logfile = 'run_tests.err' +flags.FlagValues.initialized = True diff --git a/nova/tests/test_auth.py b/nova/tests/test_auth.py index 35ffffb67..2a7817032 100644 --- a/nova/tests/test_auth.py +++ b/nova/tests/test_auth.py @@ -327,15 +327,6 @@ class AuthManagerTestCase(object): class AuthManagerLdapTestCase(AuthManagerTestCase, test.TestCase): auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver' - def __init__(self, *args, **kwargs): - AuthManagerTestCase.__init__(self) - test.TestCase.__init__(self, *args, **kwargs) - import nova.auth.fakeldap as fakeldap - if FLAGS.flush_db: - LOG.info("Flushing datastore") - r = fakeldap.Store.instance() - r.flushdb() - class AuthManagerDbTestCase(AuthManagerTestCase, test.TestCase): auth_driver = 'nova.auth.dbdriver.DbDriver' diff --git a/nova/tests/test_console.py b/nova/tests/test_console.py index 85bf94458..49ff24413 100644 --- a/nova/tests/test_console.py +++ b/nova/tests/test_console.py @@ -21,7 +21,6 @@ Tests For Console proxy. """ import datetime -import logging from nova import context from nova import db @@ -38,7 +37,6 @@ FLAGS = flags.FLAGS class ConsoleTestCase(test.TestCase): """Test case for console proxy""" def setUp(self): - logging.getLogger().setLevel(logging.DEBUG) super(ConsoleTestCase, self).setUp() self.flags(console_driver='nova.console.fake.FakeConsoleProxy', stub_compute=True) diff --git a/nova/tests/test_direct.py b/nova/tests/test_direct.py index 8a74b2296..7656f5396 100644 --- a/nova/tests/test_direct.py +++ b/nova/tests/test_direct.py @@ -19,7 +19,6 @@ """Tests for Direct API.""" import json -import logging import webob diff --git a/nova/tests/test_localization.py b/nova/tests/test_localization.py index 6992773f5..393d71038 100644 --- a/nova/tests/test_localization.py +++ b/nova/tests/test_localization.py @@ -15,7 +15,6 @@ # under the License. import glob -import logging import os import re import sys diff --git a/nova/tests/test_log.py b/nova/tests/test_log.py index c2c9d7772..ada8d0a56 100644 --- a/nova/tests/test_log.py +++ b/nova/tests/test_log.py @@ -1,9 +1,12 @@ import cStringIO from nova import context +from nova import flags from nova import log from nova import test +FLAGS = flags.FLAGS + def _fake_context(): return context.RequestContext(1, 1) @@ -14,15 +17,11 @@ class RootLoggerTestCase(test.TestCase): super(RootLoggerTestCase, self).setUp() self.log = log.logging.root - def tearDown(self): - super(RootLoggerTestCase, self).tearDown() - log.NovaLogger.manager.loggerDict = {} - def test_is_nova_instance(self): self.assert_(isinstance(self.log, log.NovaLogger)) - def test_name_is_nova_root(self): - self.assertEqual("nova.root", self.log.name) + def test_name_is_nova(self): + self.assertEqual("nova", self.log.name) def test_handlers_have_nova_formatter(self): formatters = [] @@ -45,25 +44,38 @@ class RootLoggerTestCase(test.TestCase): log.audit("foo", context=_fake_context()) self.assert_(True) # didn't raise exception + def test_will_be_verbose_if_verbose_flag_set(self): + self.flags(verbose=True) + self.log.initialized = False + log.audit("foo", context=_fake_context()) + self.assertEqual(log.DEBUG, self.log.level) + + def test_will_not_be_verbose_if_verbose_flag_not_set(self): + self.flags(verbose=False) + self.log.initialized = False + log.audit("foo", context=_fake_context()) + self.assertEqual(log.INFO, self.log.level) + class LogHandlerTestCase(test.TestCase): def test_log_path_logdir(self): - self.flags(logdir='/some/path') - self.assertEquals(log.get_log_file_path(binary='foo-bar'), + self.flags(logdir='/some/path', logfile=None) + self.assertEquals(log._get_log_file_path(binary='foo-bar'), '/some/path/foo-bar.log') def test_log_path_logfile(self): self.flags(logfile='/some/path/foo-bar.log') - self.assertEquals(log.get_log_file_path(binary='foo-bar'), + self.assertEquals(log._get_log_file_path(binary='foo-bar'), '/some/path/foo-bar.log') def test_log_path_none(self): - self.assertTrue(log.get_log_file_path(binary='foo-bar') is None) + self.flags(logdir=None, logfile=None) + self.assertTrue(log._get_log_file_path(binary='foo-bar') is None) def test_log_path_logfile_overrides_logdir(self): self.flags(logdir='/some/other/path', logfile='/some/path/foo-bar.log') - self.assertEquals(log.get_log_file_path(binary='foo-bar'), + self.assertEquals(log._get_log_file_path(binary='foo-bar'), '/some/path/foo-bar.log') @@ -76,13 +88,15 @@ class NovaFormatterTestCase(test.TestCase): logging_debug_format_suffix="--DBG") self.log = log.logging.root self.stream = cStringIO.StringIO() - handler = log.StreamHandler(self.stream) - self.log.addHandler(handler) + self.handler = log.StreamHandler(self.stream) + self.log.addHandler(self.handler) + self.level = self.log.level self.log.setLevel(log.DEBUG) def tearDown(self): + self.log.setLevel(self.level) + self.log.removeHandler(self.handler) super(NovaFormatterTestCase, self).tearDown() - log.NovaLogger.manager.loggerDict = {} def test_uncontextualized_log(self): self.log.info("foo") @@ -102,30 +116,15 @@ class NovaFormatterTestCase(test.TestCase): class NovaLoggerTestCase(test.TestCase): def setUp(self): super(NovaLoggerTestCase, self).setUp() - self.flags(default_log_levels=["nova-test=AUDIT"], verbose=False) + levels = FLAGS.default_log_levels + levels.append("nova-test=AUDIT") + self.flags(default_log_levels=levels, + verbose=True) self.log = log.getLogger('nova-test') - def tearDown(self): - super(NovaLoggerTestCase, self).tearDown() - log.NovaLogger.manager.loggerDict = {} - def test_has_level_from_flags(self): self.assertEqual(log.AUDIT, self.log.level) def test_child_log_has_level_of_parent_flag(self): l = log.getLogger('nova-test.foo') self.assertEqual(log.AUDIT, l.level) - - -class VerboseLoggerTestCase(test.TestCase): - def setUp(self): - super(VerboseLoggerTestCase, self).setUp() - self.flags(default_log_levels=["nova.test=AUDIT"], verbose=True) - self.log = log.getLogger('nova.test') - - def tearDown(self): - super(VerboseLoggerTestCase, self).tearDown() - log.NovaLogger.manager.loggerDict = {} - - def test_will_be_verbose_if_named_nova_and_verbose_flag_set(self): - self.assertEqual(log.DEBUG, self.log.level) diff --git a/nova/utils.py b/nova/utils.py index 42efa0008..c2fd5f2ee 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -55,7 +55,7 @@ def import_class(import_str): __import__(mod_str) return getattr(sys.modules[mod_str], class_str) except (ImportError, ValueError, AttributeError), exc: - logging.debug(_('Inner Exception: %s'), exc) + LOG.info(_('Inner Exception: %s'), exc) raise exception.NotFound(_('Class %s cannot be found') % class_str) diff --git a/run_tests.py b/run_tests.py index 24786e8ad..bf12c62c6 100644 --- a/run_tests.py +++ b/run_tests.py @@ -26,8 +26,6 @@ from nose import config from nose import result from nose import core -from nova import log as logging - class NovaTestResult(result.TextTestResult): def __init__(self, *args, **kw): @@ -60,7 +58,8 @@ class NovaTestRunner(core.TextTestRunner): if __name__ == '__main__': - logging.basicConfig() + if os.path.exists("nova.sqlite"): + os.unlink("nova.sqlite") c = config.Config(stream=sys.stdout, env=os.environ, verbosity=3, -- cgit From 98c2ef77e6018c0a6233e32ace435fba488175f5 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 20 Feb 2011 23:18:09 -0800 Subject: move the fake initialized into fake flags --- nova/test.py | 2 -- nova/tests/fake_flags.py | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/test.py b/nova/test.py index a649b4c5f..a12cf9d32 100644 --- a/nova/test.py +++ b/nova/test.py @@ -60,8 +60,6 @@ class TestCase(unittest.TestCase): def setUp(self): """Run before each test method to initialize test environment""" super(TestCase, self).setUp() - # NOTE(vish): pretend like we've loaded flags from command line - flags.FlagValues.initialized = True # NOTE(vish): We need a better method for creating fixtures for tests # now that we have some required db setup for the system # to work properly. diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index 68b14a46e..0a1011d86 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -42,4 +42,5 @@ FLAGS.verbose = True FLAGS.sql_connection = 'sqlite:///nova.sqlite' FLAGS.use_ipv6 = True FLAGS.logfile = 'run_tests.err' +# NOTE(vish): pretend like we've loaded flags from command line flags.FlagValues.initialized = True -- cgit From eaddb9ea529672549af28cab4aacd2f8243c62a3 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 20 Feb 2011 23:36:36 -0800 Subject: remove extra references to logging.basicConfig --- bin/nova-api | 4 ++-- bin/nova-combined | 2 -- bin/nova-dhcpbridge | 1 - bin/nova-manage | 2 -- nova/log.py | 19 +++++++++++++------ nova/service.py | 1 - nova/twistd.py | 1 - nova/wsgi.py | 1 - 8 files changed, 15 insertions(+), 16 deletions(-) diff --git a/bin/nova-api b/bin/nova-api index 11176a021..8b3674880 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -39,7 +39,6 @@ from nova import log as logging from nova import version from nova import wsgi -logging.basicConfig() LOG = logging.getLogger('nova.api') LOG.setLevel(logging.DEBUG) @@ -71,7 +70,8 @@ def run_app(paste_config_file): return # NOTE(todd): redo logging config, verbose could be set in paste config - logging.basicConfig() + logging.root.setup_from_flags() + server = wsgi.Server() for app in apps: server.start(*app) diff --git a/bin/nova-combined b/bin/nova-combined index 913c866bf..5911d9016 100755 --- a/bin/nova-combined +++ b/bin/nova-combined @@ -49,7 +49,6 @@ FLAGS = flags.FLAGS if __name__ == '__main__': utils.default_flagfile() FLAGS(sys.argv) - logging.basicConfig() compute = service.Service.create(binary='nova-compute') network = service.Service.create(binary='nova-network') @@ -73,7 +72,6 @@ if __name__ == '__main__': apps.append((app, getattr(FLAGS, "%s_port" % api), getattr(FLAGS, "%s_host" % api))) if len(apps) > 0: - logging.basicConfig() server = wsgi.Server() for app in apps: server.start(*app) diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge index d38ba2543..e0e6af826 100755 --- a/bin/nova-dhcpbridge +++ b/bin/nova-dhcpbridge @@ -102,7 +102,6 @@ def main(): flagfile = os.environ.get('FLAGFILE', FLAGS.dhcpbridge_flagfile) utils.default_flagfile(flagfile) argv = FLAGS(sys.argv) - logging.basicConfig() interface = os.environ.get('DNSMASQ_INTERFACE', 'br0') if int(os.environ.get('TESTING', '0')): FLAGS.fake_rabbit = True diff --git a/bin/nova-manage b/bin/nova-manage index 6d67252b8..878a9afaa 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -77,7 +77,6 @@ from nova import crypto from nova import db from nova import exception from nova import flags -from nova import log as logging from nova import quota from nova import rpc from nova import utils @@ -87,7 +86,6 @@ from nova.cloudpipe import pipelib from nova.db import migration -logging.basicConfig() FLAGS = flags.FLAGS flags.DECLARE('fixed_range', 'nova.network.manager') flags.DECLARE('num_networks', 'nova.network.manager') diff --git a/nova/log.py b/nova/log.py index 12b695a41..0a863c921 100644 --- a/nova/log.py +++ b/nova/log.py @@ -160,20 +160,20 @@ class NovaLogger(logging.Logger): logging.Logger.__init__(self, name, level) self.initialized = False if flags.FlagValues.initialized: - self._setup_from_flags() + self.setup_from_flags() - def _setup_from_flags(self): + def setup_from_flags(self): """Setup logger from flags""" level_name = _get_level_from_flags(self.name) self.setLevel(globals()[level_name]) self.initialized = True if not logging.root.initialized: - logging.root._setup_from_flags() + logging.root.setup_from_flags() def isEnabledFor(self, level): """Reset level after flags have been loaded""" if not self.initialized and flags.FlagValues.initialized: - self._setup_from_flags() + self.setup_from_flags() return logging.Logger.isEnabledFor(self, level) def _log(self, level, msg, args, exc_info=None, extra=None, context=None): @@ -276,18 +276,24 @@ class NovaRootLogger(NovaLogger): NovaLogger.__init__(self, name, level) self.addHandler(_streamlog) - def _setup_from_flags(self): + def setup_from_flags(self): """Setup logger from flags""" global _filelog if FLAGS.use_syslog: self.addHandler(_syslog) + else: + self.removeHandler(_syslog) logpath = _get_log_file_path() if logpath: if not _filelog: _filelog = WatchedFileHandler(logpath) self.addHandler(_filelog) self.removeHandler(_streamlog) - return NovaLogger._setup_from_flags(self) + else: + self.removeHandler(_filelog) + self.addHandler(_streamlog) + + return NovaLogger.setup_from_flags(self) if not isinstance(logging.root, NovaRootLogger): @@ -296,6 +302,7 @@ if not isinstance(logging.root, NovaRootLogger): logging.root = NovaRootLogger("nova") NovaLogger.root = logging.root NovaLogger.manager.root = logging.root +root=logging.root def audit(msg, *args, **kwargs): diff --git a/nova/service.py b/nova/service.py index 59648adf2..02e86f6b3 100644 --- a/nova/service.py +++ b/nova/service.py @@ -215,7 +215,6 @@ class Service(object): def serve(*services): FLAGS(sys.argv) - logging.basicConfig() if not services: services = [Service.create()] diff --git a/nova/twistd.py b/nova/twistd.py index 60ff7879a..0e4db022c 100644 --- a/nova/twistd.py +++ b/nova/twistd.py @@ -258,7 +258,6 @@ def serve(filename): print 'usage: %s [options] [start|stop|restart]' % argv[0] sys.exit(1) - logging.basicConfig() logging.debug(_("Full set of FLAGS:")) for flag in FLAGS: logging.debug("%s : %s" % (flag, FLAGS.get(flag, None))) diff --git a/nova/wsgi.py b/nova/wsgi.py index e01cc1e1e..280baa80b 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -59,7 +59,6 @@ class Server(object): """Server class to manage multiple WSGI sockets and applications.""" def __init__(self, threads=1000): - logging.basicConfig() self.pool = eventlet.GreenPool(threads) def start(self, application, port, host='0.0.0.0', backlog=128): -- cgit From 2792e42a9c7da390b3db0b59b7dff357c440d3e5 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 20 Feb 2011 23:45:43 -0800 Subject: clean up location of method --- nova/log.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/nova/log.py b/nova/log.py index 0a863c921..57a550a0b 100644 --- a/nova/log.py +++ b/nova/log.py @@ -117,21 +117,6 @@ def _get_binary_name(): return os.path.basename(inspect.stack()[-1][1]) -def _get_level_from_flags(name): - # if exactly "nova", or a child logger, honor the verbose flag - if (name == "nova" or name.startswith("nova.")) and FLAGS.verbose: - return 'DEBUG' - for pair in FLAGS.default_log_levels: - logger, _sep, level = pair.partition('=') - # NOTE(todd): if we set a.b, we want a.b.c to have the same level - # (but not a.bc, so we check the dot) - if name == logger: - return level - if name.startswith(logger) and name[len(logger)] == '.': - return level - return 'INFO' - - def _get_log_file_path(binary=None): if FLAGS.logfile: return FLAGS.logfile @@ -162,9 +147,24 @@ class NovaLogger(logging.Logger): if flags.FlagValues.initialized: self.setup_from_flags() + @staticmethod + def _get_level_from_flags(name): + # if exactly "nova", or a child logger, honor the verbose flag + if (name == "nova" or name.startswith("nova.")) and FLAGS.verbose: + return 'DEBUG' + for pair in FLAGS.default_log_levels: + logger, _sep, level = pair.partition('=') + # NOTE(todd): if we set a.b, we want a.b.c to have the same level + # (but not a.bc, so we check the dot) + if name == logger: + return level + if name.startswith(logger) and name[len(logger)] == '.': + return level + return 'INFO' + def setup_from_flags(self): """Setup logger from flags""" - level_name = _get_level_from_flags(self.name) + level_name = self._get_level_from_flags(self.name) self.setLevel(globals()[level_name]) self.initialized = True if not logging.root.initialized: @@ -302,7 +302,7 @@ if not isinstance(logging.root, NovaRootLogger): logging.root = NovaRootLogger("nova") NovaLogger.root = logging.root NovaLogger.manager.root = logging.root -root=logging.root +root = logging.root def audit(msg, *args, **kwargs): -- cgit From 7eab72b30cad9708e976f60e121569972b835b61 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 21 Feb 2011 00:15:49 -0800 Subject: get rid of initialized flag --- nova/flags.py | 4 ++-- nova/log.py | 16 ++++------------ nova/test.py | 3 +++ nova/tests/fake_flags.py | 2 -- nova/tests/test_log.py | 6 ++---- 5 files changed, 11 insertions(+), 20 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index 2f3bdd675..72d123e21 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -29,7 +29,6 @@ import sys import gflags - class FlagValues(gflags.FlagValues): """Extension of gflags.FlagValues that allows undefined and runtime flags. @@ -93,7 +92,8 @@ class FlagValues(gflags.FlagValues): self.__dict__['__stored_argv'] = original_argv self.__dict__['__was_already_parsed'] = True self.ClearDirty() - FlagValues.initialized = True + from nova import log as logging + logging.reset() return args def Reset(self): diff --git a/nova/log.py b/nova/log.py index 57a550a0b..8d240782d 100644 --- a/nova/log.py +++ b/nova/log.py @@ -143,9 +143,7 @@ class NovaLogger(logging.Logger): """ def __init__(self, name, level=NOTSET): logging.Logger.__init__(self, name, level) - self.initialized = False - if flags.FlagValues.initialized: - self.setup_from_flags() + self.setup_from_flags() @staticmethod def _get_level_from_flags(name): @@ -166,15 +164,6 @@ class NovaLogger(logging.Logger): """Setup logger from flags""" level_name = self._get_level_from_flags(self.name) self.setLevel(globals()[level_name]) - self.initialized = True - if not logging.root.initialized: - logging.root.setup_from_flags() - - def isEnabledFor(self, level): - """Reset level after flags have been loaded""" - if not self.initialized and flags.FlagValues.initialized: - self.setup_from_flags() - return logging.Logger.isEnabledFor(self, level) def _log(self, level, msg, args, exc_info=None, extra=None, context=None): """Extract context from any log call""" @@ -304,6 +293,9 @@ if not isinstance(logging.root, NovaRootLogger): NovaLogger.manager.root = logging.root root = logging.root +def reset(): + root.setup_from_flags() + def audit(msg, *args, **kwargs): """Shortcut for logging to root log with sevrity 'AUDIT'.""" diff --git a/nova/test.py b/nova/test.py index a12cf9d32..8022396cd 100644 --- a/nova/test.py +++ b/nova/test.py @@ -32,9 +32,12 @@ from nova import context from nova import db from nova import fakerabbit from nova import flags +from nova import log as logging from nova import rpc from nova.network import manager as network_manager + from nova.tests import fake_flags +logging.reset() FLAGS = flags.FLAGS diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index 0a1011d86..59839b090 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -42,5 +42,3 @@ FLAGS.verbose = True FLAGS.sql_connection = 'sqlite:///nova.sqlite' FLAGS.use_ipv6 = True FLAGS.logfile = 'run_tests.err' -# NOTE(vish): pretend like we've loaded flags from command line -flags.FlagValues.initialized = True diff --git a/nova/tests/test_log.py b/nova/tests/test_log.py index ada8d0a56..122351ff6 100644 --- a/nova/tests/test_log.py +++ b/nova/tests/test_log.py @@ -46,14 +46,12 @@ class RootLoggerTestCase(test.TestCase): def test_will_be_verbose_if_verbose_flag_set(self): self.flags(verbose=True) - self.log.initialized = False - log.audit("foo", context=_fake_context()) + log.reset() self.assertEqual(log.DEBUG, self.log.level) def test_will_not_be_verbose_if_verbose_flag_not_set(self): self.flags(verbose=False) - self.log.initialized = False - log.audit("foo", context=_fake_context()) + log.reset() self.assertEqual(log.INFO, self.log.level) -- cgit From e729c49543c5acf354b154a3e2d9fd76a2f7da35 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 21 Feb 2011 09:17:33 +0100 Subject: Fix refresh sec groups. --- nova/virt/libvirt_conn.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 0ddf889a1..3faf01f4b 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1231,7 +1231,10 @@ class IptablesFirewallDriver(FirewallDriver): def prepare_instance_filter(self, instance): self.instances[instance['id']] = instance + self.add_filters_for_instance(instance) + self.iptables.apply() + def add_filters_for_instance(self, instance): chain_name = self._instance_chain_name(instance) self.iptables.ipv4['filter'].add_chain(chain_name) @@ -1257,18 +1260,17 @@ class IptablesFirewallDriver(FirewallDriver): for rule in ipv6_rules: self.iptables.ipv6['filter'].add_rule(chain_name, rule) + def unfilter_instance(self, instance): + self.remove_filters_for_instance(instance) self.iptables.apply() - def unfilter_instance(self, instance): + def remove_filters_for_instance(self, instance): chain_name = self._instance_chain_name(instance) self.iptables.ipv4['filter'].remove_chain(chain_name) if FLAGS.use_ipv6: self.iptables.ipv6['filter'].remove_chain(chain_name) - self.iptables.apply() - - def instance_rules(self, instance): ctxt = context.get_admin_context() @@ -1374,7 +1376,10 @@ class IptablesFirewallDriver(FirewallDriver): pass def refresh_security_group_rules(self, security_group): - self.apply_ruleset() + for instance in self.instances: + self.remove_filters_for_instance(instance) + self.add_filters_for_instance(instance) + self.iptables.apply() def _security_group_chain_name(self, security_group_id): return 'nova-sg-%s' % (security_group_id,) -- cgit From 86b202f7397b80358346e1b2a9894af81faa4f4b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 21 Feb 2011 00:17:58 -0800 Subject: fix nova-api as well --- bin/nova-api | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-api b/bin/nova-api index 8b3674880..5937f7744 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -70,7 +70,7 @@ def run_app(paste_config_file): return # NOTE(todd): redo logging config, verbose could be set in paste config - logging.root.setup_from_flags() + logging.reset() server = wsgi.Server() for app in apps: -- cgit From f9af5309cf50b3b1a4ef9799c071cbaa6b1b304f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 21 Feb 2011 00:22:45 -0800 Subject: removed extra comments and initialized from flags --- nova/flags.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index 72d123e21..e2f7960ec 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -37,7 +37,6 @@ class FlagValues(gflags.FlagValues): defined after the initial parsing. """ - initialized = False def __init__(self, extra_context=None): gflags.FlagValues.__init__(self) @@ -45,8 +44,6 @@ class FlagValues(gflags.FlagValues): self.__dict__['__was_already_parsed'] = False self.__dict__['__stored_argv'] = [] self.__dict__['__extra_context'] = extra_context - # NOTE(vish): force a pseudo flag to keep track of whether - # flags have been parsed already def __call__(self, argv): # We're doing some hacky stuff here so that we don't have to copy -- cgit From f28ed7d95afd17e55e1db25a75e065f9da0f06e6 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 21 Feb 2011 00:24:35 -0800 Subject: add docstring to reset method --- nova/log.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/log.py b/nova/log.py index 8d240782d..94eeecce0 100644 --- a/nova/log.py +++ b/nova/log.py @@ -294,6 +294,7 @@ if not isinstance(logging.root, NovaRootLogger): root = logging.root def reset(): + """Resets logging handlers. Should be called if FLAGS changes.""" root.setup_from_flags() -- cgit From bfba5b2cf8ade746d74485bd76f9d60238ccb2ea Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 21 Feb 2011 00:48:33 -0800 Subject: reset all loggers on flag change, not just root --- nova/log.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nova/log.py b/nova/log.py index 94eeecce0..2b43f7311 100644 --- a/nova/log.py +++ b/nova/log.py @@ -295,6 +295,9 @@ root = logging.root def reset(): """Resets logging handlers. Should be called if FLAGS changes.""" + for logger in logging.Logger.manager.loggerDict.itervalues(): + if isinstance(logger, NovaLogger): + logger.setup_from_flags() root.setup_from_flags() -- cgit From e773c16e1bce0e00b269394d1ed20d15884827ff Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 21 Feb 2011 01:07:46 -0800 Subject: simplify logic for parsing log level flags --- nova/log.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/nova/log.py b/nova/log.py index 2b43f7311..0cd42d00e 100644 --- a/nova/log.py +++ b/nova/log.py @@ -145,25 +145,14 @@ class NovaLogger(logging.Logger): logging.Logger.__init__(self, name, level) self.setup_from_flags() - @staticmethod - def _get_level_from_flags(name): - # if exactly "nova", or a child logger, honor the verbose flag - if (name == "nova" or name.startswith("nova.")) and FLAGS.verbose: - return 'DEBUG' + def setup_from_flags(self): + """Setup logger from flags""" for pair in FLAGS.default_log_levels: logger, _sep, level = pair.partition('=') # NOTE(todd): if we set a.b, we want a.b.c to have the same level # (but not a.bc, so we check the dot) - if name == logger: - return level - if name.startswith(logger) and name[len(logger)] == '.': - return level - return 'INFO' - - def setup_from_flags(self): - """Setup logger from flags""" - level_name = self._get_level_from_flags(self.name) - self.setLevel(globals()[level_name]) + if self.name == logger or self.name.startswith("%s." % logger): + self.setLevel(globals()[level]) def _log(self, level, msg, args, exc_info=None, extra=None, context=None): """Extract context from any log call""" @@ -281,8 +270,10 @@ class NovaRootLogger(NovaLogger): else: self.removeHandler(_filelog) self.addHandler(_streamlog) - - return NovaLogger.setup_from_flags(self) + if FLAGS.verbose: + self.setLevel(DEBUG) + else: + self.setLevel(INFO) if not isinstance(logging.root, NovaRootLogger): -- cgit From cbb0402efac4ededdda0ac2097ec087216e23931 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 21 Feb 2011 10:18:43 +0100 Subject: Also remove rules that jump to deleted chains. --- nova/network/linux_net.py | 5 ++++- nova/virt/libvirt_conn.py | 7 ++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index ecda450bf..1f96a4d55 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -104,6 +104,9 @@ class IptablesTable(object): self.chains.remove(name) self.rules = filter(lambda r: r.chain != name, self.rules) + jump_snippet = '-j %s-%s' % (binary_name, name) + self.rules = filter(lambda r: jump_snippet not in r.rule, self.rules) + def add_rule(self, chain, rule, wrap=True): if wrap and chain not in self.chains: raise ValueError(_("Unknown chain: %r") % chain) @@ -283,7 +286,7 @@ def remove_floating_forward(floating_ip, fixed_ip): def floating_forward_rules(floating_ip, fixed_ip): return [("PREROUTING", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)), ("OUTPUT", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)), - ("SNATTING", "-d %s -j DNAT --to %s" % (fixed_ip, floating_ip))] + ("SNATTING", "-d %s -j SNAT --to %s" % (fixed_ip, floating_ip))] def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): """Create a vlan and bridge unless they already exist""" diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 3faf01f4b..daf8f0ed7 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -44,9 +44,6 @@ import uuid from xml.dom import minidom -from eventlet import greenthread -from eventlet import event -from eventlet import semaphore from eventlet import tpool import IPy @@ -1246,7 +1243,7 @@ class IptablesFirewallDriver(FirewallDriver): if FLAGS.use_ipv6: self.iptables.ipv6['filter'].add_chain(chain_name) ipv6_address = self._ip_for_instance_v6(instance) - self.iptables.ipv4['filter'].add_rule('local', + self.iptables.ipv6['filter'].add_rule('local', '-d %s -j $%s' % (ipv6_address, chain_name)) @@ -1376,7 +1373,7 @@ class IptablesFirewallDriver(FirewallDriver): pass def refresh_security_group_rules(self, security_group): - for instance in self.instances: + for instance in self.instances.values(): self.remove_filters_for_instance(instance) self.add_filters_for_instance(instance) self.iptables.apply() -- cgit From 753d3a6915ad8387ea29ad1a7fb4aed74c4b71fd Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 21 Feb 2011 01:26:15 -0800 Subject: move exception hook into appropriate location and remove extra stuff from module namespace --- bin/nova-manage | 1 - nova/log.py | 45 +++++++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 878a9afaa..861798717 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -85,7 +85,6 @@ from nova.auth import manager from nova.cloudpipe import pipelib from nova.db import migration - FLAGS = flags.FLAGS flags.DECLARE('fixed_range', 'nova.network.manager') flags.DECLARE('num_networks', 'nova.network.manager') diff --git a/nova/log.py b/nova/log.py index 0cd42d00e..5ffb52cde 100644 --- a/nova/log.py +++ b/nova/log.py @@ -130,9 +130,6 @@ def basicConfig(): logging.basicConfig = basicConfig -_syslog = SysLogHandler(address='/dev/log') -_filelog = None -_streamlog = StreamHandler() class NovaLogger(logging.Logger): @@ -193,16 +190,6 @@ class NovaLogger(logging.Logger): self.error(message, **kwargs) -def handle_exception(type, value, tb): - if len(logging.root.handlers) == 0: - logging.root.addHandler(_streamlog) - logging.root.critical(str(value), exc_info=(type, value, tb)) - - -sys.excepthook = handle_exception -logging.setLoggerClass(NovaLogger) - - class NovaFormatter(logging.Formatter): """ A nova.context.RequestContext aware formatter configured through flags. @@ -251,25 +238,30 @@ _formatter = NovaFormatter() class NovaRootLogger(NovaLogger): def __init__(self, name, level=NOTSET): + self.logpath = None + self.filelog = None + self.syslog = SysLogHandler(address='/dev/log') + self.streamlog = StreamHandler() NovaLogger.__init__(self, name, level) - self.addHandler(_streamlog) def setup_from_flags(self): """Setup logger from flags""" global _filelog if FLAGS.use_syslog: - self.addHandler(_syslog) + self.addHandler(self.syslog) else: - self.removeHandler(_syslog) + self.removeHandler(self.syslog) logpath = _get_log_file_path() if logpath: - if not _filelog: - _filelog = WatchedFileHandler(logpath) - self.addHandler(_filelog) - self.removeHandler(_streamlog) + self.removeHandler(self.streamlog) + if logpath != self.logpath: + self.removeHandler(self.filelog) + self.filelog = WatchedFileHandler(logpath) + self.addHandler(self.filelog) + self.logpath = logpath else: - self.removeHandler(_filelog) - self.addHandler(_streamlog) + self.removeHandler(self.filelog) + self.addHandler(self.streamlog) if FLAGS.verbose: self.setLevel(DEBUG) else: @@ -284,6 +276,15 @@ if not isinstance(logging.root, NovaRootLogger): NovaLogger.manager.root = logging.root root = logging.root + +def handle_exception(type, value, tb): + root.critical(str(value), exc_info=(type, value, tb)) + + +sys.excepthook = handle_exception +logging.setLoggerClass(NovaLogger) + + def reset(): """Resets logging handlers. Should be called if FLAGS changes.""" for logger in logging.Logger.manager.loggerDict.itervalues(): -- cgit From 9eebe4317f86ae13ffeaca1622e9fc555bc28ebc Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 21 Feb 2011 10:42:59 +0100 Subject: Unfilter instance correctly on termination. --- nova/network/linux_net.py | 4 ++++ nova/virt/libvirt_conn.py | 8 +++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 1f96a4d55..1145bfa7a 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -101,6 +101,10 @@ class IptablesTable(object): self.chains.add(name) def remove_chain(self, name): + if name not in self.chain: + LOG.debug(_("Attempted to remove chain %s which doesn't exist"), + name) + return self.chains.remove(name) self.rules = filter(lambda r: r.chain != name, self.rules) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index daf8f0ed7..0c355e48e 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1219,9 +1219,11 @@ class IptablesFirewallDriver(FirewallDriver): """No-op. Everything is done in prepare_instance_filter""" pass - def remove_instance(self, instance): + def unfilter_instance(self, instance): if instance['id'] in self.instances: del self.instances[instance['id']] + self.remove_filters_for_instance(instance) + self.iptables.apply() else: LOG.info(_('Attempted to unfilter instance %s which is not ' 'filtered'), instance['id']) @@ -1257,10 +1259,6 @@ class IptablesFirewallDriver(FirewallDriver): for rule in ipv6_rules: self.iptables.ipv6['filter'].add_rule(chain_name, rule) - def unfilter_instance(self, instance): - self.remove_filters_for_instance(instance) - self.iptables.apply() - def remove_filters_for_instance(self, instance): chain_name = self._instance_chain_name(instance) -- cgit From 384a4525e9d6de54158cd170487fce95df814b52 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 21 Feb 2011 11:01:27 +0100 Subject: Fix typo --- nova/network/linux_net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 1145bfa7a..c5779f85f 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -101,7 +101,7 @@ class IptablesTable(object): self.chains.add(name) def remove_chain(self, name): - if name not in self.chain: + if name not in self.chains: LOG.debug(_("Attempted to remove chain %s which doesn't exist"), name) return -- cgit From 15203c9ceaa94f0cd5bad96622ee93af7662bcce Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 21 Feb 2011 12:22:29 +0100 Subject: Allow non-existing rules to be removed. --- nova/network/linux_net.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index c5779f85f..d4cfbbde9 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -125,8 +125,12 @@ class IptablesTable(object): return '%s-%s' % (binary_name, s[1:]) return s - def remove_rule(self, chain, rule): - self.rules.remove(IptablesRule(chain, rule)) + def remove_rule(self, *args, **kwargs): + try: + self.rules.remove(IptablesRule(*args, **kwargs)) + except ValueError: + LOG.debug(_("Tried to remove rule that wasn't there: %r %r"), + args, kwargs) class IptablesManager(object): def __init__(self, execute=None): -- cgit From a57dffb5fdfbfac59b9ddbe7b33d6f03b7b748ba Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 21 Feb 2011 14:16:42 +0100 Subject: PEP-8 fixes --- nova/network/linux_net.py | 28 ++++++++++++++++++++-------- nova/tests/test_network.py | 21 +++++++++------------ nova/tests/test_virt.py | 3 ++- nova/utils.py | 12 ++++++------ nova/virt/libvirt_conn.py | 2 -- 5 files changed, 37 insertions(+), 29 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index d4cfbbde9..b5d1323a1 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -129,8 +129,10 @@ class IptablesTable(object): try: self.rules.remove(IptablesRule(*args, **kwargs)) except ValueError: - LOG.debug(_("Tried to remove rule that wasn't there: %r %r"), - args, kwargs) + LOG.debug(_("Tried to remove rule that wasn't there:" + " %(args)r %(kwargs)r"), {'args': args, + 'kwargs': kwargs}) + class IptablesManager(object): def __init__(self, execute=None): @@ -142,9 +144,9 @@ class IptablesManager(object): else: self.execute = execute - self.ipv4 = { 'filter': IptablesTable(), - 'nat': IptablesTable() } - self.ipv6 = { 'filter': IptablesTable() } + self.ipv4 = {'filter': IptablesTable(), + 'nat': IptablesTable()} + self.ipv6 = {'filter': IptablesTable()} self.ipv4['nat'].add_chain('SNATTING') self.ipv4['nat'].add_rule('POSTROUTING', @@ -155,11 +157,18 @@ class IptablesManager(object): self.ipv4['filter'].add_rule('FORWARD', '-j %s-local' % (binary_name,), wrap=False) - self.ipv4['filter'].add_rule('OUTPUT', '-j %s-local' % (binary_name,), wrap=False) + self.ipv6['filter'].add_chain('local') + self.ipv6['filter'].add_rule('FORWARD', + '-j %s-local' % (binary_name,), + wrap=False) + self.ipv6['filter'].add_rule('OUTPUT', + '-j %s-local' % (binary_name,), + wrap=False) + # Wrap the builtin chains builtin_chains = {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], 'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']} @@ -172,7 +181,6 @@ class IptablesManager(object): wrap=False) self.semaphore = semaphore.Semaphore() - def apply(self): with self.semaphore: s = [('iptables', self.ipv4)] @@ -184,7 +192,8 @@ class IptablesManager(object): current_table, _ = self.execute('sudo %s-save -t %s' % (cmd, table), attempts=5) current_lines = current_table.split('\n') - new_filter = self.modify_rules(current_lines, tables[table]) + new_filter = self.modify_rules(current_lines, + tables[table]) self.execute('sudo %s-restore' % (cmd,), process_input='\n'.join(new_filter), attempts=5) @@ -285,17 +294,20 @@ def ensure_floating_forward(floating_ip, fixed_ip): iptables_manager.ipv4['nat'].add_rule(chain, rule) iptables_manager.apply() + def remove_floating_forward(floating_ip, fixed_ip): """Remove forwarding for floating ip""" for chain, rule in floating_forward_rules(floating_ip, fixed_ip): iptables_manager.ipv4['nat'].remove_rule(chain, rule) iptables_manager.apply() + def floating_forward_rules(floating_ip, fixed_ip): return [("PREROUTING", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)), ("OUTPUT", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)), ("SNATTING", "-d %s -j SNAT --to %s" % (fixed_ip, floating_ip))] + def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): """Create a vlan and bridge unless they already exist""" interface = ensure_vlan(vlan_num) diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index b28d64245..c9a62a391 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -42,15 +42,14 @@ class IptablesManagerTestCase(test.TestCase): :INPUT ACCEPT [2223527:305688874] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [2172501:140856656] --A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT --A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT --A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT --A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT --A FORWARD -d 192.168.122.0/24 -o virbr0 -m state --state RELATED,ESTABLISHED -j ACCEPT --A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT --A FORWARD -i virbr0 -o virbr0 -j ACCEPT --A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable --A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable +-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT +-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT +-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT +-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT +-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT +-A FORWARD -i virbr0 -o virbr0 -j ACCEPT +-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable +-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable COMMIT # Completed on Fri Feb 18 15:17:05 2011""" @@ -77,8 +76,7 @@ COMMIT # TODO(soren): Add stuff for ipv6 check_matrix = {4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], - 'nat': ['PREROUTING', 'INPUT', - 'OUTPUT', 'POSTROUTING']} } + 'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']}} for ip_version in check_matrix: ip = getattr(self.manager, 'ipv%d' % ip_version) @@ -90,7 +88,6 @@ COMMIT (chain,) in new_lines) self.assertTrue('-A %s -j run_tests.py-%s' % \ (chain, chain) in new_lines) - print '\n'.join(new_lines) class NetworkTestCase(test.TestCase): diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index a88e01818..11201788c 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -345,7 +345,8 @@ class IptablesFirewallTestCase(test.TestCase): self.fw.prepare_instance_filter(instance_ref) self.fw.apply_instance_filter(instance_ref) - in_rules = filter(lambda l: not l.startswith('#'), self.in_filter_rules) + in_rules = filter(lambda l: not l.startswith('#'), + self.in_filter_rules) for rule in in_rules: if not 'nova' in rule: self.assertTrue(rule in self.out_rules, diff --git a/nova/utils.py b/nova/utils.py index 644bf18fd..5b44bccb5 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -126,7 +126,8 @@ def fetchfile(url, target): execute("curl --fail %s -o %s" % (url, target)) -def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, attempts=1): +def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, + attempts=1): while attempts > 0: attempts -= 1 try: @@ -150,17 +151,16 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, attemp stdout=stdout, stderr=stderr, cmd=cmd) - # NOTE(termie): this appears to be necessary to let the subprocess call - # clean something up in between calls, without it two - # execute calls in a row hangs the second one + # NOTE(termie): this appears to be necessary to let the subprocess + # call clean something up in between calls, without + # it two execute calls in a row hangs the second one greenthread.sleep(0) return result except ProcessExecutionError: if not attempts: raise else: - greenthread.sleep(random.randint(50,300)/100) - pass + greenthread.sleep(random.randint(20, 200) / 100.0) def ssh_execute(ssh, cmd, process_input=None, diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 0c355e48e..7f74e3505 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1303,11 +1303,9 @@ class IptablesFirewallDriver(FirewallDriver): cidrv6 = self._project_cidrv6_for_instance(instance) ipv6_rules += ['-s %s -j ACCEPT' % (cidrv6,)] - security_groups = db.security_group_get_by_instance(ctxt, instance['id']) - # then, security group chains and rules for security_group in security_groups: rules = db.security_group_rule_get_by_security_group(ctxt, -- cgit From 3d2ec0f594e02018a32c8d0d7a8cc46f7ab4c849 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 21 Feb 2011 14:39:02 +0100 Subject: Wrap ipv6 rules, too --- nova/network/linux_net.py | 26 +++++++++++++++++--------- nova/tests/test_network.py | 3 ++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index b5d1323a1..f47219b2e 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -170,15 +170,23 @@ class IptablesManager(object): wrap=False) # Wrap the builtin chains - builtin_chains = {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], - 'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']} - - for table, chains in builtin_chains.iteritems(): - for chain in chains: - self.ipv4[table].add_chain(chain) - self.ipv4[table].add_rule(chain, - '-j %s-%s' % (binary_name, chain), - wrap=False) + builtin_chains = { 4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], + 'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']}, + 6: {'filter': ['INPUT', 'OUTPUT', 'FORWARD']}} + + for ip_version in builtin_chains: + if ip_version == 4: + tables = self.ipv4 + elif ip_version == 6: + tables = self.ipv6 + + for table, chains in builtin_chains[ip_version].iteritems(): + for chain in chains: + tables[table].add_chain(chain) + tables[table].add_rule(chain, + '-j %s-%s' % (binary_name, chain), + wrap=False) + self.semaphore = semaphore.Semaphore() def apply(self): diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index c9a62a391..f1d4fe133 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -76,7 +76,8 @@ COMMIT # TODO(soren): Add stuff for ipv6 check_matrix = {4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], - 'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']}} + 'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']}, + 6: {'filter': ['INPUT', 'OUTPUT', 'FORWARD']}} for ip_version in check_matrix: ip = getattr(self.manager, 'ipv%d' % ip_version) -- cgit From 1ed4df93a246a06518f2c216cd0fc60ca67eb5c4 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 21 Feb 2011 14:39:38 +0100 Subject: More PEP-8 --- nova/network/linux_net.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index f47219b2e..d7a3075cb 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -170,9 +170,9 @@ class IptablesManager(object): wrap=False) # Wrap the builtin chains - builtin_chains = { 4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], - 'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']}, - 6: {'filter': ['INPUT', 'OUTPUT', 'FORWARD']}} + builtin_chains = {4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], + 'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']}, + 6: {'filter': ['INPUT', 'OUTPUT', 'FORWARD']}} for ip_version in builtin_chains: if ip_version == 4: -- cgit From eebb9bb14edb6fd1d218b3aa18142ee739ddd715 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Mon, 21 Feb 2011 16:16:21 +0100 Subject: introducing a new flag timeout_nbd for manually setting the time in seconds for waiting for an upcoming NBD device --- nova/virt/disk.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index c5565abfa..cb639a102 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -38,6 +38,8 @@ 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') +flags.DEFINE_integer('timeout_nbd', 10, + 'time to wait for a NBD device coming up') def extend(image, size): @@ -117,7 +119,7 @@ def _link_device(image, nbd): 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 - for i in xrange(10): + for i in xrange(FLAGS.timeout_nbd): if os.path.exists("/sys/block/%s/pid" % os.path.basename(device)): return device time.sleep(1) -- cgit From ac5a1cfb0dbcebd36e7cbaab20795d03d523afee Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 21 Feb 2011 11:20:03 -0600 Subject: Stub out VM create --- nova/virt/xenapi/vmops.py | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 4f3468f8e..ac09179a3 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -27,6 +27,7 @@ import tempfile import uuid from nova import db +from nova import compute from nova import context from nova import log as logging from nova import exception @@ -49,6 +50,8 @@ class VMOps(object): def __init__(self, session): self.XenAPI = session.get_imported_xenapi() self._session = session + self.compute_api = compute.API() + VMHelper.XenAPI = self.XenAPI def list_instances(self): @@ -364,21 +367,31 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - target_vm = VMHelper.lookup(self._session, "instance-00000012") - - self._shutdown(instance, vm) - vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] - vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] - vbd_ref = VMHelper.create_vbd( - self._session, - target_vm, - vdi_ref, - 1, - False) + #self._shutdown(instance, vm) + #target_vm = VMHelper.lookup(self._session, "instance-00000012") + target_vm = self.compute_api.create( + context=context.get_admin_context(), + instance_type="m1.tiny", + image_id=1, + kernel_id=3, + ramdisk_id=2, + display_name="test", + display_description="test") + print context.get_admin_context().__dict__ + print target_vm + + #vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] + #vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] + #vbd_ref = VMHelper.create_vbd( + # self._session, + # target_vm, + # vdi_ref, + # 1, + # False) # Plug the VBD into the target instance - self._session.call_xenapi("Async.VBD.plug", vbd_ref) + #self._session.call_xenapi("Async.VBD.plug", vbd_ref) def unrescue(self, instance, callback): """Unrescue the specified instance""" -- cgit From 8b30a903a4d2c5c6ffe44e58b8531ddc889492c0 Mon Sep 17 00:00:00 2001 From: "jaypipes@gmail.com" <> Date: Mon, 21 Feb 2011 13:10:45 -0500 Subject: PEP8 errors and remove check in authors file for nova-core, since nova-core owns the translation export branch --- nova/tests/test_misc.py | 2 ++ setup.py | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/nova/tests/test_misc.py b/nova/tests/test_misc.py index 33c1777d5..7a4d512a4 100644 --- a/nova/tests/test_misc.py +++ b/nova/tests/test_misc.py @@ -46,6 +46,8 @@ class ProjectTestCase(test.TestCase): missing = set() for contributor in contributors: + if contributor == 'nova-core': + pass if not contributor in authors_file: missing.add(contributor) diff --git a/setup.py b/setup.py index 89add02c3..4ab8f386b 100644 --- a/setup.py +++ b/setup.py @@ -26,9 +26,11 @@ from setuptools.command.sdist import sdist try: import DistUtilsExtra.auto except ImportError: - print >> sys.stderr, 'To build nova you need https://launchpad.net/python-distutils-extra' + print >> sys.stderr, 'To build nova you need '\ + 'https://launchpad.net/python-distutils-extra' sys.exit(1) -assert DistUtilsExtra.auto.__version__ >= '2.18', 'needs DistUtilsExtra.auto >= 2.18' +assert DistUtilsExtra.auto.__version__ >= '2.18',\ + 'needs DistUtilsExtra.auto >= 2.18' from nova.utils import parse_mailmap, str_dict_replace -- cgit From 78ed840ef2f7066c638a76cc3192fec2f93d8450 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 21 Feb 2011 12:48:34 -0600 Subject: Enable rescue testing --- nova/virt/xenapi/vmops.py | 51 ++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3c9fd7d31..bd57dc9a1 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -406,8 +406,8 @@ class VMOps(object): if ramdisk: args['ramdisk-file'] = ramdisk task2 = self._session.async_call_plugin('glance', fn, args) - self._session.wait_for_task(instance.id, task1) - self._session.wait_for_task(instance.id, task2) + self._session.wait_for_task(task1, instance.id) + self._session.wait_for_task(task2, instance.id) LOG.debug(_("kernel/ramdisk files removed")) except self.XenAPI.Failure, exc: LOG.exception(exc) @@ -477,35 +477,36 @@ class VMOps(object): """Rescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - #self._shutdown(instance, vm) - #target_vm = VMHelper.lookup(self._session, "instance-00000012") - target_vm = self.compute_api.create( - context=context.get_admin_context(), - instance_type="m1.tiny", - image_id=1, - kernel_id=3, - ramdisk_id=2, - display_name="test", - display_description="test") - print context.get_admin_context().__dict__ - print target_vm - - #vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] - #vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] - #vbd_ref = VMHelper.create_vbd( - # self._session, - # target_vm, - # vdi_ref, - # 1, - # False) + self._shutdown(instance, vm) + target_vm = VMHelper.lookup(self._session, "instance-00000001") + #target_vm = self.compute_api.create( + # context=context.get_admin_context(), + # instance_type="m1.tiny", + # image_id=1, + # kernel_id=3, + # ramdisk_id=2, + # display_name="test", + # display_description="test") + #print context.get_admin_context().__dict__ + #print target_vm + + vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] + vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] + vbd_ref = VMHelper.create_vbd( + self._session, + target_vm, + vdi_ref, + 1, + False) # Plug the VBD into the target instance - #self._session.call_xenapi("Async.VBD.plug", vbd_ref) + self._session.call_xenapi("Async.VBD.plug", vbd_ref) + pass def unrescue(self, instance, callback): """Unrescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - target_vm = VMHelper.lookup(self._session, "instance-00000012") + target_vm = VMHelper.lookup(self._session, "instance-00000001") vbds = self._session.get_xenapi().VM.get_VBDs(target_vm) -- cgit From 5e02518d8ae5e7f57033e28ae85067d28c1ff3d8 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 21 Feb 2011 10:52:28 -0800 Subject: reset to notset if level isn't in flags --- nova/log.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nova/log.py b/nova/log.py index 5ffb52cde..61a279882 100644 --- a/nova/log.py +++ b/nova/log.py @@ -144,12 +144,14 @@ class NovaLogger(logging.Logger): def setup_from_flags(self): """Setup logger from flags""" + level = NOTSET for pair in FLAGS.default_log_levels: - logger, _sep, level = pair.partition('=') + logger, _sep, level_name = pair.partition('=') # NOTE(todd): if we set a.b, we want a.b.c to have the same level # (but not a.bc, so we check the dot) if self.name == logger or self.name.startswith("%s." % logger): - self.setLevel(globals()[level]) + level = globals()[level_name] + self.setLevel(level) def _log(self, level, msg, args, exc_info=None, extra=None, context=None): """Extract context from any log call""" -- cgit From cf006cce87ce18cdf9d7705fcc2be05b912d4c3f Mon Sep 17 00:00:00 2001 From: "jaypipes@gmail.com" <> Date: Mon, 21 Feb 2011 13:55:25 -0500 Subject: Duh, continue skips iteration, not pass. #iamanidiot --- nova/tests/test_misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_misc.py b/nova/tests/test_misc.py index 7a4d512a4..e6da6112a 100644 --- a/nova/tests/test_misc.py +++ b/nova/tests/test_misc.py @@ -47,7 +47,7 @@ class ProjectTestCase(test.TestCase): missing = set() for contributor in contributors: if contributor == 'nova-core': - pass + continue if not contributor in authors_file: missing.add(contributor) -- cgit From 8388144744849265b46d26735da01a11e35990b0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 21 Feb 2011 11:07:50 -0800 Subject: cleanup from review --- nova/log.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/nova/log.py b/nova/log.py index 61a279882..3a48c97ff 100644 --- a/nova/log.py +++ b/nova/log.py @@ -125,13 +125,6 @@ def _get_log_file_path(binary=None): return '%s.log' % (os.path.join(FLAGS.logdir, binary),) -def basicConfig(): - pass - - -logging.basicConfig = basicConfig - - class NovaLogger(logging.Logger): """ NovaLogger manages request context and formatting. @@ -176,7 +169,7 @@ class NovaLogger(logging.Logger): """Logging.exception doesn't handle kwargs, so breaks context""" if not kwargs.get('exc_info'): kwargs['exc_info'] = 1 - return self.error(msg, *args, **kwargs) + self.error(msg, *args, **kwargs) # NOTE(todd): does this really go here, or in _log ? extra = kwargs.get('extra') if not extra: @@ -271,11 +264,16 @@ class NovaRootLogger(NovaLogger): if not isinstance(logging.root, NovaRootLogger): + logging._acquireLock() for handler in logging.root.handlers: logging.root.removeHandler(handler) logging.root = NovaRootLogger("nova") + for logger in NovaLogger.manager.loggerDict.itervalues(): + logger.root = logging.root NovaLogger.root = logging.root NovaLogger.manager.root = logging.root + NovaLogger.manager.loggerDict["nova"] = logging.root + logging._releaseLock() root = logging.root @@ -289,14 +287,11 @@ logging.setLoggerClass(NovaLogger) def reset(): """Resets logging handlers. Should be called if FLAGS changes.""" - for logger in logging.Logger.manager.loggerDict.itervalues(): + for logger in NovaLogger.manager.loggerDict.itervalues(): if isinstance(logger, NovaLogger): logger.setup_from_flags() - root.setup_from_flags() def audit(msg, *args, **kwargs): """Shortcut for logging to root log with sevrity 'AUDIT'.""" - if len(logging.root.handlers) == 0: - basicConfig() logging.root.log(AUDIT, msg, *args, **kwargs) -- cgit From c7d83e26f7d6388857b4db4538602395b688aa7a Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 21 Feb 2011 11:42:46 -0800 Subject: use tests.sqlite so it doesn't conflict with running db --- nova/tests/fake_flags.py | 4 ++-- run_tests.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index 59839b090..575fefff6 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -39,6 +39,6 @@ FLAGS.num_shelves = 2 FLAGS.blades_per_shelf = 4 FLAGS.iscsi_num_targets = 8 FLAGS.verbose = True -FLAGS.sql_connection = 'sqlite:///nova.sqlite' +FLAGS.sql_connection = 'sqlite:///tests.sqlite' FLAGS.use_ipv6 = True -FLAGS.logfile = 'run_tests.err' +FLAGS.logfile = 'tests.log' diff --git a/run_tests.py b/run_tests.py index bf12c62c6..274dc4a97 100644 --- a/run_tests.py +++ b/run_tests.py @@ -58,8 +58,8 @@ class NovaTestRunner(core.TextTestRunner): if __name__ == '__main__': - if os.path.exists("nova.sqlite"): - os.unlink("nova.sqlite") + if os.path.exists("tests.sqlite"): + os.unlink("tests.sqlite") c = config.Config(stream=sys.stdout, env=os.environ, verbosity=3, -- cgit From 3392f6b4b060402c8d9a442f1a89a24fa31c9342 Mon Sep 17 00:00:00 2001 From: Anne Gentle Date: Mon, 21 Feb 2011 14:27:37 -0600 Subject: Removing duplicate installation docs and adding flag file information, plus pointing to docs.openstack.org for Admin-audience docs --- doc/source/adminguide/binaries.rst | 57 ---- doc/source/adminguide/distros/others.rst | 88 ------ doc/source/adminguide/distros/ubuntu.10.04.rst | 40 --- doc/source/adminguide/distros/ubuntu.10.10.rst | 41 --- doc/source/adminguide/euca2ools.rst | 49 ---- doc/source/adminguide/flags.rst | 23 -- doc/source/adminguide/getting.started.rst | 167 ----------- doc/source/adminguide/index.rst | 91 ------ doc/source/adminguide/managing.images.rst | 21 -- doc/source/adminguide/managing.instances.rst | 59 ---- doc/source/adminguide/managing.networks.rst | 70 ----- doc/source/adminguide/managing.projects.rst | 68 ----- doc/source/adminguide/managing.users.rst | 82 ------ doc/source/adminguide/managingsecurity.rst | 39 --- doc/source/adminguide/monitoring.rst | 27 -- doc/source/adminguide/multi.node.install.rst | 392 ------------------------- doc/source/adminguide/network.flat.rst | 60 ---- doc/source/adminguide/network.vlan.rst | 179 ----------- doc/source/adminguide/nova.manage.rst | 239 --------------- doc/source/adminguide/single.node.install.rst | 362 ----------------------- doc/source/community.rst | 12 +- doc/source/index.rst | 20 +- doc/source/object.model.rst | 14 +- doc/source/quickstart.rst | 2 +- 24 files changed, 24 insertions(+), 2178 deletions(-) delete mode 100644 doc/source/adminguide/binaries.rst delete mode 100644 doc/source/adminguide/distros/others.rst delete mode 100644 doc/source/adminguide/distros/ubuntu.10.04.rst delete mode 100644 doc/source/adminguide/distros/ubuntu.10.10.rst delete mode 100644 doc/source/adminguide/euca2ools.rst delete mode 100644 doc/source/adminguide/flags.rst delete mode 100644 doc/source/adminguide/getting.started.rst delete mode 100644 doc/source/adminguide/index.rst delete mode 100644 doc/source/adminguide/managing.images.rst delete mode 100644 doc/source/adminguide/managing.instances.rst delete mode 100644 doc/source/adminguide/managing.networks.rst delete mode 100644 doc/source/adminguide/managing.projects.rst delete mode 100644 doc/source/adminguide/managing.users.rst delete mode 100644 doc/source/adminguide/managingsecurity.rst delete mode 100644 doc/source/adminguide/monitoring.rst delete mode 100644 doc/source/adminguide/multi.node.install.rst delete mode 100644 doc/source/adminguide/network.flat.rst delete mode 100644 doc/source/adminguide/network.vlan.rst delete mode 100644 doc/source/adminguide/nova.manage.rst delete mode 100644 doc/source/adminguide/single.node.install.rst diff --git a/doc/source/adminguide/binaries.rst b/doc/source/adminguide/binaries.rst deleted file mode 100644 index 5c50a51f1..000000000 --- a/doc/source/adminguide/binaries.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. - Copyright 2010-2011 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. - -.. _binaries: - -Nova Daemons -============= - -The configuration of these binaries relies on "flagfiles" using the google -gflags package:: - - $ nova-xxxxx --flagfile flagfile - -The binaries can all run on the same machine or be spread out amongst multiple boxes in a large deployment. - -nova-api --------- - -Nova api receives xml requests and sends them to the rest of the system. It is a wsgi app that routes and authenticate requests. It supports the ec2 and openstack apis. - -nova-objectstore ----------------- - -Nova objectstore is an ultra simple file-based storage system for images that replicates most of the S3 Api. It will soon be replaced with glance and a simple image manager. - -nova-compute ------------- - -Nova compute is responsible for managing virtual machines. It loads a Service object which exposes the public methods on ComputeManager via rpc. - -nova-volume ------------ - -Nova volume is responsible for managing attachable block storage devices. It loads a Service object which exposes the public methods on VolumeManager via rpc. - -nova-network ------------- - -Nova network is responsible for managing floating and fixed ips, dhcp, bridging and vlans. It loads a Service object which exposes the public methods on one of the subclasses of NetworkManager. Different networking strategies are as simple as changing the network_manager flag:: - - $ nova-network --network_manager=nova.network.manager.FlatManager - -IMPORTANT: Make sure that you also set the network_manager on nova-api and nova_compute, since make some calls to network manager in process instead of through rpc. More information on the interactions between services, managers, and drivers can be found :ref:`here ` diff --git a/doc/source/adminguide/distros/others.rst b/doc/source/adminguide/distros/others.rst deleted file mode 100644 index ec14a9abb..000000000 --- a/doc/source/adminguide/distros/others.rst +++ /dev/null @@ -1,88 +0,0 @@ -Installation on other distros (like Debian, Fedora or CentOS ) -============================================================== - -Feel free to add additional notes for additional distributions. - -Nova installation on CentOS 5.5 -------------------------------- - -These are notes for installing OpenStack Compute on CentOS 5.5 and will be updated but are NOT final. Please test for accuracy and edit as you see fit. - -The principle botleneck for running nova on centos in python 2.6. Nova is written in python 2.6 and CentOS 5.5. comes with python 2.4. We can not update python system wide as some core utilities (like yum) is dependent on python 2.4. Also very few python 2.6 modules are available in centos/epel repos. - -Pre-reqs --------- - -Add euca2ools and EPEL repo first.:: - - cat >/etc/yum.repos.d/euca2ools.repo << EUCA_REPO_CONF_EOF - [eucalyptus] - name=euca2ools - baseurl=http://www.eucalyptussoftware.com/downloads/repo/euca2ools/1.3.1/yum/centos/ - enabled=1 - gpgcheck=0 - - EUCA_REPO_CONF_EOF - -:: - - rpm -Uvh 'http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm' - -Now install python2.6, kvm and few other libraries through yum:: - - yum -y install dnsmasq vblade kpartx kvm gawk iptables ebtables bzr screen euca2ools curl rabbitmq-server gcc gcc-c++ autoconf automake swig openldap openldap-servers nginx python26 python26-devel python26-distribute git openssl-devel python26-tools mysql-server qemu kmod-kvm libxml2 libxslt libxslt-devel mysql-devel - -Then download the latest aoetools and then build(and install) it, check for the latest version on sourceforge, exact url will change if theres a new release:: - - wget -c http://sourceforge.net/projects/aoetools/files/aoetools/32/aoetools-32.tar.gz/download - tar -zxvf aoetools-32.tar.gz - cd aoetools-32 - make - make install - -Add the udev rules for aoetools:: - - cat > /etc/udev/rules.d/60-aoe.rules << AOE_RULES_EOF - SUBSYSTEM=="aoe", KERNEL=="discover", NAME="etherd/%k", GROUP="disk", MODE="0220" - SUBSYSTEM=="aoe", KERNEL=="err", NAME="etherd/%k", GROUP="disk", MODE="0440" - SUBSYSTEM=="aoe", KERNEL=="interfaces", NAME="etherd/%k", GROUP="disk", MODE="0220" - SUBSYSTEM=="aoe", KERNEL=="revalidate", NAME="etherd/%k", GROUP="disk", MODE="0220" - # aoe block devices - KERNEL=="etherd*", NAME="%k", GROUP="disk" - AOE_RULES_EOF - -Load the kernel modules:: - - modprobe aoe - -:: - - modprobe kvm - -Now, install the python modules using easy_install-2.6, this ensures the installation are done against python 2.6 - - -easy_install-2.6 twisted sqlalchemy mox greenlet carrot daemon eventlet tornado IPy routes lxml MySQL-python -python-gflags need to be downloaded and installed manually, use these commands (check the exact url for newer releases ): - -:: - - wget -c "http://python-gflags.googlecode.com/files/python-gflags-1.4.tar.gz" - tar -zxvf python-gflags-1.4.tar.gz - cd python-gflags-1.4 - python2.6 setup.py install - cd .. - -Same for python2.6-libxml2 module, notice the --with-python and --prefix flags. --with-python ensures we are building it against python2.6 (otherwise it will build against python2.4, which is default):: - - wget -c "ftp://xmlsoft.org/libxml2/libxml2-2.7.3.tar.gz" - tar -zxvf libxml2-2.7.3.tar.gz - cd libxml2-2.7.3 - ./configure --with-python=/usr/bin/python26 --prefix=/usr - make all - make install - cd python - python2.6 setup.py install - cd .. - -Once you've done this, continue at Step 3 here: :doc:`../single.node.install` diff --git a/doc/source/adminguide/distros/ubuntu.10.04.rst b/doc/source/adminguide/distros/ubuntu.10.04.rst deleted file mode 100644 index bd0693c46..000000000 --- a/doc/source/adminguide/distros/ubuntu.10.04.rst +++ /dev/null @@ -1,40 +0,0 @@ -Installing on Ubuntu 10.04 (Lucid) -================================== - -Step 1: Install dependencies ----------------------------- -Grab the latest code from launchpad: - -:: - - bzr clone lp:nova - -Here's a script you can use to install (and then run) Nova on Ubuntu or Debian (when using Debian, edit nova.sh to have USE_PPA=0): - -.. todo:: give a link to a stable releases page - -Step 2: Install dependencies ----------------------------- - -Nova requires rabbitmq for messaging, so install that first. - -*Note:* You must have sudo installed to run these commands as shown here. - -:: - - sudo apt-get install rabbitmq-server - - -You'll see messages starting with "Reading package lists... Done" and you must confirm by typing Y that you want to continue. - -If you're running on Ubuntu 10.04, you'll need to install Twisted and python-gflags which is included in the OpenStack PPA. - -:: - - sudo apt-get install python-software-properties - sudo add-apt-repository ppa:nova-core/trunk - sudo apt-get update - sudo apt-get install python-twisted python-gflags - - -Once you've done this, continue at Step 3 here: :doc:`../single.node.install` diff --git a/doc/source/adminguide/distros/ubuntu.10.10.rst b/doc/source/adminguide/distros/ubuntu.10.10.rst deleted file mode 100644 index a3fa2def1..000000000 --- a/doc/source/adminguide/distros/ubuntu.10.10.rst +++ /dev/null @@ -1,41 +0,0 @@ -Installing on Ubuntu 10.10 (Maverick) -===================================== -Single Machine Installation (Ubuntu 10.10) - -While we wouldn't expect you to put OpenStack Compute into production on a non-LTS version of Ubuntu, these instructions are up-to-date with the latest version of Ubuntu. - -Make sure you are running Ubuntu 10.10 so that the packages will be available. This install requires more than 70 MB of free disk space. - -These instructions are based on Soren Hansen's blog entry, Openstack on Maverick. A script is in progress as well. - -Step 1: Install required prerequisites --------------------------------------- -Nova requires rabbitmq for messaging and redis for storing state (for now), so we'll install these first.:: - - sudo apt-get install rabbitmq-server redis-server - -You'll see messages starting with "Reading package lists... Done" and you must confirm by typing Y that you want to continue. - -Step 2: Install Nova packages available in Maverick Meerkat ------------------------------------------------------------ -Type or copy/paste in the following line to get the packages that you use to run OpenStack Compute.:: - - sudo apt-get install python-nova - sudo apt-get install nova-api nova-objectstore nova-compute nova-scheduler nova-network euca2ools unzip - -You'll see messages starting with "Reading package lists... Done" and you must confirm by typing Y that you want to continue. This operation may take a while as many dependent packages will be installed. Note: there is a dependency problem with python-nova which can be worked around by installing first. - -When the installation is complete, you'll see the following lines confirming::: - - Adding system user `nova' (UID 106) ... - Adding new user `nova' (UID 106) with group `nogroup' ... - Not creating home directory `/var/lib/nova'. - Setting up nova-scheduler (0.9.1~bzr331-0ubuntu2) ... - * Starting nova scheduler nova-scheduler - WARNING:root:Starting scheduler node - ...done. - Processing triggers for libc-bin ... - ldconfig deferred processing now taking place - Processing triggers for python-support ... - -Once you've done this, continue at Step 3 here: :doc:`../single.node.install` diff --git a/doc/source/adminguide/euca2ools.rst b/doc/source/adminguide/euca2ools.rst deleted file mode 100644 index 6f0c57358..000000000 --- a/doc/source/adminguide/euca2ools.rst +++ /dev/null @@ -1,49 +0,0 @@ -Euca2ools -========= - -Nova is compatible with most of the euca2ools command line utilities. Both Administrators and Users will find these tools helpful for day-to-day administration. - -* euca-add-group -* euca-delete-bundle -* euca-describe-instances -* euca-register -* euca-add-keypair -* euca-delete-group -* euca-describe-keypairs -* euca-release-address -* euca-allocate-address -* euca-delete-keypair -* euca-describe-regions -* euca-reset-image-attribute -* euca-associate-address -* euca-delete-snapshot -* euca-describe-snapshots -* euca-revoke -* euca-attach-volume -* euca-delete-volume -* euca-describe-volumes -* euca-run-instances -* euca-authorize -* euca-deregister -* euca-detach-volume -* euca-terminate-instances -* euca-bundle-image -* euca-describe-addresses -* euca-disassociate-address -* euca-unbundle -* euca-bundle-vol -* euca-describe-availability-zones -* euca-download-bundle -* euca-upload-bundle -* euca-confirm-product-instance -* euca-describe-groups -* euca-get-console-output -* euca-version -* euca-create-snapshot -* euca-describe-image-attribute -* euca-modify-image-attribute -* euca-create-volume -* euca-describe-images -* euca-reboot-instances - - diff --git a/doc/source/adminguide/flags.rst b/doc/source/adminguide/flags.rst deleted file mode 100644 index 072f0a1a5..000000000 --- a/doc/source/adminguide/flags.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. - Copyright 2010-2011 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. - -Flags and Flagfiles -=================== - -* python-gflags -* flagfiles -* list of flags by component (see concepts list) diff --git a/doc/source/adminguide/getting.started.rst b/doc/source/adminguide/getting.started.rst deleted file mode 100644 index 675d8e664..000000000 --- a/doc/source/adminguide/getting.started.rst +++ /dev/null @@ -1,167 +0,0 @@ -.. - Copyright 2010-2011 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. - -Getting Started with Nova -========================= - -This code base is continually changing, so dependencies also change. If you -encounter any problems, see the :doc:`../community` page. -The `contrib/nova.sh` script should be kept up to date, and may be a good -resource to review when debugging. - -The purpose of this document is to get a system installed that you can use to -test your setup assumptions. Working from this base installtion you can -tweak configurations and work with different flags to monitor interaction with -your hardware, network, and other factors that will allow you to determine -suitability for your deployment. After following this setup method, you should -be able to experiment with different managers, drivers, and flags to get the -best performance. - -Dependencies ------------- - -Related servers we rely on - -* **RabbitMQ**: messaging queue, used for all communication between components - -Optional servers - -* **OpenLDAP**: By default, the auth server uses the RDBMS-backed datastore by - setting FLAGS.auth_driver to `nova.auth.dbdriver.DbDriver`. But OpenLDAP - (or LDAP) could be configured by specifying `nova.auth.ldapdriver.LdapDriver`. - There is a script in the sources (`nova/auth/slap.sh`) to install a very basic - openldap server on ubuntu. -* **ReDIS**: There is a fake ldap auth driver - `nova.auth.ldapdriver.FakeLdapDriver` that backends to redis. This was - created for testing ldap implementation on systems that don't have an easy - means to install ldap. -* **MySQL**: Either MySQL or another database supported by sqlalchemy needs to - be avilable. Currently, only sqlite3 an mysql have been tested. - -Python libraries that we use (from pip-requires): - -.. literalinclude:: ../../../tools/pip-requires - -Other libraries: - -* **XenAPI**: Needed only for Xen Cloud Platform or XenServer support. Available - from http://wiki.xensource.com/xenwiki/XCP_SDK or - http://community.citrix.com/cdn/xs/sdks. - -External unix tools that are required: - -* iptables -* ebtables -* gawk -* curl -* kvm -* libvirt -* dnsmasq -* vlan -* open-iscsi and iscsitarget (if you use iscsi volumes) -* aoetools and vblade-persist (if you use aoe-volumes) - -Nova uses cutting-edge versions of many packages. There are ubuntu packages in -the nova-core trunk ppa. You can use add this ppa to your sources list on an -ubuntu machine with the following commands:: - - sudo apt-get install -y python-software-properties - sudo add-apt-repository ppa:nova-core/trunk - -Recommended ------------ - -* euca2ools: python implementation of aws ec2-tools and ami tools -* build tornado to use C module for evented section - - -Installation --------------- - -You can install from packages for your particular Linux distribution if they are -available. Otherwise you can install from source by checking out the source -files from the `Nova Source Code Repository `_ -and running:: - - python setup.py install - -Configuration ---------------- - -Configuring the host system -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -As you read through the Administration Guide you will notice configuration hints -inline with documentation on the subsystem you are configuring. Presented in -this "Getting Started with Nova" document, we only provide what you need to -get started as quickly as possible. For a more detailed description of system -configuration, start reading through :doc:`multi.node.install`. - -* Create a volume group (you can use an actual disk for the volume group as - well):: - - # This creates a 1GB file to create volumes out of - dd if=/dev/zero of=MY_FILE_PATH bs=100M count=10 - losetup --show -f MY_FILE_PATH - # replace /dev/loop0 below with whatever losetup returns - # nova-volumes is the default for the --volume_group flag - vgcreate nova-volumes /dev/loop0 - - -Configuring Nova -~~~~~~~~~~~~~~~~ - -Configuration of the entire system is performed through python-gflags. The -best way to track configuration is through the use of a flagfile. - -A flagfile is specified with the ``--flagfile=FILEPATH`` argument to the binary -when you launch it. Flagfiles for nova are typically stored in -``/etc/nova/nova.conf``, and flags specific to a certain program are stored in -``/etc/nova/nova-COMMAND.conf``. Each configuration file can include another -flagfile, so typically a file like ``nova-manage.conf`` would have as its first -line ``--flagfile=/etc/nova/nova.conf`` to load the common flags before -specifying overrides or additional options. - -A sample configuration to test the system follows:: - - --verbose - --nodaemon - --auth_driver=nova.auth.dbdriver.DbDriver - -Running ---------- - -There are many parts to the nova system, each with a specific function. They -are built to be highly-available, so there are may configurations they can be -run in (ie: on many machines, many listeners per machine, etc). This part -of the guide only gets you started quickly, to learn about HA options, see -:doc:`multi.node.install`. - -Launch supporting services - -* rabbitmq -* redis (optional) -* mysql (optional) -* openldap (optional) - -Launch nova components, each should have ``--flagfile=/etc/nova/nova.conf`` - -* nova-api -* nova-compute -* nova-objectstore -* nova-volume -* nova-scheduler diff --git a/doc/source/adminguide/index.rst b/doc/source/adminguide/index.rst deleted file mode 100644 index 3bd72cfdc..000000000 --- a/doc/source/adminguide/index.rst +++ /dev/null @@ -1,91 +0,0 @@ -.. - Copyright 2010-2011 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. - -Administration Guide -==================== - -This guide describes the basics of running and managing Nova. - -Running the Cloud ------------------ - -The fastest way to get a test cloud running is by following the directions in the :doc:`../quickstart`. - -Nova's cloud works via the interaction of a series of daemon processes that reside persistently on the host machine(s). Fortunately, the :doc:`../quickstart` process launches sample versions of all these daemons for you. Once you are familiar with basic Nova usage, you can learn more about daemons by reading :doc:`../service.architecture` and :doc:`binaries`. - -Administration Utilities ------------------------- - -There are two main tools that a system administrator will find useful to manage their Nova cloud: - -.. toctree:: - :maxdepth: 1 - - nova.manage - euca2ools - -The nova-manage command may only be run by users with admin priviledges. Commands for euca2ools can be used by all users, though specific commands may be restricted by Role Based Access Control. You can read more about creating and managing users in :doc:`managing.users` - -User and Resource Management ----------------------------- - -The nova-manage and euca2ools commands provide the basic interface to perform a broad range of administration functions. In this section, you can read more about how to accomplish specific administration tasks. - -For background on the core objects referenced in this section, see :doc:`../object.model` - -.. toctree:: - :maxdepth: 1 - - managing.users - managing.projects - managing.instances - managing.images - managing.volumes - managing.networks - -Deployment ----------- - -For a starting multi-node architecture, you would start with two nodes - a cloud controller node and a compute node. The cloud controller node contains the nova- services plus the Nova database. The compute node installs all the nova-services but then refers to the database installation, which is hosted by the cloud controller node. Ensure that the nova.conf file is identical on each node. If you find performance issues not related to database reads or writes, but due to the messaging queue backing up, you could add additional messaging services (rabbitmq). - -.. toctree:: - :maxdepth: 1 - - multi.node.install - dbsync - - -Networking -^^^^^^^^^^ - -.. toctree:: - :maxdepth: 1 - - multi.node.install - network.vlan.rst - network.flat.rst - - -Advanced Topics ---------------- - -.. toctree:: - :maxdepth: 1 - - flags - monitoring - diff --git a/doc/source/adminguide/managing.images.rst b/doc/source/adminguide/managing.images.rst deleted file mode 100644 index c5d93a6e8..000000000 --- a/doc/source/adminguide/managing.images.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. - Copyright 2010-2011 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. - -Managing Images -=============== - -.. todo:: Put info on managing images here! diff --git a/doc/source/adminguide/managing.instances.rst b/doc/source/adminguide/managing.instances.rst deleted file mode 100644 index e62352017..000000000 --- a/doc/source/adminguide/managing.instances.rst +++ /dev/null @@ -1,59 +0,0 @@ -.. - Copyright 2010-2011 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. - -Managing Instances -================== - -Keypairs --------- - -Images can be shared by many users, so it is dangerous to put passwords into the images. Nova therefore supports injecting ssh keys into instances before they are booted. This allows a user to login to the instances that he or she creates securely. Generally the first thing that a user does when using the system is create a keypair. Nova generates a public and private key pair, and sends the private key to the user. The public key is stored so that it can be injected into instances. - -Keypairs are created through the api. They can be created on the command line using the euca2ools script euca-add-keypair. Refer to the man page for the available options. Example usage:: - - euca-add-keypair test > test.pem - chmod 600 test.pem - euca-run-instances -k test -t m1.tiny ami-tiny - # wait for boot - ssh -i test.pem root@ip.of.instance - - -Basic Management ----------------- -Instance management can be accomplished with euca commands: - - -To run an instance: - -:: - - euca-run-instances - - -To terminate an instance: - -:: - - euca-terminate-instances - -To reboot an instance: - -:: - - euca-reboot-instances - -See the euca2ools documentation for more information diff --git a/doc/source/adminguide/managing.networks.rst b/doc/source/adminguide/managing.networks.rst deleted file mode 100644 index 9eea46d70..000000000 --- a/doc/source/adminguide/managing.networks.rst +++ /dev/null @@ -1,70 +0,0 @@ -.. - Copyright 2010-2011 United States Government as represented by the - Administrator of the National Aeronautics and Space Administration. - Overview Sections Copyright 2010-2011 Citrix - 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. - -Networking Overview -=================== -In Nova, users organize their cloud resources in projects. A Nova project consists of a number of VM instances created by a user. For each VM instance, Nova assigns to it a private IP address. (Currently, Nova only supports Linux bridge networking that allows the virtual interfaces to connect to the outside network through the physical interface. Other virtual network technologies, such as Open vSwitch, could be supported in the future.) The Network Controller provides virtual networks to enable compute servers to interact with each other and with the public network. - -Nova Network Strategies ------------------------ - -Currently, Nova supports three kinds of networks, implemented in three "Network Manager" types respectively: Flat Network Manager, Flat DHCP Network Manager, and VLAN Network Manager. The three kinds of networks can co-exist in a cloud system. However, the scheduler for selecting the type of network for a given project is not yet implemented. Here is a brief description of each of the different network strategies, with a focus on the VLAN Manager in a separate section. - -Read more about Nova network strategies here: - -.. toctree:: - :maxdepth: 1 - - network.flat.rst - network.vlan.rst - - -Network Management Commands ---------------------------- - -Admins and Network Administrators can use the 'nova-manage' command to manage network resources: - -VPN Management -~~~~~~~~~~~~~~ - -* vpn list: Print a listing of the VPNs for all projects. - * arguments: none -* vpn run: Start the VPN for a given project. - * arguments: project -* vpn spawn: Run all VPNs. - * arguments: none - - -Floating IP Management -~~~~~~~~~~~~~~~~~~~~~~ - -* floating create: Creates floating ips for host by range - * arguments: host ip_range -* floating delete: Deletes floating ips by range - * arguments: range -* floating list: Prints a listing of all floating ips - * arguments: none - -Network Management -~~~~~~~~~~~~~~~~~~ - -* network create: Creates fixed ips for host by range - * arguments: [fixed_range=FLAG], [num_networks=FLAG], - [network_size=FLAG], [vlan_start=FLAG], - [vpn_start=FLAG] - diff --git a/doc/source/adminguide/managing.projects.rst b/doc/source/adminguide/managing.projects.rst deleted file mode 100644 index 5dd7f2de9..000000000 --- a/doc/source/adminguide/managing.projects.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. - Copyright 2010-2011 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. - -Managing Projects -================= - -Projects are isolated resource containers forming the principal organizational structure within Nova. They consist of a separate vlan, volumes, instances, images, keys, and users. - -Although the original ec2 api only supports users, nova adds the concept of projects. A user can specify which project he or she wishes to use by appending `:project_id` to his or her access key. If no project is specified in the api request, nova will attempt to use a project with the same id as the user. - -The api will return NotAuthorized if a normal user attempts to make requests for a project that he or she is not a member of. Note that admins or users with special admin roles skip this check and can make requests for any project. - -To create a project, use the `project create` command of nova-manage. The syntax is nova-manage project create projectname manager_id [description] You must specify a projectname and a manager_id. For example:: - nova-manage project create john_project john "This is a sample project" - -You can add and remove users from projects with `project add` and `project remove`:: - nova-manage project add john_project john - nova-manage project remove john_project john - -Project Commands ----------------- - -Admins and Project Managers can use the 'nova-manage project' command to manage project resources: - -* project add: Adds user to project - * arguments: project user -* project create: Creates a new project - * arguments: name project_manager [description] -* project delete: Deletes an existing project - * arguments: project_id -* project environment: Exports environment variables to an sourcable file - * arguments: project_id user_id [filename='novarc] -* project list: lists all projects - * arguments: none -* project remove: Removes user from project - * arguments: project user -* project scrub: Deletes data associated with project - * arguments: project -* project zipfile: Exports credentials for project to a zip file - * arguments: project_id user_id [filename='nova.zip] - -Setting Quotas --------------- -Nova utilizes a quota system at the project level to control resource consumption across available hardware resources. Current quota controls are available to limit the: - -* Number of volumes which may be created -* Total size of all volumes within a project as measured in GB -* Number of instances which may be launched -* Number of processor cores which may be allocated -* Publicly accessible IP addresses - -Use the following command to set quotas for a project -* project quota: Set or display quotas for project - * arguments: project_id [key] [value] diff --git a/doc/source/adminguide/managing.users.rst b/doc/source/adminguide/managing.users.rst deleted file mode 100644 index 392142e86..000000000 --- a/doc/source/adminguide/managing.users.rst +++ /dev/null @@ -1,82 +0,0 @@ -Managing Users -============== - - -Users and Access Keys ---------------------- - -Access to the ec2 api is controlled by an access and secret key. The user's access key needs to be included in the request, and the request must be signed with the secret key. Upon receipt of api requests, nova will verify the signature and execute commands on behalf of the user. - -In order to begin using nova, you will need a to create a user. This can be easily accomplished using the user create or user admin commands in nova-manage. `user create` will create a regular user, whereas `user admin` will create an admin user. The syntax of the command is nova-manage user create username [access] [secret]. For example:: - - nova-manage user create john my-access-key a-super-secret-key - -If you do not specify an access or secret key, a random uuid will be created automatically. - -Credentials ------------ - -Nova can generate a handy set of credentials for a user. These credentials include a CA for bundling images and a file for setting environment variables to be used by euca2ools. If you don't need to bundle images, just the environment script is required. You can export one with the `project environment` command. The syntax of the command is nova-manage project environment project_id user_id [filename]. If you don't specify a filename, it will be exported as novarc. After generating the file, you can simply source it in bash to add the variables to your environment:: - - nova-manage project environment john_project john - . novarc - -If you do need to bundle images, you will need to get all of the credentials using `project zipfile`. Note that zipfile will give you an error message if networks haven't been created yet. Otherwise zipfile has the same syntax as environment, only the default file name is nova.zip. Example usage:: - - nova-manage project zipfile john_project john - unzip nova.zip - . novarc - -Role Based Access Control -------------------------- -Roles control the api actions that a user is allowed to perform. For example, a user cannot allocate a public ip without the `netadmin` role. It is important to remember that a users de facto permissions in a project is the intersection of user (global) roles and project (local) roles. So for john to have netadmin permissions in his project, he needs to separate roles specified. You can add roles with `role add`. The syntax is nova-manage role add user_id role [project_id]. Let's give john the netadmin role for his project:: - - nova-manage role add john netadmin - nova-manage role add john netadmin john_project - -Role-based access control (RBAC) is an approach to restricting system access to authorized users based on an individual’s role within an organization. Various employee functions require certain levels of system access in order to be successful. These functions are mapped to defined roles and individuals are categorized accordingly. Since users are not assigned permissions directly, but only acquire them through their role (or roles), management of individual user rights becomes a matter of assigning appropriate roles to the user. This simplifies common operations, such as adding a user, or changing a user's department. - -Nova’s rights management system employs the RBAC model and currently supports the following five roles: - -* **Cloud Administrator.** (admin) Users of this class enjoy complete system access. -* **IT Security.** (itsec) This role is limited to IT security personnel. It permits role holders to quarantine instances. -* **Project Manager.** (projectmanager)The default for project owners, this role affords users the ability to add other users to a project, interact with project images, and launch and terminate instances. -* **Network Administrator.** (netadmin) Users with this role are permitted to allocate and assign publicly accessible IP addresses as well as create and modify firewall rules. -* **Developer.** This is a general purpose role that is assigned to users by default. - -RBAC management is exposed through the dashboard for simplified user management. - - -User Commands -~~~~~~~~~~~~ - -Users, including admins, are created through the ``user`` commands. - -* user admin: creates a new admin and prints exports - * arguments: name [access] [secret] -* user create: creates a new user and prints exports - * arguments: name [access] [secret] -* user delete: deletes an existing user - * arguments: name -* user exports: prints access and secrets for user in export format - * arguments: name -* user list: lists all users - * arguments: none -* user modify: update a users keys & admin flag - * arguments: accesskey secretkey admin - * leave any field blank to ignore it, admin should be 'T', 'F', or blank - - -User Role Management -~~~~~~~~~~~~~~~~~~~~ - -* role add: adds role to user - * if project is specified, adds project specific role - * arguments: user, role [project] -* role has: checks to see if user has role - * if project is specified, returns True if user has - the global role and the project role - * arguments: user, role [project] -* role remove: removes role from user - * if project is specified, removes project specific role - * arguments: user, role [project] diff --git a/doc/source/adminguide/managingsecurity.rst b/doc/source/adminguide/managingsecurity.rst deleted file mode 100644 index 7893925e7..000000000 --- a/doc/source/adminguide/managingsecurity.rst +++ /dev/null @@ -1,39 +0,0 @@ -.. - Copyright 2010-2011 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. - -Security Considerations -======================= - -.. todo:: This doc is vague and just high-level right now. Describe architecture that enables security. - -The goal of securing a cloud computing system involves both protecting the instances, data on the instances, and -ensuring users are authenticated for actions and that borders are understood by the users and the system. -Protecting the system from intrusion or attack involves authentication, network protections, and -compromise detection. - -Key Concepts ------------- - -Authentication - Each instance is authenticated with a key pair. - -Network - Instances can communicate with each other but you can configure the boundaries through firewall -configuration. - -Monitoring - Log all API commands and audit those logs. - -Encryption - Data transfer between instances is not encrypted. - diff --git a/doc/source/adminguide/monitoring.rst b/doc/source/adminguide/monitoring.rst deleted file mode 100644 index 2c93c71b5..000000000 --- a/doc/source/adminguide/monitoring.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. - Copyright 2010-2011 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. - -Monitoring -========== - -* components -* throughput -* exceptions -* hardware - -* ganglia -* syslog diff --git a/doc/source/adminguide/multi.node.install.rst b/doc/source/adminguide/multi.node.install.rst deleted file mode 100644 index c53455e3e..000000000 --- a/doc/source/adminguide/multi.node.install.rst +++ /dev/null @@ -1,392 +0,0 @@ - -Installing Nova on Multiple Servers -=================================== - -When you move beyond evaluating the technology and into building an actual -production environment, you will need to know how to configure your datacenter -and how to deploy components across your clusters. This guide should help you -through that process. - -You can install multiple nodes to increase performance and availability of the OpenStack Compute installation. - -This setup is based on an Ubuntu Lucid 10.04 installation with the latest updates. Most of this works around issues that need to be resolved either in packaging or bug-fixing. It also needs to eventually be generalized, but the intent here is to get the multi-node configuration bootstrapped so folks can move forward. - -For a starting architecture, these instructions describing installing a cloud controller node and a compute node. The cloud controller node contains the nova- services plus the database. The compute node installs all the nova-services but then refers to the database installation, which is hosted by the cloud controller node. - -Requirements for a multi-node installation ------------------------------------------- - -* You need a real database, compatible with SQLAlchemy (mysql, postgresql) There's not a specific reason to choose one over another, it basically depends what you know. MySQL is easier to do High Availability (HA) with, but people may already know PostgreSQL. We should document both configurations, though. -* For a recommended HA setup, consider a MySQL master/slave replication, with as many slaves as you like, and probably a heartbeat to kick one of the slaves into being a master if it dies. -* For performance optimization, split reads and writes to the database. MySQL proxy is the easiest way to make this work if running MySQL. - -Assumptions ------------ - -* Networking is configured between/through the physical machines on a single subnet. -* Installation and execution are both performed by ROOT user. - - -Scripted Installation ---------------------- -A script available to get your OpenStack cloud running quickly. You can copy the file to the server where you want to install OpenStack Compute services - typically you would install a compute node and a cloud controller node. - -You must run these scripts with root permissions. - -From a server you intend to use as a cloud controller node, use this command to get the cloud controller script. This script is a work-in-progress and the maintainer plans to keep it up, but it is offered "as-is." Feel free to collaborate on it in GitHub - https://github.com/dubsquared/OpenStack-NOVA-Installer-Script/. - -:: - - wget --no-check-certificate https://github.com/dubsquared/OpenStack-NOVA-Installer-Script/raw/master/nova-CC-install-v1.1.sh - -Ensure you can execute the script by modifying the permissions on the script file. - -:: - - sudo chmod 755 nova-CC-install-v1.1.sh - - -:: - - sudo ./nova-CC-install-v1.1.sh - -Next, from a server you intend to use as a compute node (doesn't contain the database), install the nova services. You can use the nova-NODE-installer.sh script from the above github-hosted project for the compute node installation. - -Copy the nova.conf from the cloud controller node to the compute node. - -Restart related services:: - - libvirtd restart; service nova-network restart; service nova-compute restart; service nova-api restart; service nova-objectstore restart; service nova-scheduler restart - -You can go to the `Configuration section`_ for next steps. - -Manual Installation - Step-by-Step ----------------------------------- -The following sections show you how to install Nova manually with a cloud controller node and a separate compute node. The cloud controller node contains the database plus all nova- services, and the compute node runs nova- services only. - -Cloud Controller Installation -````````````````````````````` -On the cloud controller node, you install nova services and the related helper applications, and then configure with the nova.conf file. You will then copy the nova.conf file to the compute node, which you install as a second node in the `Compute Installation`_. - -Step 1 - Use apt-get to get the latest code -------------------------------------------- - -1. Setup Nova PPA with https://launchpad.net/~nova-core/+archive/trunk. The ‘python-software-properties’ package is a pre-requisite for setting up the nova package repo: - -:: - - sudo apt-get install python-software-properties - sudo add-apt-repository ppa:nova-core/trunk - -2. Run update. - -:: - - sudo apt-get update - -3. Install python required packages, nova-packages, and helper apps. - -:: - - sudo apt-get install python-greenlet python-mysqldb python-nova nova-common nova-doc nova-api nova-network nova-objectstore nova-scheduler nova-compute euca2ools unzip - -It is highly likely that there will be errors when the nova services come up since they are not yet configured. Don't worry, you're only at step 1! - -Step 2 Set up configuration file (installed in /etc/nova) ---------------------------------------------------------- - -1. Nova development has consolidated all config files to nova.conf as of November 2010. There is a default set of options that are already configured in nova.conf: - -:: - ---daemonize=1 ---dhcpbridge_flagfile=/etc/nova/nova.conf ---dhcpbridge=/usr/bin/nova-dhcpbridge ---logdir=/var/log/nova ---state_path=/var/lib/nova - -The following items ALSO need to be defined in /etc/nova/nova.conf. I’ve added some explanation of the variables, as comments CANNOT be in nova.conf. There seems to be an issue with nova-manage not processing the comments/whitespace correctly: - ---sql_connection ### Location of Nova SQL DB - ---s3_host ### This is where Nova is hosting the objectstore service, which will contain the VM images and buckets - ---rabbit_host ### This is where the rabbit AMQP messaging service is hosted - ---cc_host ### This is where the the nova-api service lives - ---verbose ### Optional but very helpful during initial setup - ---ec2_url ### The location to interface nova-api - ---network_manager ### Many options here, discussed below. This is how your controller will communicate with additional Nova nodes and VMs: - -nova.network.manager.FlatManager # Simple, no-vlan networking type -nova.network.manager. FlatDHCPManager # Flat networking with DHCP -nova.network.manager.VlanManager # Vlan networking with DHCP – /DEFAULT/ if no network manager is defined in nova.conf - ---fixed_range= ### This will be the IP network that ALL the projects for future VM guests will reside on. E.g. 192.168.0.0/12 - ---network_size=<# of addrs> ### This is the total number of IP Addrs to use for VM guests, of all projects. E.g. 5000 - -The following code can be cut and paste, and edited to your setup: - -Note: CC_ADDR= - -Detailed explanation of the following example is available above. - -:: - ---sql_connection=mysql://root:nova@/nova ---s3_host= ---rabbit_host= ---cc_host= ---verbose ---ec2_url=http://:8773/services/Cloud ---network_manager=nova.network.manager.VlanManager ---fixed_range= ---network_size=<# of addrs> - -2. Create a “nova” group, and set permissions:: - - addgroup nova - -The Nova config file should have its owner set to root:nova, and mode set to 0644, since they contain your MySQL server's root password. :: - - chown -R root:nova /etc/nova - chmod 644 /etc/nova/nova.conf - -Step 3 - Setup the SQL DB (MySQL for this setup) ------------------------------------------------- - -1. First you 'preseed' to bypass all the installation prompts:: - - bash - MYSQL_PASS=nova - cat < - # The loopback network interface - auto lo - iface lo inet loopback - - # Networking for NOVA - auto br100 - - iface br100 inet dhcp - bridge_ports eth0 - bridge_stp off - bridge_maxwait 0 - bridge_fd 0 - < end /etc/network/interfaces > - -Next, restart networking to apply the changes:: - - sudo /etc/init.d/networking restart - -Configuration -````````````` - -On the Compute node, you should continue with these configuration steps. - -Step 1 - Set up the Nova environment ------------------------------------- - -These are the commands you run to update the database if needed, and then set up a user and project:: - - /usr/bin/python /usr/bin/nova-manage db sync - /usr/bin/python /usr/bin/nova-manage user admin - /usr/bin/python /usr/bin/nova-manage project create - /usr/bin/python /usr/bin/nova-manage network create - -Here is an example of what this looks like with real data:: - - /usr/bin/python /usr/bin/nova-manage db sync - /usr/bin/python /usr/bin/nova-manage user admin dub - /usr/bin/python /usr/bin/nova-manage project create dubproject dub - /usr/bin/python /usr/bin/nova-manage network create 192.168.0.0/24 1 255 - -(I chose a /24 since that falls inside my /12 range I set in ‘fixed-range’ in nova.conf. Currently, there can only be one network, and I am using the max IP’s available in a /24. You can choose to use any valid amount that you would like.) - -Note: The nova-manage service assumes that the first IP address is your network (like 192.168.0.0), that the 2nd IP is your gateway (192.168.0.1), and that the broadcast is the very last IP in the range you defined (192.168.0.255). If this is not the case you will need to manually edit the sql db 'networks' table.o. - -On running the "nova-manage network create" command, entries are made in the 'networks' and 'fixed_ips' table. However, one of the networks listed in the 'networks' table needs to be marked as bridge in order for the code to know that a bridge exists. The Network is marked as bridged automatically based on the type of network manager selected. You only need to mark the network as a bridge if you chose FlatManager as your network type. More information can be found at the end of this document discussing setting up the bridge device. - - -Step 2 - Create Nova certifications ------------------------------------ - -1. Generate the certs as a zip file. These are the certs you will use to launch instances, bundle images, and all the other assorted api functions. - -:: - - mkdir –p /root/creds - /usr/bin/python /usr/bin/nova-manage project zipfile $NOVA_PROJECT $NOVA_PROJECT_USER /root/creds/novacreds.zip - -2. Unzip them in your home directory, and add them to your environment. - -:: - - unzip /root/creds/novacreds.zip -d /root/creds/ - cat /root/creds/novarc >> ~/.bashrc - source ~/.bashrc - -Step 3 - Restart all relevant services --------------------------------------- - -Restart all six services in total, just to cover the entire spectrum:: - - libvirtd restart; service nova-network restart; service nova-compute restart; service nova-api restart; service nova-objectstore restart; service nova-scheduler restart - -Step 4 - Closing steps, and cleaning up ---------------------------------------- - -One of the most commonly missed configuration areas is not allowing the proper access to VMs. Use the 'euca-authorize' command to enable access. Below, you will find the commands to allow 'ping' and 'ssh' to your VMs:: - - euca-authorize -P icmp -t -1:-1 default - euca-authorize -P tcp -p 22 default - -Another common issue is you cannot ping or SSH your instances after issusing the 'euca-authorize' commands. Something to look at is the amount of 'dnsmasq' processes that are running. If you have a running instance, check to see that TWO 'dnsmasq' processes are running. If not, perform the following:: - - killall dnsmasq - service nova-network restart - -To avoid issues with KVM and permissions with Nova, run the following commands to ensure we have VM's that are running optimally:: - - chgrp kvm /dev/kvm - chmod g+rwx /dev/kvm - -If you want to use the 10.04 Ubuntu Enterprise Cloud images that are readily available at http://uec-images.ubuntu.com/releases/10.04/release/, you may run into delays with booting. Any server that does not have nova-api running on it needs this iptables entry so that UEC images can get metadata info. On compute nodes, configure the iptables with this next step:: - - # iptables -t nat -A PREROUTING -d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination $NOVA_API_IP:8773 - -Testing the Installation -```````````````````````` - -You can confirm that your compute node is talking to your cloud controller. From the cloud controller, run this database query:: - - mysql -u$MYSQL_USER -p$MYSQL_PASS nova -e 'select * from services;' - -In return, you should see something similar to this:: - +---------------------+---------------------+------------+---------+----+----------+----------------+-----------+--------------+----------+-------------------+ - | created_at | updated_at | deleted_at | deleted | id | host | binary | topic | report_count | disabled | availability_zone | - +---------------------+---------------------+------------+---------+----+----------+----------------+-----------+--------------+----------+-------------------+ - | 2011-01-28 22:52:46 | 2011-02-03 06:55:48 | NULL | 0 | 1 | osdemo02 | nova-network | network | 46064 | 0 | nova | - | 2011-01-28 22:52:48 | 2011-02-03 06:55:57 | NULL | 0 | 2 | osdemo02 | nova-compute | compute | 46056 | 0 | nova | - | 2011-01-28 22:52:52 | 2011-02-03 06:55:50 | NULL | 0 | 3 | osdemo02 | nova-scheduler | scheduler | 46065 | 0 | nova | - | 2011-01-29 23:49:29 | 2011-02-03 06:54:26 | NULL | 0 | 4 | osdemo01 | nova-compute | compute | 37050 | 0 | nova | - | 2011-01-30 23:42:24 | 2011-02-03 06:55:44 | NULL | 0 | 9 | osdemo04 | nova-compute | compute | 28484 | 0 | nova | - | 2011-01-30 21:27:28 | 2011-02-03 06:54:23 | NULL | 0 | 8 | osdemo05 | nova-compute | compute | 29284 | 0 | nova | - +---------------------+---------------------+------------+---------+----+----------+----------------+-----------+--------------+----------+-------------------+ -You can see that 'osdemo0{1,2,4,5} are all running 'nova-compute.' When you start spinning up instances, they will allocate on any node that is running nova-compute from this list. - -You can then use `euca2ools` to test some items:: - - euca-describe-images - euca-describe-instances - -If you have issues with the API key, you may need to re-source your creds file:: - - . /root/creds/novarc - -If you don’t get any immediate errors, you’re successfully making calls to your cloud! - -Spinning up a VM for Testing -```````````````````````````` - -(This excerpt is from Thierry Carrez's blog, with reference to http://wiki.openstack.org/GettingImages.) - -The image that you will use here will be a ttylinux image, so this is a limited function server. You will be able to ping and SSH to this instance, but it is in no way a full production VM. - -UPDATE: Due to `bug 661159 `_, we can’t use images without ramdisks yet, so we can’t use the classic Ubuntu cloud images from http://uec-images.ubuntu.com/releases/ yet. For the sake of this tutorial, we’ll use the `ttylinux images from Scott Moser instead `_. - -Download the image, and publish to your bucket: - -:: - - image="ttylinux-uec-amd64-12.1_2.6.35-22_1.tar.gz" - wget http://smoser.brickies.net/ubuntu/ttylinux-uec/$image - uec-publish-tarball $image mybucket - -This will output three references, an "emi", an "eri" and an "eki." (Image, ramdisk, and kernel) The emi is the one we use to launch instances, so take note of this. - -Create a keypair to SSH to the server: - -:: - - euca-add-keypair mykey > mykey.priv - - chmod 0600 mykey.priv - -Boot your instance: - -:: - - euca-run-instances $emi -k mykey -t m1.tiny - -($emi is replaced with the output from the previous command) - -Checking status, and confirming communication: - -Once you have booted the instance, you can check the status the the `euca-describe-instances` command. Here you can view the instance ID, IP, and current status of the VM. - -:: - - euca-describe-instances - -Once in a "running" state, you can use your SSH key connect: - -:: - - ssh -i mykey.priv root@$ipaddress - -When you are ready to terminate the instance, you may do so with the `euca-terminate-instances` command: - -:: - - euca-terminate-instances $instance-id - -You can determine the instance-id with `euca-describe-instances`, and the format is "i-" with a series of letter and numbers following: e.g. i-a4g9d. - -For more information in creating you own custom (production ready) instance images, please visit http://wiki.openstack.org/GettingImages for more information! - -Enjoy your new private cloud, and play responsibly! diff --git a/doc/source/adminguide/network.flat.rst b/doc/source/adminguide/network.flat.rst deleted file mode 100644 index 3d8680c6f..000000000 --- a/doc/source/adminguide/network.flat.rst +++ /dev/null @@ -1,60 +0,0 @@ -.. - Copyright 2010-2011 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. - - -Flat Network Mode (Original and Flat) -===================================== - -Flat network mode removes most of the complexity of VLAN mode by simply -bridging all instance interfaces onto a single network. - -There are two variations of flat mode that differ mostly in how IP addresses -are given to instances. - - -Original Flat Mode ------------------- -IP addresses for VM instances are grabbed from a subnet specified by the network administrator, and injected into the image on launch. All instances of the system are attached to the same Linux networking bridge, configured manually by the network administrator both on the network controller hosting the network and on the computer controllers hosting the instances. To recap: - -* Each compute host creates a single bridge for all instances to use to attach to the external network. -* The networking configuration is injected into the instance before it is booted or it is obtained by a guest agent installed in the instance. - -Note that the configuration injection currently only works on linux-style systems that keep networking -configuration in /etc/network/interfaces. - - -Flat DHCP Mode --------------- -IP addresses for VM instances are grabbed from a subnet specified by the network administrator. Similar to the flat network, a single Linux networking bridge is created and configured manually by the network administrator and used for all instances. A DHCP server is started to pass out IP addresses to VM instances from the specified subnet. To recap: - -* Like flat mode, all instances are attached to a single bridge on the compute node. -* In addition a DHCP server is running to configure instances. - -Implementation --------------- - -The network nodes do not act as a default gateway in flat mode. Instances -are given public IP addresses. - -Compute nodes have iptables/ebtables entries created per project and -instance to protect against IP/MAC address spoofing and ARP poisoning. - - -Examples --------- - -.. todo:: add flat network mode configuration examples diff --git a/doc/source/adminguide/network.vlan.rst b/doc/source/adminguide/network.vlan.rst deleted file mode 100644 index c06ce8e8b..000000000 --- a/doc/source/adminguide/network.vlan.rst +++ /dev/null @@ -1,179 +0,0 @@ -.. - Copyright 2010-2011 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. - - -VLAN Network Mode -================= -VLAN Network Mode is the default mode for Nova. It provides a private network -segment for each project's instances that can be accessed via a dedicated -VPN connection from the Internet. - -In this mode, each project gets its own VLAN, Linux networking bridge, and subnet. The subnets are specified by the network administrator, and are assigned dynamically to a project when required. A DHCP Server is started for each VLAN to pass out IP addresses to VM instances from the subnet assigned to the project. All instances belonging to one project are bridged into the same VLAN for that project. The Linux networking bridges and VLANs are created by Nova when required, described in more detail in Nova VLAN Network Management Implementation. - -.. - (this text revised above) - Because the flat network and flat DhCP network are simple to understand and yet do not scale well enough for real-world cloud systems, this section focuses on the VLAN network implementation by the VLAN Network Manager. - - - In the VLAN network mode, all the VM instances of a project are connected together in a VLAN with the specified private subnet. Each running VM instance is assigned an IP address within the given private subnet. - -.. image:: /images/Novadiagram.png - :width: 790 - -While network traffic between VM instances belonging to the same VLAN is always open, Nova can enforce isolation of network traffic between different projects by enforcing one VLAN per project. - -In addition, the network administrator can specify a pool of public IP addresses that users may allocate and then assign to VMs, either at boot or dynamically at run-time. This capability is similar to Amazon's 'elastic IPs'. A public IP address may be associated with a running instances, allowing the VM instance to be accessed from the public network. The public IP addresses are accessible from the network host and NATed to the private IP address of the project. - -.. todo:: Describe how a public IP address could be associated with a project (a VLAN) - -This is the default networking mode and supports the most features. For multiple machine installation, it requires a switch that supports host-managed vlan tagging. In this mode, nova will create a vlan and bridge for each project. The project gets a range of private ips that are only accessible from inside the vlan. In order for a user to access the instances in their project, a special vpn instance (code named :ref:`cloudpipe `) needs to be created. Nova generates a certificate and key for the user to access the vpn and starts the vpn automatically. More information on cloudpipe can be found :ref:`here `. - -The following diagram illustrates how the communication that occurs between the vlan (the dashed box) and the public internet (represented by the two clouds) - -.. image:: /images/cloudpipe.png - :width: 100% - -Goals ------ - -For our implementation of Nova, our goal is that each project is in a protected network segment. Here are the specifications we keep in mind for meeting this goal. - - * RFC-1918 IP space - * public IP via NAT - * no default inbound Internet access without public NAT - * limited (project-admin controllable) outbound Internet access - * limited (project-admin controllable) access to other project segments - * all connectivity to instance and cloud API is via VPN into the project segment - -We also keep as a goal a common DMZ segment for support services, meaning these items are only visible from project segment: - - * metadata - * dashboard - -Limitations ------------ - -We kept in mind some of these limitations: - -* Projects / cluster limited to available VLANs in switching infrastructure -* Requires VPN for access to project segment - -Implementation --------------- -Currently Nova segregates project VLANs using 802.1q VLAN tagging in the -switching layer. Compute hosts create VLAN-specific interfaces and bridges -as required. - -The network nodes act as default gateway for project networks and contain -all of the routing and firewall rules implementing security groups. The -network node also handles DHCP to provide instance IPs for each project. - -VPN access is provided by running a small instance called CloudPipe -on the IP immediately following the gateway IP for each project. The -network node maps a dedicated public IP/port to the CloudPipe instance. - -Compute nodes have per-VLAN interfaces and bridges created as required. -These do NOT have IP addresses in the host to protect host access. -Compute nodes have iptables/ebtables entries created per project and -instance to protect against IP/MAC address spoofing and ARP poisoning. - -The network assignment to a project, and IP address assignment to a VM instance, are triggered when a user starts to run a VM instance. When running a VM instance, a user needs to specify a project for the instances, and the security groups (described in Security Groups) when the instance wants to join. If this is the first instance to be created for the project, then Nova (the cloud controller) needs to find a network controller to be the network host for the project; it then sets up a private network by finding an unused VLAN id, an unused subnet, and then the controller assigns them to the project, it also assigns a name to the project's Linux bridge (br100 stored in the Nova database), and allocating a private IP within the project's subnet for the new instance. - -If the instance the user wants to start is not the project's first, a subnet and a VLAN must have already been assigned to the project; therefore the system needs only to find an available IP address within the subnet and assign it to the new starting instance. If there is no private IP available within the subnet, an exception will be raised to the cloud controller, and the VM creation cannot proceed. - - -External Infrastructure ------------------------ - -Nova assumes the following is available: - -* DNS -* NTP -* Internet connectivity - - -Example -------- - -This example network configuration demonstrates most of the capabilities -of VLAN Mode. It splits administrative access to the nodes onto a dedicated -management network and uses dedicated network nodes to handle all -routing and gateway functions. - -It uses a 10GB network for instance traffic and a 1GB network for management. - - -Hardware -~~~~~~~~ - -* All nodes have a minimum of two NICs for management and production. - - * management is 1GB - * production is 10GB - * add additional NICs for bonding or HA/performance - -* network nodes should have an additional NIC dedicated to public Internet traffic -* switch needs to support enough simultaneous VLANs for number of projects -* production network configured as 802.1q trunk on switch - - -Operation -~~~~~~~~~ - -The network node controls the project network configuration: - -* assigns each project a VLAN and private IP range -* starts dnsmasq on project VLAN to serve private IP range -* configures iptables on network node for default project access -* launches CloudPipe instance and configures iptables access - -When starting an instance the network node: - -* sets up a VLAN interface and bridge on each host as required when an - instance is started on that host -* assigns private IP to instance -* generates MAC address for instance -* update dnsmasq with IP/MAC for instance - -When starting an instance the compute node: - -* sets up a VLAN interface and bridge on each host as required when an - instance is started on that host - - -Setup -~~~~~ - -* Assign VLANs in the switch: - - * public Internet segment - * production network - * management network - * cluster DMZ - -* Assign a contiguous range of VLANs to Nova for project use. -* Configure management NIC ports as management VLAN access ports. -* Configure management VLAN with Internet access as required -* Configure production NIC ports as 802.1q trunk ports. -* Configure Nova (need to add specifics here) - - * public IPs - * instance IPs - * project network size - * DMZ network - -.. todo:: need specific Nova configuration added diff --git a/doc/source/adminguide/nova.manage.rst b/doc/source/adminguide/nova.manage.rst deleted file mode 100644 index 0e9a29b6b..000000000 --- a/doc/source/adminguide/nova.manage.rst +++ /dev/null @@ -1,239 +0,0 @@ -.. - Copyright 2010-2011 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. - - -The nova-manage command -======================= - -Introduction -~~~~~~~~~~~~ - -The nova-manage command is used to perform many essential functions for -administration and ongoing maintenance of nova, such as user creation, -vpn management, and much more. - -The standard pattern for executing a nova-manage command is: -``nova-manage []`` - -For example, to obtain a list of all projects: -``nova-manage project list`` - -Run without arguments to see a list of available command categories: -``nova-manage`` - -Categories are user, project, role, shell, vpn, and floating. Detailed descriptions are below. - -You can also run with a category argument such as user to see a list of all commands in that category: -``nova-manage user`` - -These sections describe the available categories and arguments for nova-manage. - -Nova Db -~~~~~~~ - -``nova-manage db version`` - - Print the current database version. - -``nova-manage db sync`` - - Sync the database up to the most recent version. This is the standard way to create the db as well. - -Nova User -~~~~~~~~~ - -``nova-manage user admin `` - - Create an admin user with the name . - -``nova-manage user create `` - - Create a normal user with the name . - -``nova-manage user delete `` - - Delete the user with the name . - -``nova-manage user exports `` - - Outputs a list of access key and secret keys for user to the screen - -``nova-manage user list`` - - Outputs a list of all the user names to the screen. - -``nova-manage user modify `` - - Updates the indicated user keys, indicating with T or F if the user is an admin user. Leave any argument blank if you do not want to update it. - -Nova Project -~~~~~~~~~~~~ - -``nova-manage project add `` - - Add a nova project with the name to the database. - -``nova-manage project create `` - - Create a new nova project with the name (you still need to do nova-manage project add to add it to the database). - -``nova-manage project delete `` - - Delete a nova project with the name . - -``nova-manage project environment `` - - Exports environment variables for the named project to a file named novarc. - -``nova-manage project list`` - - Outputs a list of all the projects to the screen. - -``nova-manage project quota `` - - Outputs the size and specs of the project's instances including gigabytes, instances, floating IPs, volumes, and cores. - -``nova-manage project remove `` - - Deletes the project with the name . - -``nova-manage project zipfile`` - - Compresses all related files for a created project into a zip file nova.zip. - -Nova Role -~~~~~~~~~ - -nova-manage role [] -``nova-manage role add <(optional) projectname>`` - - Add a user to either a global or project-based role with the indicated assigned to the named user. Role names can be one of the following five roles: admin, itsec, projectmanager, netadmin, developer. If you add the project name as the last argument then the role is assigned just for that project, otherwise the user is assigned the named role for all projects. - -``nova-manage role has `` - Checks the user or project and responds with True if the user has a global role with a particular project. - -``nova-manage role remove `` - Remove the indicated role from the user. - -Nova Shell -~~~~~~~~~~ - -``nova-manage shell bpython`` - - Starts a new bpython shell. - -``nova-manage shell ipython`` - - Starts a new ipython shell. - -``nova-manage shell python`` - - Starts a new python shell. - -``nova-manage shell run`` - - Starts a new shell using python. - -``nova-manage shell script `` - - Runs the named script from the specified path with flags set. - -Nova VPN -~~~~~~~~ - -``nova-manage vpn list`` - - Displays a list of projects, their IP prot numbers, and what state they're in. - -``nova-manage vpn run `` - - Starts the VPN for the named project. - -``nova-manage vpn spawn`` - - Runs all VPNs. - -Nova Floating IPs -~~~~~~~~~~~~~~~~~ - -``nova-manage floating create `` - - Creates floating IP addresses for the named host by the given range. - -``nova-manage floating delete `` - - Deletes floating IP addresses in the range given. - -``nova-manage floating list`` - - Displays a list of all floating IP addresses. - -Concept: Flags --------------- - -python-gflags - - -Concept: Plugins ----------------- - -* Managers/Drivers: utils.import_object from string flag -* virt/connections: conditional loading from string flag -* db: LazyPluggable via string flag -* auth_manager: utils.import_class based on string flag -* Volumes: moving to pluggable driver instead of manager -* Network: pluggable managers -* Compute: same driver used, but pluggable at connection - - -Concept: IPC/RPC ----------------- - -Rabbit! - - -Concept: Fakes --------------- - -* auth -* ldap - - -Concept: Scheduler ------------------- - -* simple -* random - - -Concept: Security Groups ------------------------- - -Security groups - - -Concept: Certificate Authority ------------------------------- - -Nova does a small amount of certificate management. These certificates are used for :ref:`project vpns <../cloudpipe>` and decrypting bundled images. - - -Concept: Images ---------------- - -* launching -* bundling diff --git a/doc/source/adminguide/single.node.install.rst b/doc/source/adminguide/single.node.install.rst deleted file mode 100644 index ff43aa90b..000000000 --- a/doc/source/adminguide/single.node.install.rst +++ /dev/null @@ -1,362 +0,0 @@ -Installing Nova on a Single Host -================================ - -Nova can be run on a single machine, and it is recommended that new users practice managing this type of installation before graduating to multi node systems. - -The fastest way to get a test cloud running is through our :doc:`../quickstart`. But for more detail on installing the system read this doc. - - -Step 1 and 2: Get the latest Nova code system software ------------------------------------------------------- - -Depending on your system, the method for accomplishing this varies - -.. toctree:: - :maxdepth: 1 - - distros/ubuntu.10.04 - distros/ubuntu.10.10 - distros/others - - -Step 3: Build and install Nova services ---------------------------------------- - -Switch to the base nova source directory. - -Then type or copy/paste in the following line to compile the Python code for OpenStack Compute. - -:: - - sudo python setup.py build - sudo python setup.py install - - -When the installation is complete, you'll see the following lines: - -:: - - Installing nova-network script to /usr/local/bin - Installing nova-volume script to /usr/local/bin - Installing nova-objectstore script to /usr/local/bin - Installing nova-manage script to /usr/local/bin - Installing nova-scheduler script to /usr/local/bin - Installing nova-dhcpbridge script to /usr/local/bin - Installing nova-compute script to /usr/local/bin - Installing nova-instancemonitor script to /usr/local/bin - Installing nova-api script to /usr/local/bin - Installing nova-import-canonical-imagestore script to /usr/local/bin - - Installed /usr/local/lib/python2.6/dist-packages/nova-2010.1-py2.6.egg - Processing dependencies for nova==2010.1 - Finished processing dependencies for nova==2010.1 - - -Step 4: Create the Nova Database --------------------------------- -Type or copy/paste in the following line to create your nova db:: - - sudo nova-manage db sync - -Step 5: Create a Nova administrator ------------------------------------ -Type or copy/paste in the following line to create a user named "anne.":: - - sudo nova-manage user admin anne - -You see an access key and a secret key export, such as these made-up ones::: - - export EC2_ACCESS_KEY=4e6498a2-blah-blah-blah-17d1333t97fd - export EC2_SECRET_KEY=0a520304-blah-blah-blah-340sp34k05bbe9a7 - -Step 6: Create the network --------------------------- - -Type or copy/paste in the following line to create a network prior to creating a project. - -:: - - sudo nova-manage network create 10.0.0.0/8 1 64 - -For this command, the IP address is the cidr notation for your netmask, such as 192.168.1.0/24. The value 1 is the total number of networks you want made, and the 64 value is the total number of ips in all networks. - -After running this command, entries are made in the 'networks' and 'fixed_ips' table in the database. - -Step 7: Create a project with the user you created --------------------------------------------------- -Type or copy/paste in the following line to create a project named IRT (for Ice Road Truckers, of course) with the newly-created user named anne. - -:: - - sudo nova-manage project create IRT anne - -:: - - Generating RSA private key, 1024 bit long modulus - .....++++++ - ..++++++ - e is 65537 (0x10001) - Using configuration from ./openssl.cnf - Check that the request matches the signature - Signature ok - The Subject's Distinguished Name is as follows - countryName :PRINTABLE:'US' - stateOrProvinceName :PRINTABLE:'California' - localityName :PRINTABLE:'MountainView' - organizationName :PRINTABLE:'AnsoLabs' - organizationalUnitName:PRINTABLE:'NovaDev' - commonName :PRINTABLE:'anne-2010-10-12T21:12:35Z' - Certificate is to be certified until Oct 12 21:12:35 2011 GMT (365 days) - - Write out database with 1 new entries - Data Base Updated - - -Step 8: Unzip the nova.zip --------------------------- - -You should have a nova.zip file in your current working directory. Unzip it with this command: - -:: - - unzip nova.zip - - -You'll see these files extract. - -:: - - Archive: nova.zip - extracting: novarc - extracting: pk.pem - extracting: cert.pem - extracting: nova-vpn.conf - extracting: cacert.pem - - -Step 9: Source the rc file --------------------------- -Type or copy/paste the following to source the novarc file in your current working directory. - -:: - - . novarc - - -Step 10: Pat yourself on the back :) ------------------------------------ -Congratulations, your cloud is up and running, you’ve created an admin user, created a network, retrieved the user's credentials and put them in your environment. - -Now you need an image. - - -Step 11: Get an image --------------------- -To make things easier, we've provided a small image on the Rackspace CDN. Use this command to get it on your server. - -:: - - wget http://c2477062.cdn.cloudfiles.rackspacecloud.com/images.tgz - - -:: - - --2010-10-12 21:40:55-- http://c2477062.cdn.cloudfiles.rackspacecloud.com/images.tgz - Resolving cblah2.cdn.cloudfiles.rackspacecloud.com... 208.111.196.6, 208.111.196.7 - Connecting to cblah2.cdn.cloudfiles.rackspacecloud.com|208.111.196.6|:80... connected. - HTTP request sent, awaiting response... 200 OK - Length: 58520278 (56M) [application/x-gzip] - Saving to: `images.tgz' - - 100%[======================================>] 58,520,278 14.1M/s in 3.9s - - 2010-10-12 21:40:59 (14.1 MB/s) - `images.tgz' saved [58520278/58520278] - - - -Step 12: Decompress the image file ----------------------------------- -Use this command to extract the image files::: - - tar xvzf images.tgz - -You get a directory listing like so::: - - images - |-- aki-lucid - | |-- image - | `-- info.json - |-- ami-tiny - | |-- image - | `-- info.json - `-- ari-lucid - |-- image - `-- info.json - -Step 13: Send commands to upload sample image to the cloud ----------------------------------------------------------- - -Type or copy/paste the following commands to create a manifest for the kernel.:: - - euca-bundle-image -i images/aki-lucid/image -p kernel --kernel true - -You should see this in response::: - - Checking image - Tarring image - Encrypting image - Splitting image... - Part: kernel.part.0 - Generating manifest /tmp/kernel.manifest.xml - -Type or copy/paste the following commands to create a manifest for the ramdisk.:: - - euca-bundle-image -i images/ari-lucid/image -p ramdisk --ramdisk true - -You should see this in response::: - - Checking image - Tarring image - Encrypting image - Splitting image... - Part: ramdisk.part.0 - Generating manifest /tmp/ramdisk.manifest.xml - -Type or copy/paste the following commands to upload the kernel bundle.:: - - euca-upload-bundle -m /tmp/kernel.manifest.xml -b mybucket - -You should see this in response::: - - Checking bucket: mybucket - Creating bucket: mybucket - Uploading manifest file - Uploading part: kernel.part.0 - Uploaded image as mybucket/kernel.manifest.xml - -Type or copy/paste the following commands to upload the ramdisk bundle.:: - - euca-upload-bundle -m /tmp/ramdisk.manifest.xml -b mybucket - -You should see this in response::: - - Checking bucket: mybucket - Uploading manifest file - Uploading part: ramdisk.part.0 - Uploaded image as mybucket/ramdisk.manifest.xml - -Type or copy/paste the following commands to register the kernel and get its ID.:: - - euca-register mybucket/kernel.manifest.xml - -You should see this in response::: - - IMAGE ami-fcbj2non - -Type or copy/paste the following commands to register the ramdisk and get its ID.:: - - euca-register mybucket/ramdisk.manifest.xml - -You should see this in response::: - - IMAGE ami-orukptrc - -Type or copy/paste the following commands to create a manifest for the machine image associated with the ramdisk and kernel IDs that you got from the previous commands.:: - - euca-bundle-image -i images/ami-tiny/image -p machine --kernel ami-fcbj2non --ramdisk ami-orukptrc - -You should see this in response::: - - Checking image - Tarring image - Encrypting image - Splitting image... - Part: machine.part.0 - Part: machine.part.1 - Part: machine.part.2 - Part: machine.part.3 - Part: machine.part.4 - Generating manifest /tmp/machine.manifest.xml - -Type or copy/paste the following commands to upload the machine image bundle.:: - - euca-upload-bundle -m /tmp/machine.manifest.xml -b mybucket - -You should see this in response::: - - Checking bucket: mybucket - Uploading manifest file - Uploading part: machine.part.0 - Uploading part: machine.part.1 - Uploading part: machine.part.2 - Uploading part: machine.part.3 - Uploading part: machine.part.4 - Uploaded image as mybucket/machine.manifest.xml - -Type or copy/paste the following commands to register the machine image and get its ID.:: - - euca-register mybucket/machine.manifest.xml - -You should see this in response::: - - IMAGE ami-g06qbntt - -Type or copy/paste the following commands to register a SSH keypair for use in starting and accessing the instances.:: - - euca-add-keypair mykey > mykey.priv - chmod 600 mykey.priv - -Type or copy/paste the following commands to run an instance using the keypair and IDs that we previously created.:: - - euca-run-instances ami-g06qbntt --kernel ami-fcbj2non --ramdisk ami-orukptrc -k mykey - -You should see this in response::: - - RESERVATION r-0at28z12 IRT - INSTANCE i-1b0bh8n ami-g06qbntt 10.0.0.3 10.0.0.3 scheduling mykey (IRT, None) m1.small 2010-10-18 19:02:10.443599 - -Type or copy/paste the following commands to watch as the scheduler launches, and completes booting your instance.:: - - euca-describe-instances - -You should see this in response::: - - RESERVATION r-0at28z12 IRT - INSTANCE i-1b0bh8n ami-g06qbntt 10.0.0.3 10.0.0.3 launching mykey (IRT, cloud02) m1.small 2010-10-18 19:02:10.443599 - -Type or copy/paste the following commands to see when loading is completed and the instance is running.:: - - euca-describe-instances - -You should see this in response::: - - RESERVATION r-0at28z12 IRT - INSTANCE i-1b0bh8n ami-g06qbntt 10.0.0.3 10.0.0.3 running mykey (IRT, cloud02) 0 m1.small 2010-10-18 19:02:10.443599 - -Type or copy/paste the following commands to check that the virtual machine is running.:: - - virsh list - -You should see this in response::: - - Id Name State - ---------------------------------- - 1 2842445831 running - -Type or copy/paste the following commands to ssh to the instance using your private key.:: - - ssh -i mykey.priv root@10.0.0.3 - - -Troubleshooting Installation ----------------------------- - -If you see an "error loading the config file './openssl.cnf'" it means you can copy the openssl.cnf file to the location where Nova expects it and reboot, then try the command again. - -:: - - cp /etc/ssl/openssl.cnf ~ - sudo reboot - - - diff --git a/doc/source/community.rst b/doc/source/community.rst index 4ae32f1eb..e925a47bd 100644 --- a/doc/source/community.rst +++ b/doc/source/community.rst @@ -18,7 +18,7 @@ Getting Involved ================ -The Nova community is a very friendly group and there are places online to join in with the +The OpenStack community for Nova is a very friendly group and there are places online to join in with the community. Feel free to ask questions. This document points you to some of the places where you can communicate with people. @@ -83,3 +83,13 @@ Twitter Because all the cool kids do it: `@openstack `_. Also follow the `#openstack `_ tag for relevant tweets. + +OpenStack Docs Site +------------------- + +The `nova.openstack.org `_ site is geared towards developer documentation, +and the `docs.openstack.org `_ site is intended for cloud administrators +who are standing up and running OpenStack Compute in production. You can contribute to the Docs Site +by using bzr and Launchpad and contributing to the openstack-manuals project at http://launchpad.net/openstack-manuals. + + diff --git a/doc/source/index.rst b/doc/source/index.rst index d337fb69f..846d3cfcd 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -32,11 +32,13 @@ Nova is written with the following design guidelines in mind: * **API Compatibility**: Nova strives to provide API-compatible with popular systems like Amazon EC2 This documentation is generated by the Sphinx toolkit and lives in the source -tree. Additional documentation on Nova and other components of OpenStack can -be found on the `OpenStack wiki`_. Also see the :doc:`community` page for -other ways to interact with the community. +tree. Additional draft and project documentation on Nova and other components of OpenStack can +be found on the `OpenStack wiki`_. Cloud administrators, refer to `docs.openstack.org`_. + +Also see the :doc:`community` page for other ways to interact with the community. .. _`OpenStack wiki`: http://wiki.openstack.org +.. _`docs.openstack.org`: http://docs.openstack.org Key Concepts @@ -50,17 +52,7 @@ Key Concepts service.architecture nova.object.model swift.object.model - -Administrator's Documentation -============================= - -.. toctree:: - :maxdepth: 1 - - livecd - adminguide/index - adminguide/single.node.install - adminguide/multi.node.install + runnova/index Developer Docs ============== diff --git a/doc/source/object.model.rst b/doc/source/object.model.rst index d02f151fd..419e89b0c 100644 --- a/doc/source/object.model.rst +++ b/doc/source/object.model.rst @@ -18,8 +18,6 @@ Object Model ============ -.. todo:: Add brief description for core models - .. graphviz:: digraph foo { @@ -42,27 +40,27 @@ Object Model Users ----- -Each Nova User is authorized based on their access key and secret key, assigned per-user. Read more at :doc:`/adminguide/managing.users`. +Each Nova User is authorized based on their access key and secret key, assigned per-user. Read more at :doc:`/runnova/managing.users`. Projects -------- -For Nova, access to images is based on the project. Read more at :doc:`/adminguide/managing.projects`. +For Nova, access to images is based on the project. Read more at :doc:`/runnova/managing.projects`. Images ------ -Images are binary files that run the operating system. Read more at :doc:`/adminguide/managing.images`. +Images are binary files that run the operating system. Read more at :doc:`/runnova/managing.images`. Instances --------- -Instances are running virtual servers. Read more at :doc:`/adminguide/managing.instances`. +Instances are running virtual servers. Read more at :doc:`/runnova/managing.instances`. Volumes ------- -.. todo:: Write doc about volumes +Volumes offer extra block level storage to instances. Read more at `Managing Volumes `_. Security Groups --------------- @@ -72,7 +70,7 @@ In Nova, a security group is a named collection of network access rules, like fi VLANs ----- -VLAN is the default network mode for Nova. Read more at :doc:`/adminguide/network.vlan`. +VLAN is the default network mode for Nova. Read more at :doc:`/runnova/network.vlan`. IP Addresses ------------ diff --git a/doc/source/quickstart.rst b/doc/source/quickstart.rst index 17c9e10a8..84ed3fe01 100644 --- a/doc/source/quickstart.rst +++ b/doc/source/quickstart.rst @@ -54,7 +54,7 @@ Environment Variables By tweaking the environment that nova.sh run in, you can build slightly different configurations (though for more complex setups you should see -:doc:`/adminguide/getting.started` and :doc:`/adminguide/multi.node.install`). +`Installing and Configuring OpenStack Compute `_). * HOST_IP * Default: address of first interface from the ifconfig command -- cgit From df1213b8091c9a63a25b15c6eb82272255f96cb6 Mon Sep 17 00:00:00 2001 From: Anne Gentle Date: Mon, 21 Feb 2011 14:30:20 -0600 Subject: Updated to remove built docs --- doc/.autogenerated | 406 ++++++ doc/build/.DS_Store | Bin 0 -> 6148 bytes doc/build/html/.DS_Store | Bin 0 -> 6148 bytes doc/build/html/.buildinfo | 4 + doc/source/api/autoindex.rst | 138 ++ doc/source/api/nova..adminclient.rst | 6 + doc/source/api/nova..api.direct.rst | 6 + doc/source/api/nova..api.ec2.admin.rst | 6 + doc/source/api/nova..api.ec2.apirequest.rst | 6 + doc/source/api/nova..api.ec2.cloud.rst | 6 + .../api/nova..api.ec2.metadatarequesthandler.rst | 6 + doc/source/api/nova..api.openstack.auth.rst | 6 + .../api/nova..api.openstack.backup_schedules.rst | 6 + doc/source/api/nova..api.openstack.common.rst | 6 + doc/source/api/nova..api.openstack.consoles.rst | 6 + doc/source/api/nova..api.openstack.faults.rst | 6 + doc/source/api/nova..api.openstack.flavors.rst | 6 + doc/source/api/nova..api.openstack.images.rst | 6 + doc/source/api/nova..api.openstack.servers.rst | 6 + .../api/nova..api.openstack.shared_ip_groups.rst | 6 + doc/source/api/nova..api.openstack.zones.rst | 6 + doc/source/api/nova..auth.dbdriver.rst | 6 + doc/source/api/nova..auth.fakeldap.rst | 6 + doc/source/api/nova..auth.ldapdriver.rst | 6 + doc/source/api/nova..auth.manager.rst | 6 + doc/source/api/nova..auth.signer.rst | 6 + doc/source/api/nova..cloudpipe.pipelib.rst | 6 + doc/source/api/nova..compute.api.rst | 6 + doc/source/api/nova..compute.instance_types.rst | 6 + doc/source/api/nova..compute.manager.rst | 6 + doc/source/api/nova..compute.monitor.rst | 6 + doc/source/api/nova..compute.power_state.rst | 6 + doc/source/api/nova..console.api.rst | 6 + doc/source/api/nova..console.fake.rst | 6 + doc/source/api/nova..console.manager.rst | 6 + doc/source/api/nova..console.xvp.rst | 6 + doc/source/api/nova..context.rst | 6 + doc/source/api/nova..crypto.rst | 6 + doc/source/api/nova..db.api.rst | 6 + doc/source/api/nova..db.base.rst | 6 + doc/source/api/nova..db.migration.rst | 6 + doc/source/api/nova..db.sqlalchemy.api.rst | 6 + .../nova..db.sqlalchemy.migrate_repo.manage.rst | 6 + ...sqlalchemy.migrate_repo.versions.001_austin.rst | 6 + ....sqlalchemy.migrate_repo.versions.002_bexar.rst | 6 + ...ate_repo.versions.003_add_label_to_networks.rst | 6 + ...y.migrate_repo.versions.004_add_zone_tables.rst | 6 + doc/source/api/nova..db.sqlalchemy.migration.rst | 6 + doc/source/api/nova..db.sqlalchemy.models.rst | 6 + doc/source/api/nova..db.sqlalchemy.session.rst | 6 + doc/source/api/nova..exception.rst | 6 + doc/source/api/nova..fakememcache.rst | 6 + doc/source/api/nova..fakerabbit.rst | 6 + doc/source/api/nova..flags.rst | 6 + doc/source/api/nova..image.glance.rst | 6 + doc/source/api/nova..image.local.rst | 6 + doc/source/api/nova..image.s3.rst | 6 + doc/source/api/nova..image.service.rst | 6 + doc/source/api/nova..log.rst | 6 + doc/source/api/nova..manager.rst | 6 + doc/source/api/nova..network.api.rst | 6 + doc/source/api/nova..network.linux_net.rst | 6 + doc/source/api/nova..network.manager.rst | 6 + doc/source/api/nova..objectstore.bucket.rst | 6 + doc/source/api/nova..objectstore.handler.rst | 6 + doc/source/api/nova..objectstore.image.rst | 6 + doc/source/api/nova..objectstore.stored.rst | 6 + doc/source/api/nova..quota.rst | 6 + doc/source/api/nova..rpc.rst | 6 + doc/source/api/nova..scheduler.chance.rst | 6 + doc/source/api/nova..scheduler.driver.rst | 6 + doc/source/api/nova..scheduler.manager.rst | 6 + doc/source/api/nova..scheduler.simple.rst | 6 + doc/source/api/nova..scheduler.zone.rst | 6 + doc/source/api/nova..service.rst | 6 + doc/source/api/nova..test.rst | 6 + doc/source/api/nova..tests.api.openstack.fakes.rst | 6 + .../nova..tests.api.openstack.test_adminapi.rst | 6 + .../api/nova..tests.api.openstack.test_api.rst | 6 + .../api/nova..tests.api.openstack.test_auth.rst | 6 + .../api/nova..tests.api.openstack.test_common.rst | 6 + .../api/nova..tests.api.openstack.test_faults.rst | 6 + .../api/nova..tests.api.openstack.test_flavors.rst | 6 + .../api/nova..tests.api.openstack.test_images.rst | 6 + ...nova..tests.api.openstack.test_ratelimiting.rst | 6 + .../api/nova..tests.api.openstack.test_servers.rst | 6 + .....tests.api.openstack.test_shared_ip_groups.rst | 6 + .../api/nova..tests.api.openstack.test_zones.rst | 6 + doc/source/api/nova..tests.api.test_wsgi.rst | 6 + doc/source/api/nova..tests.db.fakes.rst | 6 + doc/source/api/nova..tests.declare_flags.rst | 6 + doc/source/api/nova..tests.fake_flags.rst | 6 + doc/source/api/nova..tests.glance.stubs.rst | 6 + doc/source/api/nova..tests.hyperv_unittest.rst | 6 + .../api/nova..tests.objectstore_unittest.rst | 6 + doc/source/api/nova..tests.real_flags.rst | 6 + doc/source/api/nova..tests.runtime_flags.rst | 6 + doc/source/api/nova..tests.test_access.rst | 6 + doc/source/api/nova..tests.test_api.rst | 6 + doc/source/api/nova..tests.test_auth.rst | 6 + doc/source/api/nova..tests.test_cloud.rst | 6 + doc/source/api/nova..tests.test_compute.rst | 6 + doc/source/api/nova..tests.test_console.rst | 6 + doc/source/api/nova..tests.test_direct.rst | 6 + doc/source/api/nova..tests.test_flags.rst | 6 + doc/source/api/nova..tests.test_localization.rst | 6 + doc/source/api/nova..tests.test_log.rst | 6 + doc/source/api/nova..tests.test_middleware.rst | 6 + doc/source/api/nova..tests.test_misc.rst | 6 + doc/source/api/nova..tests.test_network.rst | 6 + doc/source/api/nova..tests.test_quota.rst | 6 + doc/source/api/nova..tests.test_rpc.rst | 6 + doc/source/api/nova..tests.test_scheduler.rst | 6 + doc/source/api/nova..tests.test_service.rst | 6 + doc/source/api/nova..tests.test_twistd.rst | 6 + doc/source/api/nova..tests.test_virt.rst | 6 + doc/source/api/nova..tests.test_volume.rst | 6 + doc/source/api/nova..tests.test_xenapi.rst | 6 + doc/source/api/nova..tests.xenapi.stubs.rst | 6 + doc/source/api/nova..twistd.rst | 6 + doc/source/api/nova..utils.rst | 6 + doc/source/api/nova..version.rst | 6 + doc/source/api/nova..virt.connection.rst | 6 + doc/source/api/nova..virt.disk.rst | 6 + doc/source/api/nova..virt.fake.rst | 6 + doc/source/api/nova..virt.hyperv.rst | 6 + doc/source/api/nova..virt.images.rst | 6 + doc/source/api/nova..virt.libvirt_conn.rst | 6 + doc/source/api/nova..virt.xenapi.fake.rst | 6 + doc/source/api/nova..virt.xenapi.network_utils.rst | 6 + doc/source/api/nova..virt.xenapi.vm_utils.rst | 6 + doc/source/api/nova..virt.xenapi.vmops.rst | 6 + doc/source/api/nova..virt.xenapi.volume_utils.rst | 6 + doc/source/api/nova..virt.xenapi.volumeops.rst | 6 + doc/source/api/nova..virt.xenapi_conn.rst | 6 + doc/source/api/nova..volume.api.rst | 6 + doc/source/api/nova..volume.driver.rst | 6 + doc/source/api/nova..volume.manager.rst | 6 + doc/source/api/nova..volume.san.rst | 6 + doc/source/api/nova..wsgi.rst | 6 + doc/source/runnova/binaries.rst | 57 + doc/source/runnova/euca2ools.rst | 49 + doc/source/runnova/flags.rst | 193 +++ doc/source/runnova/getting.started.rst | 168 +++ doc/source/runnova/index.rst | 90 ++ doc/source/runnova/managing.images.rst | 21 + doc/source/runnova/managing.instances.rst | 59 + doc/source/runnova/managing.networks.rst | 70 + doc/source/runnova/managing.projects.rst | 68 + doc/source/runnova/managing.users.rst | 82 ++ doc/source/runnova/managingsecurity.rst | 39 + doc/source/runnova/monitoring.rst | 27 + doc/source/runnova/network.flat.rst | 60 + doc/source/runnova/network.vlan.rst | 179 +++ doc/source/runnova/nova.manage.rst | 239 ++++ test/.Python | 1 + test/bin/activate | 76 ++ test/bin/activate.csh | 32 + test/bin/activate.fish | 79 ++ test/bin/activate_this.py | 32 + test/bin/easy_install | 9 + test/bin/easy_install-2.6 | 9 + test/bin/pip | 9 + test/bin/pip-2.6 | 9 + test/bin/python | Bin 0 -> 50720 bytes test/bin/python2.6 | 1 + test/include/python2.6 | 1 + test/lib/python2.6/UserDict.py | 1 + test/lib/python2.6/_abcoll.py | 1 + test/lib/python2.6/abc.py | 1 + test/lib/python2.6/codecs.py | 1 + test/lib/python2.6/config | 1 + test/lib/python2.6/copy_reg.py | 1 + test/lib/python2.6/distutils/__init__.py | 91 ++ test/lib/python2.6/distutils/distutils.cfg | 6 + test/lib/python2.6/encodings | 1 + test/lib/python2.6/fnmatch.py | 1 + test/lib/python2.6/genericpath.py | 1 + test/lib/python2.6/lib-dynload | 1 + test/lib/python2.6/linecache.py | 1 + test/lib/python2.6/locale.py | 1 + test/lib/python2.6/ntpath.py | 1 + test/lib/python2.6/orig-prefix.txt | 1 + test/lib/python2.6/os.py | 1 + test/lib/python2.6/posixpath.py | 1 + test/lib/python2.6/re.py | 1 + test/lib/python2.6/site-packages/easy-install.pth | 4 + .../pip-0.8.1-py2.6.egg/EGG-INFO/PKG-INFO | 348 +++++ .../pip-0.8.1-py2.6.egg/EGG-INFO/SOURCES.txt | 57 + .../EGG-INFO/dependency_links.txt | 1 + .../pip-0.8.1-py2.6.egg/EGG-INFO/entry_points.txt | 4 + .../pip-0.8.1-py2.6.egg/EGG-INFO/not-zip-safe | 1 + .../pip-0.8.1-py2.6.egg/EGG-INFO/top_level.txt | 1 + .../pip-0.8.1-py2.6.egg/pip/__init__.py | 261 ++++ .../pip-0.8.1-py2.6.egg/pip/_pkgutil.py | 589 ++++++++ .../pip-0.8.1-py2.6.egg/pip/backwardcompat.py | 55 + .../pip-0.8.1-py2.6.egg/pip/basecommand.py | 203 +++ .../pip-0.8.1-py2.6.egg/pip/baseparser.py | 231 ++++ .../pip-0.8.1-py2.6.egg/pip/commands/__init__.py | 1 + .../pip-0.8.1-py2.6.egg/pip/commands/bundle.py | 33 + .../pip-0.8.1-py2.6.egg/pip/commands/completion.py | 60 + .../pip-0.8.1-py2.6.egg/pip/commands/freeze.py | 109 ++ .../pip-0.8.1-py2.6.egg/pip/commands/help.py | 32 + .../pip-0.8.1-py2.6.egg/pip/commands/install.py | 247 ++++ .../pip-0.8.1-py2.6.egg/pip/commands/search.py | 116 ++ .../pip-0.8.1-py2.6.egg/pip/commands/uninstall.py | 42 + .../pip-0.8.1-py2.6.egg/pip/commands/unzip.py | 9 + .../pip-0.8.1-py2.6.egg/pip/commands/zip.py | 346 +++++ .../pip-0.8.1-py2.6.egg/pip/download.py | 470 +++++++ .../pip-0.8.1-py2.6.egg/pip/exceptions.py | 17 + .../site-packages/pip-0.8.1-py2.6.egg/pip/index.py | 686 ++++++++++ .../pip-0.8.1-py2.6.egg/pip/locations.py | 45 + .../site-packages/pip-0.8.1-py2.6.egg/pip/log.py | 181 +++ .../site-packages/pip-0.8.1-py2.6.egg/pip/req.py | 1432 ++++++++++++++++++++ .../pip-0.8.1-py2.6.egg/pip/runner.py | 18 + .../site-packages/pip-0.8.1-py2.6.egg/pip/util.py | 479 +++++++ .../pip-0.8.1-py2.6.egg/pip/vcs/__init__.py | 238 ++++ .../pip-0.8.1-py2.6.egg/pip/vcs/bazaar.py | 138 ++ .../pip-0.8.1-py2.6.egg/pip/vcs/git.py | 204 +++ .../pip-0.8.1-py2.6.egg/pip/vcs/mercurial.py | 162 +++ .../pip-0.8.1-py2.6.egg/pip/vcs/subversion.py | 260 ++++ .../site-packages/pip-0.8.1-py2.6.egg/pip/venv.py | 53 + .../site-packages/setuptools-0.6c11-py2.6.egg | Bin 0 -> 333447 bytes test/lib/python2.6/site-packages/setuptools.pth | 1 + test/lib/python2.6/site.py | 713 ++++++++++ test/lib/python2.6/sre.py | 1 + test/lib/python2.6/sre_compile.py | 1 + test/lib/python2.6/sre_constants.py | 1 + test/lib/python2.6/sre_parse.py | 1 + test/lib/python2.6/stat.py | 1 + test/lib/python2.6/types.py | 1 + test/lib/python2.6/warnings.py | 1 + 232 files changed, 10985 insertions(+) create mode 100644 doc/.autogenerated create mode 100644 doc/build/.DS_Store create mode 100644 doc/build/html/.DS_Store create mode 100644 doc/build/html/.buildinfo create mode 100644 doc/source/api/autoindex.rst create mode 100644 doc/source/api/nova..adminclient.rst create mode 100644 doc/source/api/nova..api.direct.rst create mode 100644 doc/source/api/nova..api.ec2.admin.rst create mode 100644 doc/source/api/nova..api.ec2.apirequest.rst create mode 100644 doc/source/api/nova..api.ec2.cloud.rst create mode 100644 doc/source/api/nova..api.ec2.metadatarequesthandler.rst create mode 100644 doc/source/api/nova..api.openstack.auth.rst create mode 100644 doc/source/api/nova..api.openstack.backup_schedules.rst create mode 100644 doc/source/api/nova..api.openstack.common.rst create mode 100644 doc/source/api/nova..api.openstack.consoles.rst create mode 100644 doc/source/api/nova..api.openstack.faults.rst create mode 100644 doc/source/api/nova..api.openstack.flavors.rst create mode 100644 doc/source/api/nova..api.openstack.images.rst create mode 100644 doc/source/api/nova..api.openstack.servers.rst create mode 100644 doc/source/api/nova..api.openstack.shared_ip_groups.rst create mode 100644 doc/source/api/nova..api.openstack.zones.rst create mode 100644 doc/source/api/nova..auth.dbdriver.rst create mode 100644 doc/source/api/nova..auth.fakeldap.rst create mode 100644 doc/source/api/nova..auth.ldapdriver.rst create mode 100644 doc/source/api/nova..auth.manager.rst create mode 100644 doc/source/api/nova..auth.signer.rst create mode 100644 doc/source/api/nova..cloudpipe.pipelib.rst create mode 100644 doc/source/api/nova..compute.api.rst create mode 100644 doc/source/api/nova..compute.instance_types.rst create mode 100644 doc/source/api/nova..compute.manager.rst create mode 100644 doc/source/api/nova..compute.monitor.rst create mode 100644 doc/source/api/nova..compute.power_state.rst create mode 100644 doc/source/api/nova..console.api.rst create mode 100644 doc/source/api/nova..console.fake.rst create mode 100644 doc/source/api/nova..console.manager.rst create mode 100644 doc/source/api/nova..console.xvp.rst create mode 100644 doc/source/api/nova..context.rst create mode 100644 doc/source/api/nova..crypto.rst create mode 100644 doc/source/api/nova..db.api.rst create mode 100644 doc/source/api/nova..db.base.rst create mode 100644 doc/source/api/nova..db.migration.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.api.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.migrate_repo.manage.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.migration.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.models.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.session.rst create mode 100644 doc/source/api/nova..exception.rst create mode 100644 doc/source/api/nova..fakememcache.rst create mode 100644 doc/source/api/nova..fakerabbit.rst create mode 100644 doc/source/api/nova..flags.rst create mode 100644 doc/source/api/nova..image.glance.rst create mode 100644 doc/source/api/nova..image.local.rst create mode 100644 doc/source/api/nova..image.s3.rst create mode 100644 doc/source/api/nova..image.service.rst create mode 100644 doc/source/api/nova..log.rst create mode 100644 doc/source/api/nova..manager.rst create mode 100644 doc/source/api/nova..network.api.rst create mode 100644 doc/source/api/nova..network.linux_net.rst create mode 100644 doc/source/api/nova..network.manager.rst create mode 100644 doc/source/api/nova..objectstore.bucket.rst create mode 100644 doc/source/api/nova..objectstore.handler.rst create mode 100644 doc/source/api/nova..objectstore.image.rst create mode 100644 doc/source/api/nova..objectstore.stored.rst create mode 100644 doc/source/api/nova..quota.rst create mode 100644 doc/source/api/nova..rpc.rst create mode 100644 doc/source/api/nova..scheduler.chance.rst create mode 100644 doc/source/api/nova..scheduler.driver.rst create mode 100644 doc/source/api/nova..scheduler.manager.rst create mode 100644 doc/source/api/nova..scheduler.simple.rst create mode 100644 doc/source/api/nova..scheduler.zone.rst create mode 100644 doc/source/api/nova..service.rst create mode 100644 doc/source/api/nova..test.rst create mode 100644 doc/source/api/nova..tests.api.openstack.fakes.rst create mode 100644 doc/source/api/nova..tests.api.openstack.test_adminapi.rst create mode 100644 doc/source/api/nova..tests.api.openstack.test_api.rst create mode 100644 doc/source/api/nova..tests.api.openstack.test_auth.rst create mode 100644 doc/source/api/nova..tests.api.openstack.test_common.rst create mode 100644 doc/source/api/nova..tests.api.openstack.test_faults.rst create mode 100644 doc/source/api/nova..tests.api.openstack.test_flavors.rst create mode 100644 doc/source/api/nova..tests.api.openstack.test_images.rst create mode 100644 doc/source/api/nova..tests.api.openstack.test_ratelimiting.rst create mode 100644 doc/source/api/nova..tests.api.openstack.test_servers.rst create mode 100644 doc/source/api/nova..tests.api.openstack.test_shared_ip_groups.rst create mode 100644 doc/source/api/nova..tests.api.openstack.test_zones.rst create mode 100644 doc/source/api/nova..tests.api.test_wsgi.rst create mode 100644 doc/source/api/nova..tests.db.fakes.rst create mode 100644 doc/source/api/nova..tests.declare_flags.rst create mode 100644 doc/source/api/nova..tests.fake_flags.rst create mode 100644 doc/source/api/nova..tests.glance.stubs.rst create mode 100644 doc/source/api/nova..tests.hyperv_unittest.rst create mode 100644 doc/source/api/nova..tests.objectstore_unittest.rst create mode 100644 doc/source/api/nova..tests.real_flags.rst create mode 100644 doc/source/api/nova..tests.runtime_flags.rst create mode 100644 doc/source/api/nova..tests.test_access.rst create mode 100644 doc/source/api/nova..tests.test_api.rst create mode 100644 doc/source/api/nova..tests.test_auth.rst create mode 100644 doc/source/api/nova..tests.test_cloud.rst create mode 100644 doc/source/api/nova..tests.test_compute.rst create mode 100644 doc/source/api/nova..tests.test_console.rst create mode 100644 doc/source/api/nova..tests.test_direct.rst create mode 100644 doc/source/api/nova..tests.test_flags.rst create mode 100644 doc/source/api/nova..tests.test_localization.rst create mode 100644 doc/source/api/nova..tests.test_log.rst create mode 100644 doc/source/api/nova..tests.test_middleware.rst create mode 100644 doc/source/api/nova..tests.test_misc.rst create mode 100644 doc/source/api/nova..tests.test_network.rst create mode 100644 doc/source/api/nova..tests.test_quota.rst create mode 100644 doc/source/api/nova..tests.test_rpc.rst create mode 100644 doc/source/api/nova..tests.test_scheduler.rst create mode 100644 doc/source/api/nova..tests.test_service.rst create mode 100644 doc/source/api/nova..tests.test_twistd.rst create mode 100644 doc/source/api/nova..tests.test_virt.rst create mode 100644 doc/source/api/nova..tests.test_volume.rst create mode 100644 doc/source/api/nova..tests.test_xenapi.rst create mode 100644 doc/source/api/nova..tests.xenapi.stubs.rst create mode 100644 doc/source/api/nova..twistd.rst create mode 100644 doc/source/api/nova..utils.rst create mode 100644 doc/source/api/nova..version.rst create mode 100644 doc/source/api/nova..virt.connection.rst create mode 100644 doc/source/api/nova..virt.disk.rst create mode 100644 doc/source/api/nova..virt.fake.rst create mode 100644 doc/source/api/nova..virt.hyperv.rst create mode 100644 doc/source/api/nova..virt.images.rst create mode 100644 doc/source/api/nova..virt.libvirt_conn.rst create mode 100644 doc/source/api/nova..virt.xenapi.fake.rst create mode 100644 doc/source/api/nova..virt.xenapi.network_utils.rst create mode 100644 doc/source/api/nova..virt.xenapi.vm_utils.rst create mode 100644 doc/source/api/nova..virt.xenapi.vmops.rst create mode 100644 doc/source/api/nova..virt.xenapi.volume_utils.rst create mode 100644 doc/source/api/nova..virt.xenapi.volumeops.rst create mode 100644 doc/source/api/nova..virt.xenapi_conn.rst create mode 100644 doc/source/api/nova..volume.api.rst create mode 100644 doc/source/api/nova..volume.driver.rst create mode 100644 doc/source/api/nova..volume.manager.rst create mode 100644 doc/source/api/nova..volume.san.rst create mode 100644 doc/source/api/nova..wsgi.rst create mode 100644 doc/source/runnova/binaries.rst create mode 100644 doc/source/runnova/euca2ools.rst create mode 100644 doc/source/runnova/flags.rst create mode 100644 doc/source/runnova/getting.started.rst create mode 100644 doc/source/runnova/index.rst create mode 100644 doc/source/runnova/managing.images.rst create mode 100644 doc/source/runnova/managing.instances.rst create mode 100644 doc/source/runnova/managing.networks.rst create mode 100644 doc/source/runnova/managing.projects.rst create mode 100644 doc/source/runnova/managing.users.rst create mode 100644 doc/source/runnova/managingsecurity.rst create mode 100644 doc/source/runnova/monitoring.rst create mode 100644 doc/source/runnova/network.flat.rst create mode 100644 doc/source/runnova/network.vlan.rst create mode 100644 doc/source/runnova/nova.manage.rst create mode 120000 test/.Python create mode 100644 test/bin/activate create mode 100644 test/bin/activate.csh create mode 100644 test/bin/activate.fish create mode 100644 test/bin/activate_this.py create mode 100755 test/bin/easy_install create mode 100755 test/bin/easy_install-2.6 create mode 100755 test/bin/pip create mode 100755 test/bin/pip-2.6 create mode 100755 test/bin/python create mode 120000 test/bin/python2.6 create mode 120000 test/include/python2.6 create mode 120000 test/lib/python2.6/UserDict.py create mode 120000 test/lib/python2.6/_abcoll.py create mode 120000 test/lib/python2.6/abc.py create mode 120000 test/lib/python2.6/codecs.py create mode 120000 test/lib/python2.6/config create mode 120000 test/lib/python2.6/copy_reg.py create mode 100644 test/lib/python2.6/distutils/__init__.py create mode 100644 test/lib/python2.6/distutils/distutils.cfg create mode 120000 test/lib/python2.6/encodings create mode 120000 test/lib/python2.6/fnmatch.py create mode 120000 test/lib/python2.6/genericpath.py create mode 120000 test/lib/python2.6/lib-dynload create mode 120000 test/lib/python2.6/linecache.py create mode 120000 test/lib/python2.6/locale.py create mode 120000 test/lib/python2.6/ntpath.py create mode 100644 test/lib/python2.6/orig-prefix.txt create mode 120000 test/lib/python2.6/os.py create mode 120000 test/lib/python2.6/posixpath.py create mode 120000 test/lib/python2.6/re.py create mode 100644 test/lib/python2.6/site-packages/easy-install.pth create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/PKG-INFO create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/SOURCES.txt create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/dependency_links.txt create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/entry_points.txt create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/not-zip-safe create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/top_level.txt create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/__init__.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/_pkgutil.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/backwardcompat.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/basecommand.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/baseparser.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/__init__.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/bundle.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/completion.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/freeze.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/help.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/install.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/search.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/uninstall.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/unzip.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/zip.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/download.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/exceptions.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/index.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/locations.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/log.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/req.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/runner.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/util.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/__init__.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/bazaar.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/git.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/mercurial.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/subversion.py create mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/venv.py create mode 100644 test/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg create mode 100644 test/lib/python2.6/site-packages/setuptools.pth create mode 100644 test/lib/python2.6/site.py create mode 120000 test/lib/python2.6/sre.py create mode 120000 test/lib/python2.6/sre_compile.py create mode 120000 test/lib/python2.6/sre_constants.py create mode 120000 test/lib/python2.6/sre_parse.py create mode 120000 test/lib/python2.6/stat.py create mode 120000 test/lib/python2.6/types.py create mode 120000 test/lib/python2.6/warnings.py diff --git a/doc/.autogenerated b/doc/.autogenerated new file mode 100644 index 000000000..e4c98ec9b --- /dev/null +++ b/doc/.autogenerated @@ -0,0 +1,406 @@ +source/api/nova..adminclient.rst +source/api/nova..api.direct.rst +source/api/nova..api.ec2.admin.rst +source/api/nova..api.ec2.apirequest.rst +source/api/nova..api.ec2.cloud.rst +source/api/nova..api.ec2.metadatarequesthandler.rst +source/api/nova..api.openstack.auth.rst +source/api/nova..api.openstack.backup_schedules.rst +source/api/nova..api.openstack.common.rst +source/api/nova..api.openstack.consoles.rst +source/api/nova..api.openstack.faults.rst +source/api/nova..api.openstack.flavors.rst +source/api/nova..api.openstack.images.rst +source/api/nova..api.openstack.servers.rst +source/api/nova..api.openstack.shared_ip_groups.rst +source/api/nova..api.openstack.zones.rst +source/api/nova..auth.dbdriver.rst +source/api/nova..auth.fakeldap.rst +source/api/nova..auth.ldapdriver.rst +source/api/nova..auth.manager.rst +source/api/nova..auth.signer.rst +source/api/nova..cloudpipe.pipelib.rst +source/api/nova..compute.api.rst +source/api/nova..compute.instance_types.rst +source/api/nova..compute.manager.rst +source/api/nova..compute.monitor.rst +source/api/nova..compute.power_state.rst +source/api/nova..console.api.rst +source/api/nova..console.fake.rst +source/api/nova..console.manager.rst +source/api/nova..console.xvp.rst +source/api/nova..context.rst +source/api/nova..crypto.rst +source/api/nova..db.api.rst +source/api/nova..db.base.rst +source/api/nova..db.migration.rst +source/api/nova..db.sqlalchemy.api.rst +source/api/nova..db.sqlalchemy.migrate_repo.manage.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst +source/api/nova..db.sqlalchemy.migration.rst +source/api/nova..db.sqlalchemy.models.rst +source/api/nova..db.sqlalchemy.session.rst +source/api/nova..exception.rst +source/api/nova..fakememcache.rst +source/api/nova..fakerabbit.rst +source/api/nova..flags.rst +source/api/nova..image.glance.rst +source/api/nova..image.local.rst +source/api/nova..image.s3.rst +source/api/nova..image.service.rst +source/api/nova..log.rst +source/api/nova..manager.rst +source/api/nova..network.api.rst +source/api/nova..network.linux_net.rst +source/api/nova..network.manager.rst +source/api/nova..objectstore.bucket.rst +source/api/nova..objectstore.handler.rst +source/api/nova..objectstore.image.rst +source/api/nova..objectstore.stored.rst +source/api/nova..quota.rst +source/api/nova..rpc.rst +source/api/nova..scheduler.chance.rst +source/api/nova..scheduler.driver.rst +source/api/nova..scheduler.manager.rst +source/api/nova..scheduler.simple.rst +source/api/nova..scheduler.zone.rst +source/api/nova..service.rst +source/api/nova..test.rst +source/api/nova..tests.api.openstack.fakes.rst +source/api/nova..tests.api.openstack.test_adminapi.rst +source/api/nova..tests.api.openstack.test_api.rst +source/api/nova..tests.api.openstack.test_auth.rst +source/api/nova..tests.api.openstack.test_common.rst +source/api/nova..tests.api.openstack.test_faults.rst +source/api/nova..tests.api.openstack.test_flavors.rst +source/api/nova..tests.api.openstack.test_images.rst +source/api/nova..tests.api.openstack.test_ratelimiting.rst +source/api/nova..tests.api.openstack.test_servers.rst +source/api/nova..tests.api.openstack.test_shared_ip_groups.rst +source/api/nova..tests.api.openstack.test_zones.rst +source/api/nova..tests.api.test_wsgi.rst +source/api/nova..tests.db.fakes.rst +source/api/nova..tests.declare_flags.rst +source/api/nova..tests.fake_flags.rst +source/api/nova..tests.glance.stubs.rst +source/api/nova..tests.hyperv_unittest.rst +source/api/nova..tests.objectstore_unittest.rst +source/api/nova..tests.real_flags.rst +source/api/nova..tests.runtime_flags.rst +source/api/nova..tests.test_access.rst +source/api/nova..tests.test_api.rst +source/api/nova..tests.test_auth.rst +source/api/nova..tests.test_cloud.rst +source/api/nova..tests.test_compute.rst +source/api/nova..tests.test_console.rst +source/api/nova..tests.test_direct.rst +source/api/nova..tests.test_flags.rst +source/api/nova..tests.test_localization.rst +source/api/nova..tests.test_log.rst +source/api/nova..tests.test_middleware.rst +source/api/nova..tests.test_misc.rst +source/api/nova..tests.test_network.rst +source/api/nova..tests.test_quota.rst +source/api/nova..tests.test_rpc.rst +source/api/nova..tests.test_scheduler.rst +source/api/nova..tests.test_service.rst +source/api/nova..tests.test_twistd.rst +source/api/nova..tests.test_virt.rst +source/api/nova..tests.test_volume.rst +source/api/nova..tests.test_xenapi.rst +source/api/nova..tests.xenapi.stubs.rst +source/api/nova..twistd.rst +source/api/nova..utils.rst +source/api/nova..version.rst +source/api/nova..virt.connection.rst +source/api/nova..virt.disk.rst +source/api/nova..virt.fake.rst +source/api/nova..virt.hyperv.rst +source/api/nova..virt.images.rst +source/api/nova..virt.libvirt_conn.rst +source/api/nova..virt.xenapi.fake.rst +source/api/nova..virt.xenapi.network_utils.rst +source/api/nova..virt.xenapi.vm_utils.rst +source/api/nova..virt.xenapi.vmops.rst +source/api/nova..virt.xenapi.volume_utils.rst +source/api/nova..virt.xenapi.volumeops.rst +source/api/nova..virt.xenapi_conn.rst +source/api/nova..volume.api.rst +source/api/nova..volume.driver.rst +source/api/nova..volume.manager.rst +source/api/nova..volume.san.rst +source/api/nova..wsgi.rst +source/api/autoindex.rst +source/api/nova..adminclient.rst +source/api/nova..api.direct.rst +source/api/nova..api.ec2.admin.rst +source/api/nova..api.ec2.apirequest.rst +source/api/nova..api.ec2.cloud.rst +source/api/nova..api.ec2.metadatarequesthandler.rst +source/api/nova..api.openstack.auth.rst +source/api/nova..api.openstack.backup_schedules.rst +source/api/nova..api.openstack.common.rst +source/api/nova..api.openstack.consoles.rst +source/api/nova..api.openstack.faults.rst +source/api/nova..api.openstack.flavors.rst +source/api/nova..api.openstack.images.rst +source/api/nova..api.openstack.servers.rst +source/api/nova..api.openstack.shared_ip_groups.rst +source/api/nova..api.openstack.zones.rst +source/api/nova..auth.dbdriver.rst +source/api/nova..auth.fakeldap.rst +source/api/nova..auth.ldapdriver.rst +source/api/nova..auth.manager.rst +source/api/nova..auth.signer.rst +source/api/nova..cloudpipe.pipelib.rst +source/api/nova..compute.api.rst +source/api/nova..compute.instance_types.rst +source/api/nova..compute.manager.rst +source/api/nova..compute.monitor.rst +source/api/nova..compute.power_state.rst +source/api/nova..console.api.rst +source/api/nova..console.fake.rst +source/api/nova..console.manager.rst +source/api/nova..console.xvp.rst +source/api/nova..context.rst +source/api/nova..crypto.rst +source/api/nova..db.api.rst +source/api/nova..db.base.rst +source/api/nova..db.migration.rst +source/api/nova..db.sqlalchemy.api.rst +source/api/nova..db.sqlalchemy.migrate_repo.manage.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst +source/api/nova..db.sqlalchemy.migration.rst +source/api/nova..db.sqlalchemy.models.rst +source/api/nova..db.sqlalchemy.session.rst +source/api/nova..exception.rst +source/api/nova..fakememcache.rst +source/api/nova..fakerabbit.rst +source/api/nova..flags.rst +source/api/nova..image.glance.rst +source/api/nova..image.local.rst +source/api/nova..image.s3.rst +source/api/nova..image.service.rst +source/api/nova..log.rst +source/api/nova..manager.rst +source/api/nova..network.api.rst +source/api/nova..network.linux_net.rst +source/api/nova..network.manager.rst +source/api/nova..objectstore.bucket.rst +source/api/nova..objectstore.handler.rst +source/api/nova..objectstore.image.rst +source/api/nova..objectstore.stored.rst +source/api/nova..quota.rst +source/api/nova..rpc.rst +source/api/nova..scheduler.chance.rst +source/api/nova..scheduler.driver.rst +source/api/nova..scheduler.manager.rst +source/api/nova..scheduler.simple.rst +source/api/nova..scheduler.zone.rst +source/api/nova..service.rst +source/api/nova..test.rst +source/api/nova..tests.api.openstack.fakes.rst +source/api/nova..tests.api.openstack.test_adminapi.rst +source/api/nova..tests.api.openstack.test_api.rst +source/api/nova..tests.api.openstack.test_auth.rst +source/api/nova..tests.api.openstack.test_common.rst +source/api/nova..tests.api.openstack.test_faults.rst +source/api/nova..tests.api.openstack.test_flavors.rst +source/api/nova..tests.api.openstack.test_images.rst +source/api/nova..tests.api.openstack.test_ratelimiting.rst +source/api/nova..tests.api.openstack.test_servers.rst +source/api/nova..tests.api.openstack.test_shared_ip_groups.rst +source/api/nova..tests.api.openstack.test_zones.rst +source/api/nova..tests.api.test_wsgi.rst +source/api/nova..tests.db.fakes.rst +source/api/nova..tests.declare_flags.rst +source/api/nova..tests.fake_flags.rst +source/api/nova..tests.glance.stubs.rst +source/api/nova..tests.hyperv_unittest.rst +source/api/nova..tests.objectstore_unittest.rst +source/api/nova..tests.real_flags.rst +source/api/nova..tests.runtime_flags.rst +source/api/nova..tests.test_access.rst +source/api/nova..tests.test_api.rst +source/api/nova..tests.test_auth.rst +source/api/nova..tests.test_cloud.rst +source/api/nova..tests.test_compute.rst +source/api/nova..tests.test_console.rst +source/api/nova..tests.test_direct.rst +source/api/nova..tests.test_flags.rst +source/api/nova..tests.test_localization.rst +source/api/nova..tests.test_log.rst +source/api/nova..tests.test_middleware.rst +source/api/nova..tests.test_misc.rst +source/api/nova..tests.test_network.rst +source/api/nova..tests.test_quota.rst +source/api/nova..tests.test_rpc.rst +source/api/nova..tests.test_scheduler.rst +source/api/nova..tests.test_service.rst +source/api/nova..tests.test_twistd.rst +source/api/nova..tests.test_virt.rst +source/api/nova..tests.test_volume.rst +source/api/nova..tests.test_xenapi.rst +source/api/nova..tests.xenapi.stubs.rst +source/api/nova..twistd.rst +source/api/nova..utils.rst +source/api/nova..version.rst +source/api/nova..virt.connection.rst +source/api/nova..virt.disk.rst +source/api/nova..virt.fake.rst +source/api/nova..virt.hyperv.rst +source/api/nova..virt.images.rst +source/api/nova..virt.libvirt_conn.rst +source/api/nova..virt.xenapi.fake.rst +source/api/nova..virt.xenapi.network_utils.rst +source/api/nova..virt.xenapi.vm_utils.rst +source/api/nova..virt.xenapi.vmops.rst +source/api/nova..virt.xenapi.volume_utils.rst +source/api/nova..virt.xenapi.volumeops.rst +source/api/nova..virt.xenapi_conn.rst +source/api/nova..volume.api.rst +source/api/nova..volume.driver.rst +source/api/nova..volume.manager.rst +source/api/nova..volume.san.rst +source/api/nova..wsgi.rst +source/api/nova..adminclient.rst +source/api/nova..api.direct.rst +source/api/nova..api.ec2.admin.rst +source/api/nova..api.ec2.apirequest.rst +source/api/nova..api.ec2.cloud.rst +source/api/nova..api.ec2.metadatarequesthandler.rst +source/api/nova..api.openstack.auth.rst +source/api/nova..api.openstack.backup_schedules.rst +source/api/nova..api.openstack.common.rst +source/api/nova..api.openstack.consoles.rst +source/api/nova..api.openstack.faults.rst +source/api/nova..api.openstack.flavors.rst +source/api/nova..api.openstack.images.rst +source/api/nova..api.openstack.servers.rst +source/api/nova..api.openstack.shared_ip_groups.rst +source/api/nova..api.openstack.zones.rst +source/api/nova..auth.dbdriver.rst +source/api/nova..auth.fakeldap.rst +source/api/nova..auth.ldapdriver.rst +source/api/nova..auth.manager.rst +source/api/nova..auth.signer.rst +source/api/nova..cloudpipe.pipelib.rst +source/api/nova..compute.api.rst +source/api/nova..compute.instance_types.rst +source/api/nova..compute.manager.rst +source/api/nova..compute.monitor.rst +source/api/nova..compute.power_state.rst +source/api/nova..console.api.rst +source/api/nova..console.fake.rst +source/api/nova..console.manager.rst +source/api/nova..console.xvp.rst +source/api/nova..context.rst +source/api/nova..crypto.rst +source/api/nova..db.api.rst +source/api/nova..db.base.rst +source/api/nova..db.migration.rst +source/api/nova..db.sqlalchemy.api.rst +source/api/nova..db.sqlalchemy.migrate_repo.manage.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst +source/api/nova..db.sqlalchemy.migration.rst +source/api/nova..db.sqlalchemy.models.rst +source/api/nova..db.sqlalchemy.session.rst +source/api/nova..exception.rst +source/api/nova..fakememcache.rst +source/api/nova..fakerabbit.rst +source/api/nova..flags.rst +source/api/nova..image.glance.rst +source/api/nova..image.local.rst +source/api/nova..image.s3.rst +source/api/nova..image.service.rst +source/api/nova..log.rst +source/api/nova..manager.rst +source/api/nova..network.api.rst +source/api/nova..network.linux_net.rst +source/api/nova..network.manager.rst +source/api/nova..objectstore.bucket.rst +source/api/nova..objectstore.handler.rst +source/api/nova..objectstore.image.rst +source/api/nova..objectstore.stored.rst +source/api/nova..quota.rst +source/api/nova..rpc.rst +source/api/nova..scheduler.chance.rst +source/api/nova..scheduler.driver.rst +source/api/nova..scheduler.manager.rst +source/api/nova..scheduler.simple.rst +source/api/nova..scheduler.zone.rst +source/api/nova..service.rst +source/api/nova..test.rst +source/api/nova..tests.api.openstack.fakes.rst +source/api/nova..tests.api.openstack.test_adminapi.rst +source/api/nova..tests.api.openstack.test_api.rst +source/api/nova..tests.api.openstack.test_auth.rst +source/api/nova..tests.api.openstack.test_common.rst +source/api/nova..tests.api.openstack.test_faults.rst +source/api/nova..tests.api.openstack.test_flavors.rst +source/api/nova..tests.api.openstack.test_images.rst +source/api/nova..tests.api.openstack.test_ratelimiting.rst +source/api/nova..tests.api.openstack.test_servers.rst +source/api/nova..tests.api.openstack.test_shared_ip_groups.rst +source/api/nova..tests.api.openstack.test_zones.rst +source/api/nova..tests.api.test_wsgi.rst +source/api/nova..tests.db.fakes.rst +source/api/nova..tests.declare_flags.rst +source/api/nova..tests.fake_flags.rst +source/api/nova..tests.glance.stubs.rst +source/api/nova..tests.hyperv_unittest.rst +source/api/nova..tests.objectstore_unittest.rst +source/api/nova..tests.real_flags.rst +source/api/nova..tests.runtime_flags.rst +source/api/nova..tests.test_access.rst +source/api/nova..tests.test_api.rst +source/api/nova..tests.test_auth.rst +source/api/nova..tests.test_cloud.rst +source/api/nova..tests.test_compute.rst +source/api/nova..tests.test_console.rst +source/api/nova..tests.test_direct.rst +source/api/nova..tests.test_flags.rst +source/api/nova..tests.test_localization.rst +source/api/nova..tests.test_log.rst +source/api/nova..tests.test_middleware.rst +source/api/nova..tests.test_misc.rst +source/api/nova..tests.test_network.rst +source/api/nova..tests.test_quota.rst +source/api/nova..tests.test_rpc.rst +source/api/nova..tests.test_scheduler.rst +source/api/nova..tests.test_service.rst +source/api/nova..tests.test_twistd.rst +source/api/nova..tests.test_virt.rst +source/api/nova..tests.test_volume.rst +source/api/nova..tests.test_xenapi.rst +source/api/nova..tests.xenapi.stubs.rst +source/api/nova..twistd.rst +source/api/nova..utils.rst +source/api/nova..version.rst +source/api/nova..virt.connection.rst +source/api/nova..virt.disk.rst +source/api/nova..virt.fake.rst +source/api/nova..virt.hyperv.rst +source/api/nova..virt.images.rst +source/api/nova..virt.libvirt_conn.rst +source/api/nova..virt.xenapi.fake.rst +source/api/nova..virt.xenapi.network_utils.rst +source/api/nova..virt.xenapi.vm_utils.rst +source/api/nova..virt.xenapi.vmops.rst +source/api/nova..virt.xenapi.volume_utils.rst +source/api/nova..virt.xenapi.volumeops.rst +source/api/nova..virt.xenapi_conn.rst +source/api/nova..volume.api.rst +source/api/nova..volume.driver.rst +source/api/nova..volume.manager.rst +source/api/nova..volume.san.rst +source/api/nova..wsgi.rst diff --git a/doc/build/.DS_Store b/doc/build/.DS_Store new file mode 100644 index 000000000..5008ddfcf Binary files /dev/null and b/doc/build/.DS_Store differ diff --git a/doc/build/html/.DS_Store b/doc/build/html/.DS_Store new file mode 100644 index 000000000..5008ddfcf Binary files /dev/null and b/doc/build/html/.DS_Store differ diff --git a/doc/build/html/.buildinfo b/doc/build/html/.buildinfo new file mode 100644 index 000000000..091736d4f --- /dev/null +++ b/doc/build/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 2a2fe6198f4be4a4d6f289b09d16d74a +tags: fbb0d17656682115ca4d033fb2f83ba1 diff --git a/doc/source/api/autoindex.rst b/doc/source/api/autoindex.rst new file mode 100644 index 000000000..41fc1f4a9 --- /dev/null +++ b/doc/source/api/autoindex.rst @@ -0,0 +1,138 @@ +.. toctree:: + :maxdepth: 1 + + nova..adminclient.rst + nova..api.direct.rst + nova..api.ec2.admin.rst + nova..api.ec2.apirequest.rst + nova..api.ec2.cloud.rst + nova..api.ec2.metadatarequesthandler.rst + nova..api.openstack.auth.rst + nova..api.openstack.backup_schedules.rst + nova..api.openstack.common.rst + nova..api.openstack.consoles.rst + nova..api.openstack.faults.rst + nova..api.openstack.flavors.rst + nova..api.openstack.images.rst + nova..api.openstack.servers.rst + nova..api.openstack.shared_ip_groups.rst + nova..api.openstack.zones.rst + nova..auth.dbdriver.rst + nova..auth.fakeldap.rst + nova..auth.ldapdriver.rst + nova..auth.manager.rst + nova..auth.signer.rst + nova..cloudpipe.pipelib.rst + nova..compute.api.rst + nova..compute.instance_types.rst + nova..compute.manager.rst + nova..compute.monitor.rst + nova..compute.power_state.rst + nova..console.api.rst + nova..console.fake.rst + nova..console.manager.rst + nova..console.xvp.rst + nova..context.rst + nova..crypto.rst + nova..db.api.rst + nova..db.base.rst + nova..db.migration.rst + nova..db.sqlalchemy.api.rst + nova..db.sqlalchemy.migrate_repo.manage.rst + nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst + nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst + nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst + nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst + nova..db.sqlalchemy.migration.rst + nova..db.sqlalchemy.models.rst + nova..db.sqlalchemy.session.rst + nova..exception.rst + nova..fakememcache.rst + nova..fakerabbit.rst + nova..flags.rst + nova..image.glance.rst + nova..image.local.rst + nova..image.s3.rst + nova..image.service.rst + nova..log.rst + nova..manager.rst + nova..network.api.rst + nova..network.linux_net.rst + nova..network.manager.rst + nova..objectstore.bucket.rst + nova..objectstore.handler.rst + nova..objectstore.image.rst + nova..objectstore.stored.rst + nova..quota.rst + nova..rpc.rst + nova..scheduler.chance.rst + nova..scheduler.driver.rst + nova..scheduler.manager.rst + nova..scheduler.simple.rst + nova..scheduler.zone.rst + nova..service.rst + nova..test.rst + nova..tests.api.openstack.fakes.rst + nova..tests.api.openstack.test_adminapi.rst + nova..tests.api.openstack.test_api.rst + nova..tests.api.openstack.test_auth.rst + nova..tests.api.openstack.test_common.rst + nova..tests.api.openstack.test_faults.rst + nova..tests.api.openstack.test_flavors.rst + nova..tests.api.openstack.test_images.rst + nova..tests.api.openstack.test_ratelimiting.rst + nova..tests.api.openstack.test_servers.rst + nova..tests.api.openstack.test_shared_ip_groups.rst + nova..tests.api.openstack.test_zones.rst + nova..tests.api.test_wsgi.rst + nova..tests.db.fakes.rst + nova..tests.declare_flags.rst + nova..tests.fake_flags.rst + nova..tests.glance.stubs.rst + nova..tests.hyperv_unittest.rst + nova..tests.objectstore_unittest.rst + nova..tests.real_flags.rst + nova..tests.runtime_flags.rst + nova..tests.test_access.rst + nova..tests.test_api.rst + nova..tests.test_auth.rst + nova..tests.test_cloud.rst + nova..tests.test_compute.rst + nova..tests.test_console.rst + nova..tests.test_direct.rst + nova..tests.test_flags.rst + nova..tests.test_localization.rst + nova..tests.test_log.rst + nova..tests.test_middleware.rst + nova..tests.test_misc.rst + nova..tests.test_network.rst + nova..tests.test_quota.rst + nova..tests.test_rpc.rst + nova..tests.test_scheduler.rst + nova..tests.test_service.rst + nova..tests.test_twistd.rst + nova..tests.test_virt.rst + nova..tests.test_volume.rst + nova..tests.test_xenapi.rst + nova..tests.xenapi.stubs.rst + nova..twistd.rst + nova..utils.rst + nova..version.rst + nova..virt.connection.rst + nova..virt.disk.rst + nova..virt.fake.rst + nova..virt.hyperv.rst + nova..virt.images.rst + nova..virt.libvirt_conn.rst + nova..virt.xenapi.fake.rst + nova..virt.xenapi.network_utils.rst + nova..virt.xenapi.vm_utils.rst + nova..virt.xenapi.vmops.rst + nova..virt.xenapi.volume_utils.rst + nova..virt.xenapi.volumeops.rst + nova..virt.xenapi_conn.rst + nova..volume.api.rst + nova..volume.driver.rst + nova..volume.manager.rst + nova..volume.san.rst + nova..wsgi.rst diff --git a/doc/source/api/nova..adminclient.rst b/doc/source/api/nova..adminclient.rst new file mode 100644 index 000000000..35fa839e1 --- /dev/null +++ b/doc/source/api/nova..adminclient.rst @@ -0,0 +1,6 @@ +The :mod:`nova..adminclient` Module +============================================================================== +.. automodule:: nova..adminclient + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.direct.rst b/doc/source/api/nova..api.direct.rst new file mode 100644 index 000000000..a1705c707 --- /dev/null +++ b/doc/source/api/nova..api.direct.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.direct` Module +============================================================================== +.. automodule:: nova..api.direct + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.ec2.admin.rst b/doc/source/api/nova..api.ec2.admin.rst new file mode 100644 index 000000000..4e9ab308b --- /dev/null +++ b/doc/source/api/nova..api.ec2.admin.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.ec2.admin` Module +============================================================================== +.. automodule:: nova..api.ec2.admin + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.ec2.apirequest.rst b/doc/source/api/nova..api.ec2.apirequest.rst new file mode 100644 index 000000000..c17a2ff3a --- /dev/null +++ b/doc/source/api/nova..api.ec2.apirequest.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.ec2.apirequest` Module +============================================================================== +.. automodule:: nova..api.ec2.apirequest + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.ec2.cloud.rst b/doc/source/api/nova..api.ec2.cloud.rst new file mode 100644 index 000000000..f6145c217 --- /dev/null +++ b/doc/source/api/nova..api.ec2.cloud.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.ec2.cloud` Module +============================================================================== +.. automodule:: nova..api.ec2.cloud + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.ec2.metadatarequesthandler.rst b/doc/source/api/nova..api.ec2.metadatarequesthandler.rst new file mode 100644 index 000000000..75f5169e5 --- /dev/null +++ b/doc/source/api/nova..api.ec2.metadatarequesthandler.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.ec2.metadatarequesthandler` Module +============================================================================== +.. automodule:: nova..api.ec2.metadatarequesthandler + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.openstack.auth.rst b/doc/source/api/nova..api.openstack.auth.rst new file mode 100644 index 000000000..8c3f8f2da --- /dev/null +++ b/doc/source/api/nova..api.openstack.auth.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.openstack.auth` Module +============================================================================== +.. automodule:: nova..api.openstack.auth + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.openstack.backup_schedules.rst b/doc/source/api/nova..api.openstack.backup_schedules.rst new file mode 100644 index 000000000..6b406f12d --- /dev/null +++ b/doc/source/api/nova..api.openstack.backup_schedules.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.openstack.backup_schedules` Module +============================================================================== +.. automodule:: nova..api.openstack.backup_schedules + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.openstack.common.rst b/doc/source/api/nova..api.openstack.common.rst new file mode 100644 index 000000000..4fd734790 --- /dev/null +++ b/doc/source/api/nova..api.openstack.common.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.openstack.common` Module +============================================================================== +.. automodule:: nova..api.openstack.common + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.openstack.consoles.rst b/doc/source/api/nova..api.openstack.consoles.rst new file mode 100644 index 000000000..1e3e09599 --- /dev/null +++ b/doc/source/api/nova..api.openstack.consoles.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.openstack.consoles` Module +============================================================================== +.. automodule:: nova..api.openstack.consoles + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.openstack.faults.rst b/doc/source/api/nova..api.openstack.faults.rst new file mode 100644 index 000000000..7b25561f7 --- /dev/null +++ b/doc/source/api/nova..api.openstack.faults.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.openstack.faults` Module +============================================================================== +.. automodule:: nova..api.openstack.faults + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.openstack.flavors.rst b/doc/source/api/nova..api.openstack.flavors.rst new file mode 100644 index 000000000..0deb724de --- /dev/null +++ b/doc/source/api/nova..api.openstack.flavors.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.openstack.flavors` Module +============================================================================== +.. automodule:: nova..api.openstack.flavors + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.openstack.images.rst b/doc/source/api/nova..api.openstack.images.rst new file mode 100644 index 000000000..82bd5f1e8 --- /dev/null +++ b/doc/source/api/nova..api.openstack.images.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.openstack.images` Module +============================================================================== +.. automodule:: nova..api.openstack.images + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.openstack.servers.rst b/doc/source/api/nova..api.openstack.servers.rst new file mode 100644 index 000000000..c36856ea2 --- /dev/null +++ b/doc/source/api/nova..api.openstack.servers.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.openstack.servers` Module +============================================================================== +.. automodule:: nova..api.openstack.servers + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.openstack.shared_ip_groups.rst b/doc/source/api/nova..api.openstack.shared_ip_groups.rst new file mode 100644 index 000000000..4b1f44efe --- /dev/null +++ b/doc/source/api/nova..api.openstack.shared_ip_groups.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.openstack.shared_ip_groups` Module +============================================================================== +.. automodule:: nova..api.openstack.shared_ip_groups + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..api.openstack.zones.rst b/doc/source/api/nova..api.openstack.zones.rst new file mode 100644 index 000000000..ebe4569c5 --- /dev/null +++ b/doc/source/api/nova..api.openstack.zones.rst @@ -0,0 +1,6 @@ +The :mod:`nova..api.openstack.zones` Module +============================================================================== +.. automodule:: nova..api.openstack.zones + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..auth.dbdriver.rst b/doc/source/api/nova..auth.dbdriver.rst new file mode 100644 index 000000000..7de68b6e0 --- /dev/null +++ b/doc/source/api/nova..auth.dbdriver.rst @@ -0,0 +1,6 @@ +The :mod:`nova..auth.dbdriver` Module +============================================================================== +.. automodule:: nova..auth.dbdriver + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..auth.fakeldap.rst b/doc/source/api/nova..auth.fakeldap.rst new file mode 100644 index 000000000..ca8a3ad4d --- /dev/null +++ b/doc/source/api/nova..auth.fakeldap.rst @@ -0,0 +1,6 @@ +The :mod:`nova..auth.fakeldap` Module +============================================================================== +.. automodule:: nova..auth.fakeldap + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..auth.ldapdriver.rst b/doc/source/api/nova..auth.ldapdriver.rst new file mode 100644 index 000000000..c44463522 --- /dev/null +++ b/doc/source/api/nova..auth.ldapdriver.rst @@ -0,0 +1,6 @@ +The :mod:`nova..auth.ldapdriver` Module +============================================================================== +.. automodule:: nova..auth.ldapdriver + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..auth.manager.rst b/doc/source/api/nova..auth.manager.rst new file mode 100644 index 000000000..bc5ce2ec3 --- /dev/null +++ b/doc/source/api/nova..auth.manager.rst @@ -0,0 +1,6 @@ +The :mod:`nova..auth.manager` Module +============================================================================== +.. automodule:: nova..auth.manager + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..auth.signer.rst b/doc/source/api/nova..auth.signer.rst new file mode 100644 index 000000000..aad824ead --- /dev/null +++ b/doc/source/api/nova..auth.signer.rst @@ -0,0 +1,6 @@ +The :mod:`nova..auth.signer` Module +============================================================================== +.. automodule:: nova..auth.signer + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..cloudpipe.pipelib.rst b/doc/source/api/nova..cloudpipe.pipelib.rst new file mode 100644 index 000000000..054aaf484 --- /dev/null +++ b/doc/source/api/nova..cloudpipe.pipelib.rst @@ -0,0 +1,6 @@ +The :mod:`nova..cloudpipe.pipelib` Module +============================================================================== +.. automodule:: nova..cloudpipe.pipelib + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..compute.api.rst b/doc/source/api/nova..compute.api.rst new file mode 100644 index 000000000..caa66313a --- /dev/null +++ b/doc/source/api/nova..compute.api.rst @@ -0,0 +1,6 @@ +The :mod:`nova..compute.api` Module +============================================================================== +.. automodule:: nova..compute.api + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..compute.instance_types.rst b/doc/source/api/nova..compute.instance_types.rst new file mode 100644 index 000000000..d206ff3a4 --- /dev/null +++ b/doc/source/api/nova..compute.instance_types.rst @@ -0,0 +1,6 @@ +The :mod:`nova..compute.instance_types` Module +============================================================================== +.. automodule:: nova..compute.instance_types + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..compute.manager.rst b/doc/source/api/nova..compute.manager.rst new file mode 100644 index 000000000..33a337c39 --- /dev/null +++ b/doc/source/api/nova..compute.manager.rst @@ -0,0 +1,6 @@ +The :mod:`nova..compute.manager` Module +============================================================================== +.. automodule:: nova..compute.manager + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..compute.monitor.rst b/doc/source/api/nova..compute.monitor.rst new file mode 100644 index 000000000..a91169ecd --- /dev/null +++ b/doc/source/api/nova..compute.monitor.rst @@ -0,0 +1,6 @@ +The :mod:`nova..compute.monitor` Module +============================================================================== +.. automodule:: nova..compute.monitor + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..compute.power_state.rst b/doc/source/api/nova..compute.power_state.rst new file mode 100644 index 000000000..41b1080e5 --- /dev/null +++ b/doc/source/api/nova..compute.power_state.rst @@ -0,0 +1,6 @@ +The :mod:`nova..compute.power_state` Module +============================================================================== +.. automodule:: nova..compute.power_state + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..console.api.rst b/doc/source/api/nova..console.api.rst new file mode 100644 index 000000000..82a51d4c7 --- /dev/null +++ b/doc/source/api/nova..console.api.rst @@ -0,0 +1,6 @@ +The :mod:`nova..console.api` Module +============================================================================== +.. automodule:: nova..console.api + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..console.fake.rst b/doc/source/api/nova..console.fake.rst new file mode 100644 index 000000000..f053f85d6 --- /dev/null +++ b/doc/source/api/nova..console.fake.rst @@ -0,0 +1,6 @@ +The :mod:`nova..console.fake` Module +============================================================================== +.. automodule:: nova..console.fake + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..console.manager.rst b/doc/source/api/nova..console.manager.rst new file mode 100644 index 000000000..f9283a6c3 --- /dev/null +++ b/doc/source/api/nova..console.manager.rst @@ -0,0 +1,6 @@ +The :mod:`nova..console.manager` Module +============================================================================== +.. automodule:: nova..console.manager + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..console.xvp.rst b/doc/source/api/nova..console.xvp.rst new file mode 100644 index 000000000..a0887009e --- /dev/null +++ b/doc/source/api/nova..console.xvp.rst @@ -0,0 +1,6 @@ +The :mod:`nova..console.xvp` Module +============================================================================== +.. automodule:: nova..console.xvp + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..context.rst b/doc/source/api/nova..context.rst new file mode 100644 index 000000000..9de1adb24 --- /dev/null +++ b/doc/source/api/nova..context.rst @@ -0,0 +1,6 @@ +The :mod:`nova..context` Module +============================================================================== +.. automodule:: nova..context + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..crypto.rst b/doc/source/api/nova..crypto.rst new file mode 100644 index 000000000..af9f63634 --- /dev/null +++ b/doc/source/api/nova..crypto.rst @@ -0,0 +1,6 @@ +The :mod:`nova..crypto` Module +============================================================================== +.. automodule:: nova..crypto + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.api.rst b/doc/source/api/nova..db.api.rst new file mode 100644 index 000000000..6d998fbb2 --- /dev/null +++ b/doc/source/api/nova..db.api.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.api` Module +============================================================================== +.. automodule:: nova..db.api + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.base.rst b/doc/source/api/nova..db.base.rst new file mode 100644 index 000000000..29fb417d6 --- /dev/null +++ b/doc/source/api/nova..db.base.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.base` Module +============================================================================== +.. automodule:: nova..db.base + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.migration.rst b/doc/source/api/nova..db.migration.rst new file mode 100644 index 000000000..71dfea301 --- /dev/null +++ b/doc/source/api/nova..db.migration.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.migration` Module +============================================================================== +.. automodule:: nova..db.migration + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.api.rst b/doc/source/api/nova..db.sqlalchemy.api.rst new file mode 100644 index 000000000..76d0c1bd3 --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.api.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.api` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.api + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.manage.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.manage.rst new file mode 100644 index 000000000..93decfb27 --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.manage.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migrate_repo.manage` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migrate_repo.manage + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst new file mode 100644 index 000000000..4b1219edb --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migrate_repo.versions.001_austin` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.001_austin + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst new file mode 100644 index 000000000..82f1f4680 --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migrate_repo.versions.002_bexar` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.002_bexar + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst new file mode 100644 index 000000000..98f3e8da7 --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst new file mode 100644 index 000000000..5cbb81191 --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.migration.rst b/doc/source/api/nova..db.sqlalchemy.migration.rst new file mode 100644 index 000000000..3a9b01b9a --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migration.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migration` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migration + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.models.rst b/doc/source/api/nova..db.sqlalchemy.models.rst new file mode 100644 index 000000000..9c795d7f5 --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.models.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.models` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.models + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.session.rst b/doc/source/api/nova..db.sqlalchemy.session.rst new file mode 100644 index 000000000..cbfd6416a --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.session.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.session` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.session + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..exception.rst b/doc/source/api/nova..exception.rst new file mode 100644 index 000000000..97ac6b752 --- /dev/null +++ b/doc/source/api/nova..exception.rst @@ -0,0 +1,6 @@ +The :mod:`nova..exception` Module +============================================================================== +.. automodule:: nova..exception + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..fakememcache.rst b/doc/source/api/nova..fakememcache.rst new file mode 100644 index 000000000..7e7ffb98b --- /dev/null +++ b/doc/source/api/nova..fakememcache.rst @@ -0,0 +1,6 @@ +The :mod:`nova..fakememcache` Module +============================================================================== +.. automodule:: nova..fakememcache + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..fakerabbit.rst b/doc/source/api/nova..fakerabbit.rst new file mode 100644 index 000000000..f1e27c266 --- /dev/null +++ b/doc/source/api/nova..fakerabbit.rst @@ -0,0 +1,6 @@ +The :mod:`nova..fakerabbit` Module +============================================================================== +.. automodule:: nova..fakerabbit + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..flags.rst b/doc/source/api/nova..flags.rst new file mode 100644 index 000000000..08165be44 --- /dev/null +++ b/doc/source/api/nova..flags.rst @@ -0,0 +1,6 @@ +The :mod:`nova..flags` Module +============================================================================== +.. automodule:: nova..flags + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..image.glance.rst b/doc/source/api/nova..image.glance.rst new file mode 100644 index 000000000..b0882d5ec --- /dev/null +++ b/doc/source/api/nova..image.glance.rst @@ -0,0 +1,6 @@ +The :mod:`nova..image.glance` Module +============================================================================== +.. automodule:: nova..image.glance + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..image.local.rst b/doc/source/api/nova..image.local.rst new file mode 100644 index 000000000..b6ad5470b --- /dev/null +++ b/doc/source/api/nova..image.local.rst @@ -0,0 +1,6 @@ +The :mod:`nova..image.local` Module +============================================================================== +.. automodule:: nova..image.local + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..image.s3.rst b/doc/source/api/nova..image.s3.rst new file mode 100644 index 000000000..e5b236127 --- /dev/null +++ b/doc/source/api/nova..image.s3.rst @@ -0,0 +1,6 @@ +The :mod:`nova..image.s3` Module +============================================================================== +.. automodule:: nova..image.s3 + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..image.service.rst b/doc/source/api/nova..image.service.rst new file mode 100644 index 000000000..78ef1ecca --- /dev/null +++ b/doc/source/api/nova..image.service.rst @@ -0,0 +1,6 @@ +The :mod:`nova..image.service` Module +============================================================================== +.. automodule:: nova..image.service + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..log.rst b/doc/source/api/nova..log.rst new file mode 100644 index 000000000..ff209709f --- /dev/null +++ b/doc/source/api/nova..log.rst @@ -0,0 +1,6 @@ +The :mod:`nova..log` Module +============================================================================== +.. automodule:: nova..log + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..manager.rst b/doc/source/api/nova..manager.rst new file mode 100644 index 000000000..576902491 --- /dev/null +++ b/doc/source/api/nova..manager.rst @@ -0,0 +1,6 @@ +The :mod:`nova..manager` Module +============================================================================== +.. automodule:: nova..manager + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..network.api.rst b/doc/source/api/nova..network.api.rst new file mode 100644 index 000000000..b63be2ba3 --- /dev/null +++ b/doc/source/api/nova..network.api.rst @@ -0,0 +1,6 @@ +The :mod:`nova..network.api` Module +============================================================================== +.. automodule:: nova..network.api + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..network.linux_net.rst b/doc/source/api/nova..network.linux_net.rst new file mode 100644 index 000000000..7af78d5ad --- /dev/null +++ b/doc/source/api/nova..network.linux_net.rst @@ -0,0 +1,6 @@ +The :mod:`nova..network.linux_net` Module +============================================================================== +.. automodule:: nova..network.linux_net + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..network.manager.rst b/doc/source/api/nova..network.manager.rst new file mode 100644 index 000000000..0ea705533 --- /dev/null +++ b/doc/source/api/nova..network.manager.rst @@ -0,0 +1,6 @@ +The :mod:`nova..network.manager` Module +============================================================================== +.. automodule:: nova..network.manager + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..objectstore.bucket.rst b/doc/source/api/nova..objectstore.bucket.rst new file mode 100644 index 000000000..3bfdf639c --- /dev/null +++ b/doc/source/api/nova..objectstore.bucket.rst @@ -0,0 +1,6 @@ +The :mod:`nova..objectstore.bucket` Module +============================================================================== +.. automodule:: nova..objectstore.bucket + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..objectstore.handler.rst b/doc/source/api/nova..objectstore.handler.rst new file mode 100644 index 000000000..0eb8c4efb --- /dev/null +++ b/doc/source/api/nova..objectstore.handler.rst @@ -0,0 +1,6 @@ +The :mod:`nova..objectstore.handler` Module +============================================================================== +.. automodule:: nova..objectstore.handler + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..objectstore.image.rst b/doc/source/api/nova..objectstore.image.rst new file mode 100644 index 000000000..fa4c971f1 --- /dev/null +++ b/doc/source/api/nova..objectstore.image.rst @@ -0,0 +1,6 @@ +The :mod:`nova..objectstore.image` Module +============================================================================== +.. automodule:: nova..objectstore.image + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..objectstore.stored.rst b/doc/source/api/nova..objectstore.stored.rst new file mode 100644 index 000000000..2b1d997a3 --- /dev/null +++ b/doc/source/api/nova..objectstore.stored.rst @@ -0,0 +1,6 @@ +The :mod:`nova..objectstore.stored` Module +============================================================================== +.. automodule:: nova..objectstore.stored + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..quota.rst b/doc/source/api/nova..quota.rst new file mode 100644 index 000000000..4140d95d6 --- /dev/null +++ b/doc/source/api/nova..quota.rst @@ -0,0 +1,6 @@ +The :mod:`nova..quota` Module +============================================================================== +.. automodule:: nova..quota + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..rpc.rst b/doc/source/api/nova..rpc.rst new file mode 100644 index 000000000..5b2a9b8e2 --- /dev/null +++ b/doc/source/api/nova..rpc.rst @@ -0,0 +1,6 @@ +The :mod:`nova..rpc` Module +============================================================================== +.. automodule:: nova..rpc + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..scheduler.chance.rst b/doc/source/api/nova..scheduler.chance.rst new file mode 100644 index 000000000..89c074c8f --- /dev/null +++ b/doc/source/api/nova..scheduler.chance.rst @@ -0,0 +1,6 @@ +The :mod:`nova..scheduler.chance` Module +============================================================================== +.. automodule:: nova..scheduler.chance + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..scheduler.driver.rst b/doc/source/api/nova..scheduler.driver.rst new file mode 100644 index 000000000..793ed9c7b --- /dev/null +++ b/doc/source/api/nova..scheduler.driver.rst @@ -0,0 +1,6 @@ +The :mod:`nova..scheduler.driver` Module +============================================================================== +.. automodule:: nova..scheduler.driver + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..scheduler.manager.rst b/doc/source/api/nova..scheduler.manager.rst new file mode 100644 index 000000000..d0fc7c423 --- /dev/null +++ b/doc/source/api/nova..scheduler.manager.rst @@ -0,0 +1,6 @@ +The :mod:`nova..scheduler.manager` Module +============================================================================== +.. automodule:: nova..scheduler.manager + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..scheduler.simple.rst b/doc/source/api/nova..scheduler.simple.rst new file mode 100644 index 000000000..dacc2cf30 --- /dev/null +++ b/doc/source/api/nova..scheduler.simple.rst @@ -0,0 +1,6 @@ +The :mod:`nova..scheduler.simple` Module +============================================================================== +.. automodule:: nova..scheduler.simple + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..scheduler.zone.rst b/doc/source/api/nova..scheduler.zone.rst new file mode 100644 index 000000000..54c4bf201 --- /dev/null +++ b/doc/source/api/nova..scheduler.zone.rst @@ -0,0 +1,6 @@ +The :mod:`nova..scheduler.zone` Module +============================================================================== +.. automodule:: nova..scheduler.zone + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..service.rst b/doc/source/api/nova..service.rst new file mode 100644 index 000000000..2d2dfcf2e --- /dev/null +++ b/doc/source/api/nova..service.rst @@ -0,0 +1,6 @@ +The :mod:`nova..service` Module +============================================================================== +.. automodule:: nova..service + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..test.rst b/doc/source/api/nova..test.rst new file mode 100644 index 000000000..a6bdb6f1f --- /dev/null +++ b/doc/source/api/nova..test.rst @@ -0,0 +1,6 @@ +The :mod:`nova..test` Module +============================================================================== +.. automodule:: nova..test + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.fakes.rst b/doc/source/api/nova..tests.api.openstack.fakes.rst new file mode 100644 index 000000000..4a9ff5938 --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.fakes.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.fakes` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.fakes + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.test_adminapi.rst b/doc/source/api/nova..tests.api.openstack.test_adminapi.rst new file mode 100644 index 000000000..19a85ca0f --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.test_adminapi.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.test_adminapi` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.test_adminapi + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.test_api.rst b/doc/source/api/nova..tests.api.openstack.test_api.rst new file mode 100644 index 000000000..68106d221 --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.test_api.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.test_api` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.test_api + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.test_auth.rst b/doc/source/api/nova..tests.api.openstack.test_auth.rst new file mode 100644 index 000000000..9f0011669 --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.test_auth.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.test_auth` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.test_auth + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.test_common.rst b/doc/source/api/nova..tests.api.openstack.test_common.rst new file mode 100644 index 000000000..82f40ecb8 --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.test_common.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.test_common` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.test_common + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.test_faults.rst b/doc/source/api/nova..tests.api.openstack.test_faults.rst new file mode 100644 index 000000000..b839ae8a3 --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.test_faults.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.test_faults` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.test_faults + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.test_flavors.rst b/doc/source/api/nova..tests.api.openstack.test_flavors.rst new file mode 100644 index 000000000..471fac56e --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.test_flavors.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.test_flavors` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.test_flavors + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.test_images.rst b/doc/source/api/nova..tests.api.openstack.test_images.rst new file mode 100644 index 000000000..57ae93c8c --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.test_images.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.test_images` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.test_images + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.test_ratelimiting.rst b/doc/source/api/nova..tests.api.openstack.test_ratelimiting.rst new file mode 100644 index 000000000..9a857f795 --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.test_ratelimiting.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.test_ratelimiting` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.test_ratelimiting + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.test_servers.rst b/doc/source/api/nova..tests.api.openstack.test_servers.rst new file mode 100644 index 000000000..ea602e6ab --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.test_servers.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.test_servers` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.test_servers + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.test_shared_ip_groups.rst b/doc/source/api/nova..tests.api.openstack.test_shared_ip_groups.rst new file mode 100644 index 000000000..48814af00 --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.test_shared_ip_groups.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.test_shared_ip_groups` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.test_shared_ip_groups + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.openstack.test_zones.rst b/doc/source/api/nova..tests.api.openstack.test_zones.rst new file mode 100644 index 000000000..ba7078e63 --- /dev/null +++ b/doc/source/api/nova..tests.api.openstack.test_zones.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.openstack.test_zones` Module +============================================================================== +.. automodule:: nova..tests.api.openstack.test_zones + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.api.test_wsgi.rst b/doc/source/api/nova..tests.api.test_wsgi.rst new file mode 100644 index 000000000..8e79caa4d --- /dev/null +++ b/doc/source/api/nova..tests.api.test_wsgi.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.api.test_wsgi` Module +============================================================================== +.. automodule:: nova..tests.api.test_wsgi + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.db.fakes.rst b/doc/source/api/nova..tests.db.fakes.rst new file mode 100644 index 000000000..cc79e55e2 --- /dev/null +++ b/doc/source/api/nova..tests.db.fakes.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.db.fakes` Module +============================================================================== +.. automodule:: nova..tests.db.fakes + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.declare_flags.rst b/doc/source/api/nova..tests.declare_flags.rst new file mode 100644 index 000000000..524e72e91 --- /dev/null +++ b/doc/source/api/nova..tests.declare_flags.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.declare_flags` Module +============================================================================== +.. automodule:: nova..tests.declare_flags + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.fake_flags.rst b/doc/source/api/nova..tests.fake_flags.rst new file mode 100644 index 000000000..a8dc3df36 --- /dev/null +++ b/doc/source/api/nova..tests.fake_flags.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.fake_flags` Module +============================================================================== +.. automodule:: nova..tests.fake_flags + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.glance.stubs.rst b/doc/source/api/nova..tests.glance.stubs.rst new file mode 100644 index 000000000..7ef5fccbe --- /dev/null +++ b/doc/source/api/nova..tests.glance.stubs.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.glance.stubs` Module +============================================================================== +.. automodule:: nova..tests.glance.stubs + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.hyperv_unittest.rst b/doc/source/api/nova..tests.hyperv_unittest.rst new file mode 100644 index 000000000..c08443121 --- /dev/null +++ b/doc/source/api/nova..tests.hyperv_unittest.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.hyperv_unittest` Module +============================================================================== +.. automodule:: nova..tests.hyperv_unittest + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.objectstore_unittest.rst b/doc/source/api/nova..tests.objectstore_unittest.rst new file mode 100644 index 000000000..0ae252f04 --- /dev/null +++ b/doc/source/api/nova..tests.objectstore_unittest.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.objectstore_unittest` Module +============================================================================== +.. automodule:: nova..tests.objectstore_unittest + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.real_flags.rst b/doc/source/api/nova..tests.real_flags.rst new file mode 100644 index 000000000..e9c0d1abd --- /dev/null +++ b/doc/source/api/nova..tests.real_flags.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.real_flags` Module +============================================================================== +.. automodule:: nova..tests.real_flags + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.runtime_flags.rst b/doc/source/api/nova..tests.runtime_flags.rst new file mode 100644 index 000000000..984e21199 --- /dev/null +++ b/doc/source/api/nova..tests.runtime_flags.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.runtime_flags` Module +============================================================================== +.. automodule:: nova..tests.runtime_flags + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_access.rst b/doc/source/api/nova..tests.test_access.rst new file mode 100644 index 000000000..300d8109e --- /dev/null +++ b/doc/source/api/nova..tests.test_access.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_access` Module +============================================================================== +.. automodule:: nova..tests.test_access + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_api.rst b/doc/source/api/nova..tests.test_api.rst new file mode 100644 index 000000000..f9473062e --- /dev/null +++ b/doc/source/api/nova..tests.test_api.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_api` Module +============================================================================== +.. automodule:: nova..tests.test_api + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_auth.rst b/doc/source/api/nova..tests.test_auth.rst new file mode 100644 index 000000000..ff4445ae4 --- /dev/null +++ b/doc/source/api/nova..tests.test_auth.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_auth` Module +============================================================================== +.. automodule:: nova..tests.test_auth + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_cloud.rst b/doc/source/api/nova..tests.test_cloud.rst new file mode 100644 index 000000000..7bd03db9a --- /dev/null +++ b/doc/source/api/nova..tests.test_cloud.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_cloud` Module +============================================================================== +.. automodule:: nova..tests.test_cloud + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_compute.rst b/doc/source/api/nova..tests.test_compute.rst new file mode 100644 index 000000000..90fd6e9d1 --- /dev/null +++ b/doc/source/api/nova..tests.test_compute.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_compute` Module +============================================================================== +.. automodule:: nova..tests.test_compute + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_console.rst b/doc/source/api/nova..tests.test_console.rst new file mode 100644 index 000000000..f695f5d17 --- /dev/null +++ b/doc/source/api/nova..tests.test_console.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_console` Module +============================================================================== +.. automodule:: nova..tests.test_console + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_direct.rst b/doc/source/api/nova..tests.test_direct.rst new file mode 100644 index 000000000..4f7adef19 --- /dev/null +++ b/doc/source/api/nova..tests.test_direct.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_direct` Module +============================================================================== +.. automodule:: nova..tests.test_direct + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_flags.rst b/doc/source/api/nova..tests.test_flags.rst new file mode 100644 index 000000000..2ec35d6c2 --- /dev/null +++ b/doc/source/api/nova..tests.test_flags.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_flags` Module +============================================================================== +.. automodule:: nova..tests.test_flags + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_localization.rst b/doc/source/api/nova..tests.test_localization.rst new file mode 100644 index 000000000..d93c83ba7 --- /dev/null +++ b/doc/source/api/nova..tests.test_localization.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_localization` Module +============================================================================== +.. automodule:: nova..tests.test_localization + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_log.rst b/doc/source/api/nova..tests.test_log.rst new file mode 100644 index 000000000..04ff5ead1 --- /dev/null +++ b/doc/source/api/nova..tests.test_log.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_log` Module +============================================================================== +.. automodule:: nova..tests.test_log + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_middleware.rst b/doc/source/api/nova..tests.test_middleware.rst new file mode 100644 index 000000000..2f9df5832 --- /dev/null +++ b/doc/source/api/nova..tests.test_middleware.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_middleware` Module +============================================================================== +.. automodule:: nova..tests.test_middleware + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_misc.rst b/doc/source/api/nova..tests.test_misc.rst new file mode 100644 index 000000000..4975f89d7 --- /dev/null +++ b/doc/source/api/nova..tests.test_misc.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_misc` Module +============================================================================== +.. automodule:: nova..tests.test_misc + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_network.rst b/doc/source/api/nova..tests.test_network.rst new file mode 100644 index 000000000..3a4b04ea4 --- /dev/null +++ b/doc/source/api/nova..tests.test_network.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_network` Module +============================================================================== +.. automodule:: nova..tests.test_network + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_quota.rst b/doc/source/api/nova..tests.test_quota.rst new file mode 100644 index 000000000..24ebf9ca3 --- /dev/null +++ b/doc/source/api/nova..tests.test_quota.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_quota` Module +============================================================================== +.. automodule:: nova..tests.test_quota + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_rpc.rst b/doc/source/api/nova..tests.test_rpc.rst new file mode 100644 index 000000000..c141d6889 --- /dev/null +++ b/doc/source/api/nova..tests.test_rpc.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_rpc` Module +============================================================================== +.. automodule:: nova..tests.test_rpc + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_scheduler.rst b/doc/source/api/nova..tests.test_scheduler.rst new file mode 100644 index 000000000..1cd9991db --- /dev/null +++ b/doc/source/api/nova..tests.test_scheduler.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_scheduler` Module +============================================================================== +.. automodule:: nova..tests.test_scheduler + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_service.rst b/doc/source/api/nova..tests.test_service.rst new file mode 100644 index 000000000..a264fbb55 --- /dev/null +++ b/doc/source/api/nova..tests.test_service.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_service` Module +============================================================================== +.. automodule:: nova..tests.test_service + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_twistd.rst b/doc/source/api/nova..tests.test_twistd.rst new file mode 100644 index 000000000..cae0c0a28 --- /dev/null +++ b/doc/source/api/nova..tests.test_twistd.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_twistd` Module +============================================================================== +.. automodule:: nova..tests.test_twistd + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_virt.rst b/doc/source/api/nova..tests.test_virt.rst new file mode 100644 index 000000000..9b0dc1e46 --- /dev/null +++ b/doc/source/api/nova..tests.test_virt.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_virt` Module +============================================================================== +.. automodule:: nova..tests.test_virt + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_volume.rst b/doc/source/api/nova..tests.test_volume.rst new file mode 100644 index 000000000..b5affe53c --- /dev/null +++ b/doc/source/api/nova..tests.test_volume.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_volume` Module +============================================================================== +.. automodule:: nova..tests.test_volume + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_xenapi.rst b/doc/source/api/nova..tests.test_xenapi.rst new file mode 100644 index 000000000..7128baee4 --- /dev/null +++ b/doc/source/api/nova..tests.test_xenapi.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_xenapi` Module +============================================================================== +.. automodule:: nova..tests.test_xenapi + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.xenapi.stubs.rst b/doc/source/api/nova..tests.xenapi.stubs.rst new file mode 100644 index 000000000..356eed9a7 --- /dev/null +++ b/doc/source/api/nova..tests.xenapi.stubs.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.xenapi.stubs` Module +============================================================================== +.. automodule:: nova..tests.xenapi.stubs + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..twistd.rst b/doc/source/api/nova..twistd.rst new file mode 100644 index 000000000..d4145396d --- /dev/null +++ b/doc/source/api/nova..twistd.rst @@ -0,0 +1,6 @@ +The :mod:`nova..twistd` Module +============================================================================== +.. automodule:: nova..twistd + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..utils.rst b/doc/source/api/nova..utils.rst new file mode 100644 index 000000000..1131d1080 --- /dev/null +++ b/doc/source/api/nova..utils.rst @@ -0,0 +1,6 @@ +The :mod:`nova..utils` Module +============================================================================== +.. automodule:: nova..utils + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..version.rst b/doc/source/api/nova..version.rst new file mode 100644 index 000000000..4b0fc078f --- /dev/null +++ b/doc/source/api/nova..version.rst @@ -0,0 +1,6 @@ +The :mod:`nova..version` Module +============================================================================== +.. automodule:: nova..version + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.connection.rst b/doc/source/api/nova..virt.connection.rst new file mode 100644 index 000000000..caf766765 --- /dev/null +++ b/doc/source/api/nova..virt.connection.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.connection` Module +============================================================================== +.. automodule:: nova..virt.connection + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.disk.rst b/doc/source/api/nova..virt.disk.rst new file mode 100644 index 000000000..4a6c0f406 --- /dev/null +++ b/doc/source/api/nova..virt.disk.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.disk` Module +============================================================================== +.. automodule:: nova..virt.disk + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.fake.rst b/doc/source/api/nova..virt.fake.rst new file mode 100644 index 000000000..06ecdbf7d --- /dev/null +++ b/doc/source/api/nova..virt.fake.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.fake` Module +============================================================================== +.. automodule:: nova..virt.fake + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.hyperv.rst b/doc/source/api/nova..virt.hyperv.rst new file mode 100644 index 000000000..48d89378e --- /dev/null +++ b/doc/source/api/nova..virt.hyperv.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.hyperv` Module +============================================================================== +.. automodule:: nova..virt.hyperv + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.images.rst b/doc/source/api/nova..virt.images.rst new file mode 100644 index 000000000..4fdeb7af8 --- /dev/null +++ b/doc/source/api/nova..virt.images.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.images` Module +============================================================================== +.. automodule:: nova..virt.images + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.libvirt_conn.rst b/doc/source/api/nova..virt.libvirt_conn.rst new file mode 100644 index 000000000..7fb8aed5f --- /dev/null +++ b/doc/source/api/nova..virt.libvirt_conn.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.libvirt_conn` Module +============================================================================== +.. automodule:: nova..virt.libvirt_conn + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.xenapi.fake.rst b/doc/source/api/nova..virt.xenapi.fake.rst new file mode 100644 index 000000000..752dabb14 --- /dev/null +++ b/doc/source/api/nova..virt.xenapi.fake.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.xenapi.fake` Module +============================================================================== +.. automodule:: nova..virt.xenapi.fake + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.xenapi.network_utils.rst b/doc/source/api/nova..virt.xenapi.network_utils.rst new file mode 100644 index 000000000..15f52973e --- /dev/null +++ b/doc/source/api/nova..virt.xenapi.network_utils.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.xenapi.network_utils` Module +============================================================================== +.. automodule:: nova..virt.xenapi.network_utils + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.xenapi.vm_utils.rst b/doc/source/api/nova..virt.xenapi.vm_utils.rst new file mode 100644 index 000000000..18745dc71 --- /dev/null +++ b/doc/source/api/nova..virt.xenapi.vm_utils.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.xenapi.vm_utils` Module +============================================================================== +.. automodule:: nova..virt.xenapi.vm_utils + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.xenapi.vmops.rst b/doc/source/api/nova..virt.xenapi.vmops.rst new file mode 100644 index 000000000..30662c58d --- /dev/null +++ b/doc/source/api/nova..virt.xenapi.vmops.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.xenapi.vmops` Module +============================================================================== +.. automodule:: nova..virt.xenapi.vmops + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.xenapi.volume_utils.rst b/doc/source/api/nova..virt.xenapi.volume_utils.rst new file mode 100644 index 000000000..413e4dc4b --- /dev/null +++ b/doc/source/api/nova..virt.xenapi.volume_utils.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.xenapi.volume_utils` Module +============================================================================== +.. automodule:: nova..virt.xenapi.volume_utils + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.xenapi.volumeops.rst b/doc/source/api/nova..virt.xenapi.volumeops.rst new file mode 100644 index 000000000..626f164df --- /dev/null +++ b/doc/source/api/nova..virt.xenapi.volumeops.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.xenapi.volumeops` Module +============================================================================== +.. automodule:: nova..virt.xenapi.volumeops + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..virt.xenapi_conn.rst b/doc/source/api/nova..virt.xenapi_conn.rst new file mode 100644 index 000000000..14ac5147f --- /dev/null +++ b/doc/source/api/nova..virt.xenapi_conn.rst @@ -0,0 +1,6 @@ +The :mod:`nova..virt.xenapi_conn` Module +============================================================================== +.. automodule:: nova..virt.xenapi_conn + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..volume.api.rst b/doc/source/api/nova..volume.api.rst new file mode 100644 index 000000000..8ad36e049 --- /dev/null +++ b/doc/source/api/nova..volume.api.rst @@ -0,0 +1,6 @@ +The :mod:`nova..volume.api` Module +============================================================================== +.. automodule:: nova..volume.api + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..volume.driver.rst b/doc/source/api/nova..volume.driver.rst new file mode 100644 index 000000000..51f5c0729 --- /dev/null +++ b/doc/source/api/nova..volume.driver.rst @@ -0,0 +1,6 @@ +The :mod:`nova..volume.driver` Module +============================================================================== +.. automodule:: nova..volume.driver + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..volume.manager.rst b/doc/source/api/nova..volume.manager.rst new file mode 100644 index 000000000..91a192a8f --- /dev/null +++ b/doc/source/api/nova..volume.manager.rst @@ -0,0 +1,6 @@ +The :mod:`nova..volume.manager` Module +============================================================================== +.. automodule:: nova..volume.manager + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..volume.san.rst b/doc/source/api/nova..volume.san.rst new file mode 100644 index 000000000..1de068928 --- /dev/null +++ b/doc/source/api/nova..volume.san.rst @@ -0,0 +1,6 @@ +The :mod:`nova..volume.san` Module +============================================================================== +.. automodule:: nova..volume.san + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..wsgi.rst b/doc/source/api/nova..wsgi.rst new file mode 100644 index 000000000..0bff1c332 --- /dev/null +++ b/doc/source/api/nova..wsgi.rst @@ -0,0 +1,6 @@ +The :mod:`nova..wsgi` Module +============================================================================== +.. automodule:: nova..wsgi + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/runnova/binaries.rst b/doc/source/runnova/binaries.rst new file mode 100644 index 000000000..023831021 --- /dev/null +++ b/doc/source/runnova/binaries.rst @@ -0,0 +1,57 @@ +.. + Copyright 2010-2011 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. + +.. _binaries: + +Nova Daemons +============= + +The configuration of these binaries relies on "flagfiles" using the google +gflags package:: + + $ nova-xxxxx --flagfile flagfile + +The binaries can all run on the same machine or be spread out amongst multiple boxes in a large deployment. + +nova-api +-------- + +Nova api receives xml requests and sends them to the rest of the system. It is a wsgi app that routes and authenticate requests. It supports the ec2 and openstack apis. + +nova-objectstore +---------------- + +Nova objectstore is an ultra simple file-based storage system for images that replicates most of the S3 Api. It will soon be replaced with Glance (http://glance.openstack.org) and a simple image manager. + +nova-compute +------------ + +Nova compute is responsible for managing virtual machines. It loads a Service object which exposes the public methods on ComputeManager via rpc. + +nova-volume +----------- + +Nova volume is responsible for managing attachable block storage devices. It loads a Service object which exposes the public methods on VolumeManager via rpc. + +nova-network +------------ + +Nova network is responsible for managing floating and fixed ips, dhcp, bridging and vlans. It loads a Service object which exposes the public methods on one of the subclasses of NetworkManager. Different networking strategies are as simple as changing the network_manager flag:: + + $ nova-network --network_manager=nova.network.manager.FlatManager + +IMPORTANT: Make sure that you also set the network_manager on nova-api and nova_compute, since make some calls to network manager in process instead of through rpc. More information on the interactions between services, managers, and drivers can be found :ref:`here ` diff --git a/doc/source/runnova/euca2ools.rst b/doc/source/runnova/euca2ools.rst new file mode 100644 index 000000000..6f0c57358 --- /dev/null +++ b/doc/source/runnova/euca2ools.rst @@ -0,0 +1,49 @@ +Euca2ools +========= + +Nova is compatible with most of the euca2ools command line utilities. Both Administrators and Users will find these tools helpful for day-to-day administration. + +* euca-add-group +* euca-delete-bundle +* euca-describe-instances +* euca-register +* euca-add-keypair +* euca-delete-group +* euca-describe-keypairs +* euca-release-address +* euca-allocate-address +* euca-delete-keypair +* euca-describe-regions +* euca-reset-image-attribute +* euca-associate-address +* euca-delete-snapshot +* euca-describe-snapshots +* euca-revoke +* euca-attach-volume +* euca-delete-volume +* euca-describe-volumes +* euca-run-instances +* euca-authorize +* euca-deregister +* euca-detach-volume +* euca-terminate-instances +* euca-bundle-image +* euca-describe-addresses +* euca-disassociate-address +* euca-unbundle +* euca-bundle-vol +* euca-describe-availability-zones +* euca-download-bundle +* euca-upload-bundle +* euca-confirm-product-instance +* euca-describe-groups +* euca-get-console-output +* euca-version +* euca-create-snapshot +* euca-describe-image-attribute +* euca-modify-image-attribute +* euca-create-volume +* euca-describe-images +* euca-reboot-instances + + diff --git a/doc/source/runnova/flags.rst b/doc/source/runnova/flags.rst new file mode 100644 index 000000000..1bfa022d9 --- /dev/null +++ b/doc/source/runnova/flags.rst @@ -0,0 +1,193 @@ +.. + Copyright 2010-2011 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. + +Flags and Flagfiles +=================== + +Nova uses a configuration file containing flags located in /etc/nova/nova.conf. You can get the most recent listing of avaialble flags by running nova-(servicename) --help, for example, nova-api --help. + +Here's a list of available flags and their default settings. + + --ajax_console_proxy_port: port that ajax_console_proxy binds + (default: '8000') + --ajax_console_proxy_topic: the topic ajax proxy nodes listen on + (default: 'ajax_proxy') + --ajax_console_proxy_url: location of ajax console proxy, in the form + "http://127.0.0.1:8000" + (default: 'http://127.0.0.1:8000') + --auth_token_ttl: Seconds for auth tokens to linger + (default: '3600') + (an integer) + --aws_access_key_id: AWS Access ID + (default: 'admin') + --aws_secret_access_key: AWS Access Key + (default: 'admin') + --compute_manager: Manager for compute + (default: 'nova.compute.manager.ComputeManager') + --compute_topic: the topic compute nodes listen on + (default: 'compute') + --connection_type: libvirt, xenapi or fake + (default: 'libvirt') + --console_manager: Manager for console proxy + (default: 'nova.console.manager.ConsoleProxyManager') + --console_topic: the topic console proxy nodes listen on + (default: 'console') + --control_exchange: the main exchange to connect to + (default: 'nova') + --db_backend: The backend to use for db + (default: 'sqlalchemy') + --default_image: default image to use, testing only + (default: 'ami-11111') + --default_instance_type: default instance type to use, testing only + (default: 'm1.small') + --default_log_levels: list of logger=LEVEL pairs + (default: 'amqplib=WARN,sqlalchemy=WARN,eventlet.wsgi.server=WARN') + (a comma separated list) + --default_project: default project for openstack + (default: 'openstack') + --ec2_dmz_host: internal ip of api server + (default: '$my_ip') + --ec2_host: ip of api server + (default: '$my_ip') + --ec2_path: suffix for ec2 + (default: '/services/Cloud') + --ec2_port: cloud controller port + (default: '8773') + (an integer) + --ec2_scheme: prefix for ec2 + (default: 'http') + --[no]enable_new_services: Services to be added to the available pool on + create + (default: 'true') + --[no]fake_network: should we use fake network devices and addresses + (default: 'false') + --[no]fake_rabbit: use a fake rabbit + (default: 'false') + --glance_host: glance host + (default: '$my_ip') + --glance_port: glance port + (default: '9292') + (an integer) + -?,--[no]help: show this help + --[no]helpshort: show usage only for this module + --[no]helpxml: like --help, but generates XML output + --host: name of this node + (default: 'osdemo03') + --image_service: The service to use for retrieving and searching for images. + (default: 'nova.image.s3.S3ImageService') + --instance_name_template: Template string to be used to generate instance + names + (default: 'instance-%08x') + --logfile: output to named file + --logging_context_format_string: format string to use for log messages with + context + (default: '%(asctime)s %(levelname)s %(name)s [%(request_id)s %(user)s + %(project)s] %(message)s') + --logging_debug_format_suffix: data to append to log format when level is + DEBUG + (default: 'from %(processName)s (pid=%(process)d) %(funcName)s + %(pathname)s:%(lineno)d') + --logging_default_format_string: format string to use for log messages without + context + (default: '%(asctime)s %(levelname)s %(name)s [-] %(message)s') + --logging_exception_prefix: prefix each line of exception output with this + format + (default: '(%(name)s): TRACE: ') + --my_ip: host ip address + (default: '184.106.73.68') + --network_manager: Manager for network + (default: 'nova.network.manager.VlanManager') + --network_topic: the topic network nodes listen on + (default: 'network') + --node_availability_zone: availability zone of this node + (default: 'nova') + --null_kernel: kernel image that indicates not to use a kernel, but to use a + raw disk image instead + (default: 'nokernel') + --osapi_host: ip of api server + (default: '$my_ip') + --osapi_path: suffix for openstack + (default: '/v1.0/') + --osapi_port: OpenStack API port + (default: '8774') + (an integer) + --osapi_scheme: prefix for openstack + (default: 'http') + --periodic_interval: seconds between running periodic tasks + (default: '60') + (a positive integer) + --pidfile: pidfile to use for this service + --rabbit_host: rabbit host + (default: 'localhost') + --rabbit_max_retries: rabbit connection attempts + (default: '12') + (an integer) + --rabbit_password: rabbit password + (default: 'guest') + --rabbit_port: rabbit port + (default: '5672') + (an integer) + --rabbit_retry_interval: rabbit connection retry interval + (default: '10') + (an integer) + --rabbit_userid: rabbit userid + (default: 'guest') + --rabbit_virtual_host: rabbit virtual host + (default: '/') + --region_list: list of region=fqdn pairs separated by commas + (default: '') + (a comma separated list) + --report_interval: seconds between nodes reporting state to datastore + (default: '10') + (a positive integer) + --s3_dmz: s3 dmz ip (for instances) + (default: '$my_ip') + --s3_host: s3 host (for infrastructure) + (default: '$my_ip') + --s3_port: s3 port + (default: '3333') + (an integer) + --scheduler_manager: Manager for scheduler + (default: 'nova.scheduler.manager.SchedulerManager') + --scheduler_topic: the topic scheduler nodes listen on + (default: 'scheduler') + --sql_connection: connection string for sql database + (default: 'sqlite:///$state_path/nova.sqlite') + --sql_idle_timeout: timeout for idle sql database connections + (default: '3600') + --sql_max_retries: sql connection attempts + (default: '12') + (an integer) + --sql_retry_interval: sql connection retry interval + (default: '10') + (an integer) + --state_path: Top-level directory for maintaining nova's state + (default: '/usr/lib/pymodules/python2.6/nova/../') + --[no]use_syslog: output to syslog + (default: 'false') + --[no]verbose: show debug output + (default: 'false') + --volume_manager: Manager for volume + (default: 'nova.volume.manager.VolumeManager') + --volume_name_template: Template string to be used to generate instance names + (default: 'volume-%08x') + --volume_topic: the topic volume nodes listen on + (default: 'volume') + --vpn_image_id: AMI for cloudpipe vpn server + (default: 'ami-cloudpipe') + --vpn_key_suffix: Suffix to add to project name for vpn key and secgroups + (default: '-vpn') \ No newline at end of file diff --git a/doc/source/runnova/getting.started.rst b/doc/source/runnova/getting.started.rst new file mode 100644 index 000000000..4cc7307b0 --- /dev/null +++ b/doc/source/runnova/getting.started.rst @@ -0,0 +1,168 @@ +.. + Copyright 2010-2011 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. + +Getting Started with Nova +========================= + +This code base is continually changing, so dependencies also change. If you +encounter any problems, see the :doc:`../community` page. +The `contrib/nova.sh` script should be kept up to date, and may be a good +resource to review when debugging. + +The purpose of this document is to get a system installed that you can use to +test your setup assumptions. Working from this base installtion you can +tweak configurations and work with different flags to monitor interaction with +your hardware, network, and other factors that will allow you to determine +suitability for your deployment. After following this setup method, you should +be able to experiment with different managers, drivers, and flags to get the +best performance. + +Dependencies +------------ + +Related servers we rely on + +* **RabbitMQ**: messaging queue, used for all communication between components + +Optional servers + +* **OpenLDAP**: By default, the auth server uses the RDBMS-backed datastore by + setting FLAGS.auth_driver to `nova.auth.dbdriver.DbDriver`. But OpenLDAP + (or LDAP) could be configured by specifying `nova.auth.ldapdriver.LdapDriver`. + There is a script in the sources (`nova/auth/slap.sh`) to install a very basic + openldap server on ubuntu. +* **ReDIS**: There is a fake ldap auth driver + `nova.auth.ldapdriver.FakeLdapDriver` that backends to redis. This was + created for testing ldap implementation on systems that don't have an easy + means to install ldap. +* **MySQL**: Either MySQL or another database supported by sqlalchemy needs to + be avilable. Currently, only sqlite3 an mysql have been tested. + +Python libraries that we use (from pip-requires): + +.. literalinclude:: ../../../tools/pip-requires + +Other libraries: + +* **XenAPI**: Needed only for Xen Cloud Platform or XenServer support. Available + from http://wiki.xensource.com/xenwiki/XCP_SDK or + http://community.citrix.com/cdn/xs/sdks. + +External unix tools that are required: + +* iptables +* ebtables +* gawk +* curl +* kvm +* libvirt +* dnsmasq +* vlan +* open-iscsi and iscsitarget (if you use iscsi volumes) +* aoetools and vblade-persist (if you use aoe-volumes) + +Nova uses cutting-edge versions of many packages. There are ubuntu packages in +the nova-core trunk ppa. You can use add this ppa to your sources list on an +ubuntu machine with the following commands:: + + sudo apt-get install -y python-software-properties + sudo add-apt-repository ppa:nova-core/trunk + +Recommended +----------- + +* euca2ools: python implementation of aws ec2-tools and ami tools +* build tornado to use C module for evented section + + +Installation +-------------- + +You can install from packages for your particular Linux distribution if they are +available. Otherwise you can install from source by checking out the source +files from the `Nova Source Code Repository `_ +and running:: + + python setup.py install + +Configuration +--------------- + +Configuring the host system +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Nova can be configured in many different ways. In this "Getting Started with Nova" document, we only provide what you need to get started as quickly as possible. For a more detailed description of system +configuration, start reading through `Installing and Configuring OpenStack Compute `_. + +`Detailed instructions for creating a volume group are available `_, or use these quick instructions. + +* Create a volume group (you can use an actual disk for the volume group as + well):: + + # This creates a 1GB file to create volumes out of + dd if=/dev/zero of=MY_FILE_PATH bs=100M count=10 + losetup --show -f MY_FILE_PATH + # replace /dev/loop0 below with whatever losetup returns + # nova-volumes is the default for the --volume_group flag + vgcreate nova-volumes /dev/loop0 + + +Configuring Nova +~~~~~~~~~~~~~~~~ + +Configuration of the entire system is performed through python-gflags. The +best way to track configuration is through the use of a flagfile. + +A flagfile is specified with the ``--flagfile=FILEPATH`` argument to the binary +when you launch it. Flagfiles for nova are typically stored in +``/etc/nova/nova.conf``, and flags specific to a certain program are stored in +``/etc/nova/nova-COMMAND.conf``. Each configuration file can include another +flagfile, so typically a file like ``nova-manage.conf`` would have as its first +line ``--flagfile=/etc/nova/nova.conf`` to load the common flags before +specifying overrides or additional options. + +To get a current comprehensive list of flag file options, run bin/nova- --help, or refer to a static list at `Reference for Flags in nova.conf `_. + +A sample configuration to test the system follows:: + + --verbose + --nodaemon + --auth_driver=nova.auth.dbdriver.DbDriver + +Running +------- + +There are many parts to the nova system, each with a specific function. They +are built to be highly-available, so there are may configurations they can be +run in (ie: on many machines, many listeners per machine, etc). This part +of the guide only gets you started quickly, to learn about HA options, see +`Installing and Configuring OpenStack Compute `_. + +Launch supporting services + +* rabbitmq +* redis (optional) +* mysql (optional) +* openldap (optional) + +Launch nova components, each should have ``--flagfile=/etc/nova/nova.conf`` + +* nova-api +* nova-compute +* nova-objectstore +* nova-volume +* nova-scheduler diff --git a/doc/source/runnova/index.rst b/doc/source/runnova/index.rst new file mode 100644 index 000000000..283d268ce --- /dev/null +++ b/doc/source/runnova/index.rst @@ -0,0 +1,90 @@ +.. + Copyright 2010-2011 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. + +Running Nova +============ + +This guide describes the basics of running and managing Nova. For more administrator's documentation, refer to `docs.openstack.org `_. + +Running the Cloud +----------------- + +The fastest way to get a test cloud running is by following the directions in the :doc:`../quickstart`. It relies on a nova.sh script to run on a single machine. + +Nova's cloud works via the interaction of a series of daemon processes that reside persistently on the host machine(s). Fortunately, the :doc:`../quickstart` process launches sample versions of all these daemons for you. Once you are familiar with basic Nova usage, you can learn more about daemons by reading :doc:`../service.architecture` and :doc:`binaries`. + +Administration Utilities +------------------------ + +There are two main tools that a system administrator will find useful to manage their Nova cloud: + +.. toctree:: + :maxdepth: 1 + + nova.manage + euca2ools + +The nova-manage command may only be run by users with admin priviledges. Commands for euca2ools can be used by all users, though specific commands may be restricted by Role Based Access Control. You can read more about creating and managing users in :doc:`managing.users` + +User and Resource Management +---------------------------- + +The nova-manage and euca2ools commands provide the basic interface to perform a broad range of administration functions. In this section, you can read more about how to accomplish specific administration tasks. + +For background on the core objects referenced in this section, see :doc:`../object.model` + +.. toctree:: + :maxdepth: 1 + + managing.users + managing.projects + managing.instances + managing.images + managing.volumes + managing.networks + +Deployment +---------- + +For a starting multi-node architecture, you would start with two nodes - a cloud controller node and a compute node. The cloud controller node contains the nova- services plus the Nova database. The compute node installs all the nova-services but then refers to the database installation, which is hosted by the cloud controller node. Ensure that the nova.conf file is identical on each node. If you find performance issues not related to database reads or writes, but due to the messaging queue backing up, you could add additional messaging services (rabbitmq). For instructions on multi-server installations, refer to `Installing and Configuring OpenStack Compute `_. + + +.. toctree:: + :maxdepth: 1 + + dbsync + + +Networking +^^^^^^^^^^ + +.. toctree:: + :maxdepth: 1 + + network.vlan.rst + network.flat.rst + + +Advanced Topics +--------------- + +.. toctree:: + :maxdepth: 1 + + flags + monitoring + diff --git a/doc/source/runnova/managing.images.rst b/doc/source/runnova/managing.images.rst new file mode 100644 index 000000000..c5d93a6e8 --- /dev/null +++ b/doc/source/runnova/managing.images.rst @@ -0,0 +1,21 @@ +.. + Copyright 2010-2011 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. + +Managing Images +=============== + +.. todo:: Put info on managing images here! diff --git a/doc/source/runnova/managing.instances.rst b/doc/source/runnova/managing.instances.rst new file mode 100644 index 000000000..e62352017 --- /dev/null +++ b/doc/source/runnova/managing.instances.rst @@ -0,0 +1,59 @@ +.. + Copyright 2010-2011 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. + +Managing Instances +================== + +Keypairs +-------- + +Images can be shared by many users, so it is dangerous to put passwords into the images. Nova therefore supports injecting ssh keys into instances before they are booted. This allows a user to login to the instances that he or she creates securely. Generally the first thing that a user does when using the system is create a keypair. Nova generates a public and private key pair, and sends the private key to the user. The public key is stored so that it can be injected into instances. + +Keypairs are created through the api. They can be created on the command line using the euca2ools script euca-add-keypair. Refer to the man page for the available options. Example usage:: + + euca-add-keypair test > test.pem + chmod 600 test.pem + euca-run-instances -k test -t m1.tiny ami-tiny + # wait for boot + ssh -i test.pem root@ip.of.instance + + +Basic Management +---------------- +Instance management can be accomplished with euca commands: + + +To run an instance: + +:: + + euca-run-instances + + +To terminate an instance: + +:: + + euca-terminate-instances + +To reboot an instance: + +:: + + euca-reboot-instances + +See the euca2ools documentation for more information diff --git a/doc/source/runnova/managing.networks.rst b/doc/source/runnova/managing.networks.rst new file mode 100644 index 000000000..9eea46d70 --- /dev/null +++ b/doc/source/runnova/managing.networks.rst @@ -0,0 +1,70 @@ +.. + Copyright 2010-2011 United States Government as represented by the + Administrator of the National Aeronautics and Space Administration. + Overview Sections Copyright 2010-2011 Citrix + 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. + +Networking Overview +=================== +In Nova, users organize their cloud resources in projects. A Nova project consists of a number of VM instances created by a user. For each VM instance, Nova assigns to it a private IP address. (Currently, Nova only supports Linux bridge networking that allows the virtual interfaces to connect to the outside network through the physical interface. Other virtual network technologies, such as Open vSwitch, could be supported in the future.) The Network Controller provides virtual networks to enable compute servers to interact with each other and with the public network. + +Nova Network Strategies +----------------------- + +Currently, Nova supports three kinds of networks, implemented in three "Network Manager" types respectively: Flat Network Manager, Flat DHCP Network Manager, and VLAN Network Manager. The three kinds of networks can co-exist in a cloud system. However, the scheduler for selecting the type of network for a given project is not yet implemented. Here is a brief description of each of the different network strategies, with a focus on the VLAN Manager in a separate section. + +Read more about Nova network strategies here: + +.. toctree:: + :maxdepth: 1 + + network.flat.rst + network.vlan.rst + + +Network Management Commands +--------------------------- + +Admins and Network Administrators can use the 'nova-manage' command to manage network resources: + +VPN Management +~~~~~~~~~~~~~~ + +* vpn list: Print a listing of the VPNs for all projects. + * arguments: none +* vpn run: Start the VPN for a given project. + * arguments: project +* vpn spawn: Run all VPNs. + * arguments: none + + +Floating IP Management +~~~~~~~~~~~~~~~~~~~~~~ + +* floating create: Creates floating ips for host by range + * arguments: host ip_range +* floating delete: Deletes floating ips by range + * arguments: range +* floating list: Prints a listing of all floating ips + * arguments: none + +Network Management +~~~~~~~~~~~~~~~~~~ + +* network create: Creates fixed ips for host by range + * arguments: [fixed_range=FLAG], [num_networks=FLAG], + [network_size=FLAG], [vlan_start=FLAG], + [vpn_start=FLAG] + diff --git a/doc/source/runnova/managing.projects.rst b/doc/source/runnova/managing.projects.rst new file mode 100644 index 000000000..5dd7f2de9 --- /dev/null +++ b/doc/source/runnova/managing.projects.rst @@ -0,0 +1,68 @@ +.. + Copyright 2010-2011 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. + +Managing Projects +================= + +Projects are isolated resource containers forming the principal organizational structure within Nova. They consist of a separate vlan, volumes, instances, images, keys, and users. + +Although the original ec2 api only supports users, nova adds the concept of projects. A user can specify which project he or she wishes to use by appending `:project_id` to his or her access key. If no project is specified in the api request, nova will attempt to use a project with the same id as the user. + +The api will return NotAuthorized if a normal user attempts to make requests for a project that he or she is not a member of. Note that admins or users with special admin roles skip this check and can make requests for any project. + +To create a project, use the `project create` command of nova-manage. The syntax is nova-manage project create projectname manager_id [description] You must specify a projectname and a manager_id. For example:: + nova-manage project create john_project john "This is a sample project" + +You can add and remove users from projects with `project add` and `project remove`:: + nova-manage project add john_project john + nova-manage project remove john_project john + +Project Commands +---------------- + +Admins and Project Managers can use the 'nova-manage project' command to manage project resources: + +* project add: Adds user to project + * arguments: project user +* project create: Creates a new project + * arguments: name project_manager [description] +* project delete: Deletes an existing project + * arguments: project_id +* project environment: Exports environment variables to an sourcable file + * arguments: project_id user_id [filename='novarc] +* project list: lists all projects + * arguments: none +* project remove: Removes user from project + * arguments: project user +* project scrub: Deletes data associated with project + * arguments: project +* project zipfile: Exports credentials for project to a zip file + * arguments: project_id user_id [filename='nova.zip] + +Setting Quotas +-------------- +Nova utilizes a quota system at the project level to control resource consumption across available hardware resources. Current quota controls are available to limit the: + +* Number of volumes which may be created +* Total size of all volumes within a project as measured in GB +* Number of instances which may be launched +* Number of processor cores which may be allocated +* Publicly accessible IP addresses + +Use the following command to set quotas for a project +* project quota: Set or display quotas for project + * arguments: project_id [key] [value] diff --git a/doc/source/runnova/managing.users.rst b/doc/source/runnova/managing.users.rst new file mode 100644 index 000000000..392142e86 --- /dev/null +++ b/doc/source/runnova/managing.users.rst @@ -0,0 +1,82 @@ +Managing Users +============== + + +Users and Access Keys +--------------------- + +Access to the ec2 api is controlled by an access and secret key. The user's access key needs to be included in the request, and the request must be signed with the secret key. Upon receipt of api requests, nova will verify the signature and execute commands on behalf of the user. + +In order to begin using nova, you will need a to create a user. This can be easily accomplished using the user create or user admin commands in nova-manage. `user create` will create a regular user, whereas `user admin` will create an admin user. The syntax of the command is nova-manage user create username [access] [secret]. For example:: + + nova-manage user create john my-access-key a-super-secret-key + +If you do not specify an access or secret key, a random uuid will be created automatically. + +Credentials +----------- + +Nova can generate a handy set of credentials for a user. These credentials include a CA for bundling images and a file for setting environment variables to be used by euca2ools. If you don't need to bundle images, just the environment script is required. You can export one with the `project environment` command. The syntax of the command is nova-manage project environment project_id user_id [filename]. If you don't specify a filename, it will be exported as novarc. After generating the file, you can simply source it in bash to add the variables to your environment:: + + nova-manage project environment john_project john + . novarc + +If you do need to bundle images, you will need to get all of the credentials using `project zipfile`. Note that zipfile will give you an error message if networks haven't been created yet. Otherwise zipfile has the same syntax as environment, only the default file name is nova.zip. Example usage:: + + nova-manage project zipfile john_project john + unzip nova.zip + . novarc + +Role Based Access Control +------------------------- +Roles control the api actions that a user is allowed to perform. For example, a user cannot allocate a public ip without the `netadmin` role. It is important to remember that a users de facto permissions in a project is the intersection of user (global) roles and project (local) roles. So for john to have netadmin permissions in his project, he needs to separate roles specified. You can add roles with `role add`. The syntax is nova-manage role add user_id role [project_id]. Let's give john the netadmin role for his project:: + + nova-manage role add john netadmin + nova-manage role add john netadmin john_project + +Role-based access control (RBAC) is an approach to restricting system access to authorized users based on an individual’s role within an organization. Various employee functions require certain levels of system access in order to be successful. These functions are mapped to defined roles and individuals are categorized accordingly. Since users are not assigned permissions directly, but only acquire them through their role (or roles), management of individual user rights becomes a matter of assigning appropriate roles to the user. This simplifies common operations, such as adding a user, or changing a user's department. + +Nova’s rights management system employs the RBAC model and currently supports the following five roles: + +* **Cloud Administrator.** (admin) Users of this class enjoy complete system access. +* **IT Security.** (itsec) This role is limited to IT security personnel. It permits role holders to quarantine instances. +* **Project Manager.** (projectmanager)The default for project owners, this role affords users the ability to add other users to a project, interact with project images, and launch and terminate instances. +* **Network Administrator.** (netadmin) Users with this role are permitted to allocate and assign publicly accessible IP addresses as well as create and modify firewall rules. +* **Developer.** This is a general purpose role that is assigned to users by default. + +RBAC management is exposed through the dashboard for simplified user management. + + +User Commands +~~~~~~~~~~~~ + +Users, including admins, are created through the ``user`` commands. + +* user admin: creates a new admin and prints exports + * arguments: name [access] [secret] +* user create: creates a new user and prints exports + * arguments: name [access] [secret] +* user delete: deletes an existing user + * arguments: name +* user exports: prints access and secrets for user in export format + * arguments: name +* user list: lists all users + * arguments: none +* user modify: update a users keys & admin flag + * arguments: accesskey secretkey admin + * leave any field blank to ignore it, admin should be 'T', 'F', or blank + + +User Role Management +~~~~~~~~~~~~~~~~~~~~ + +* role add: adds role to user + * if project is specified, adds project specific role + * arguments: user, role [project] +* role has: checks to see if user has role + * if project is specified, returns True if user has + the global role and the project role + * arguments: user, role [project] +* role remove: removes role from user + * if project is specified, removes project specific role + * arguments: user, role [project] diff --git a/doc/source/runnova/managingsecurity.rst b/doc/source/runnova/managingsecurity.rst new file mode 100644 index 000000000..7893925e7 --- /dev/null +++ b/doc/source/runnova/managingsecurity.rst @@ -0,0 +1,39 @@ +.. + Copyright 2010-2011 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. + +Security Considerations +======================= + +.. todo:: This doc is vague and just high-level right now. Describe architecture that enables security. + +The goal of securing a cloud computing system involves both protecting the instances, data on the instances, and +ensuring users are authenticated for actions and that borders are understood by the users and the system. +Protecting the system from intrusion or attack involves authentication, network protections, and +compromise detection. + +Key Concepts +------------ + +Authentication - Each instance is authenticated with a key pair. + +Network - Instances can communicate with each other but you can configure the boundaries through firewall +configuration. + +Monitoring - Log all API commands and audit those logs. + +Encryption - Data transfer between instances is not encrypted. + diff --git a/doc/source/runnova/monitoring.rst b/doc/source/runnova/monitoring.rst new file mode 100644 index 000000000..2c93c71b5 --- /dev/null +++ b/doc/source/runnova/monitoring.rst @@ -0,0 +1,27 @@ +.. + Copyright 2010-2011 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. + +Monitoring +========== + +* components +* throughput +* exceptions +* hardware + +* ganglia +* syslog diff --git a/doc/source/runnova/network.flat.rst b/doc/source/runnova/network.flat.rst new file mode 100644 index 000000000..3d8680c6f --- /dev/null +++ b/doc/source/runnova/network.flat.rst @@ -0,0 +1,60 @@ +.. + Copyright 2010-2011 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. + + +Flat Network Mode (Original and Flat) +===================================== + +Flat network mode removes most of the complexity of VLAN mode by simply +bridging all instance interfaces onto a single network. + +There are two variations of flat mode that differ mostly in how IP addresses +are given to instances. + + +Original Flat Mode +------------------ +IP addresses for VM instances are grabbed from a subnet specified by the network administrator, and injected into the image on launch. All instances of the system are attached to the same Linux networking bridge, configured manually by the network administrator both on the network controller hosting the network and on the computer controllers hosting the instances. To recap: + +* Each compute host creates a single bridge for all instances to use to attach to the external network. +* The networking configuration is injected into the instance before it is booted or it is obtained by a guest agent installed in the instance. + +Note that the configuration injection currently only works on linux-style systems that keep networking +configuration in /etc/network/interfaces. + + +Flat DHCP Mode +-------------- +IP addresses for VM instances are grabbed from a subnet specified by the network administrator. Similar to the flat network, a single Linux networking bridge is created and configured manually by the network administrator and used for all instances. A DHCP server is started to pass out IP addresses to VM instances from the specified subnet. To recap: + +* Like flat mode, all instances are attached to a single bridge on the compute node. +* In addition a DHCP server is running to configure instances. + +Implementation +-------------- + +The network nodes do not act as a default gateway in flat mode. Instances +are given public IP addresses. + +Compute nodes have iptables/ebtables entries created per project and +instance to protect against IP/MAC address spoofing and ARP poisoning. + + +Examples +-------- + +.. todo:: add flat network mode configuration examples diff --git a/doc/source/runnova/network.vlan.rst b/doc/source/runnova/network.vlan.rst new file mode 100644 index 000000000..c06ce8e8b --- /dev/null +++ b/doc/source/runnova/network.vlan.rst @@ -0,0 +1,179 @@ +.. + Copyright 2010-2011 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. + + +VLAN Network Mode +================= +VLAN Network Mode is the default mode for Nova. It provides a private network +segment for each project's instances that can be accessed via a dedicated +VPN connection from the Internet. + +In this mode, each project gets its own VLAN, Linux networking bridge, and subnet. The subnets are specified by the network administrator, and are assigned dynamically to a project when required. A DHCP Server is started for each VLAN to pass out IP addresses to VM instances from the subnet assigned to the project. All instances belonging to one project are bridged into the same VLAN for that project. The Linux networking bridges and VLANs are created by Nova when required, described in more detail in Nova VLAN Network Management Implementation. + +.. + (this text revised above) + Because the flat network and flat DhCP network are simple to understand and yet do not scale well enough for real-world cloud systems, this section focuses on the VLAN network implementation by the VLAN Network Manager. + + + In the VLAN network mode, all the VM instances of a project are connected together in a VLAN with the specified private subnet. Each running VM instance is assigned an IP address within the given private subnet. + +.. image:: /images/Novadiagram.png + :width: 790 + +While network traffic between VM instances belonging to the same VLAN is always open, Nova can enforce isolation of network traffic between different projects by enforcing one VLAN per project. + +In addition, the network administrator can specify a pool of public IP addresses that users may allocate and then assign to VMs, either at boot or dynamically at run-time. This capability is similar to Amazon's 'elastic IPs'. A public IP address may be associated with a running instances, allowing the VM instance to be accessed from the public network. The public IP addresses are accessible from the network host and NATed to the private IP address of the project. + +.. todo:: Describe how a public IP address could be associated with a project (a VLAN) + +This is the default networking mode and supports the most features. For multiple machine installation, it requires a switch that supports host-managed vlan tagging. In this mode, nova will create a vlan and bridge for each project. The project gets a range of private ips that are only accessible from inside the vlan. In order for a user to access the instances in their project, a special vpn instance (code named :ref:`cloudpipe `) needs to be created. Nova generates a certificate and key for the user to access the vpn and starts the vpn automatically. More information on cloudpipe can be found :ref:`here `. + +The following diagram illustrates how the communication that occurs between the vlan (the dashed box) and the public internet (represented by the two clouds) + +.. image:: /images/cloudpipe.png + :width: 100% + +Goals +----- + +For our implementation of Nova, our goal is that each project is in a protected network segment. Here are the specifications we keep in mind for meeting this goal. + + * RFC-1918 IP space + * public IP via NAT + * no default inbound Internet access without public NAT + * limited (project-admin controllable) outbound Internet access + * limited (project-admin controllable) access to other project segments + * all connectivity to instance and cloud API is via VPN into the project segment + +We also keep as a goal a common DMZ segment for support services, meaning these items are only visible from project segment: + + * metadata + * dashboard + +Limitations +----------- + +We kept in mind some of these limitations: + +* Projects / cluster limited to available VLANs in switching infrastructure +* Requires VPN for access to project segment + +Implementation +-------------- +Currently Nova segregates project VLANs using 802.1q VLAN tagging in the +switching layer. Compute hosts create VLAN-specific interfaces and bridges +as required. + +The network nodes act as default gateway for project networks and contain +all of the routing and firewall rules implementing security groups. The +network node also handles DHCP to provide instance IPs for each project. + +VPN access is provided by running a small instance called CloudPipe +on the IP immediately following the gateway IP for each project. The +network node maps a dedicated public IP/port to the CloudPipe instance. + +Compute nodes have per-VLAN interfaces and bridges created as required. +These do NOT have IP addresses in the host to protect host access. +Compute nodes have iptables/ebtables entries created per project and +instance to protect against IP/MAC address spoofing and ARP poisoning. + +The network assignment to a project, and IP address assignment to a VM instance, are triggered when a user starts to run a VM instance. When running a VM instance, a user needs to specify a project for the instances, and the security groups (described in Security Groups) when the instance wants to join. If this is the first instance to be created for the project, then Nova (the cloud controller) needs to find a network controller to be the network host for the project; it then sets up a private network by finding an unused VLAN id, an unused subnet, and then the controller assigns them to the project, it also assigns a name to the project's Linux bridge (br100 stored in the Nova database), and allocating a private IP within the project's subnet for the new instance. + +If the instance the user wants to start is not the project's first, a subnet and a VLAN must have already been assigned to the project; therefore the system needs only to find an available IP address within the subnet and assign it to the new starting instance. If there is no private IP available within the subnet, an exception will be raised to the cloud controller, and the VM creation cannot proceed. + + +External Infrastructure +----------------------- + +Nova assumes the following is available: + +* DNS +* NTP +* Internet connectivity + + +Example +------- + +This example network configuration demonstrates most of the capabilities +of VLAN Mode. It splits administrative access to the nodes onto a dedicated +management network and uses dedicated network nodes to handle all +routing and gateway functions. + +It uses a 10GB network for instance traffic and a 1GB network for management. + + +Hardware +~~~~~~~~ + +* All nodes have a minimum of two NICs for management and production. + + * management is 1GB + * production is 10GB + * add additional NICs for bonding or HA/performance + +* network nodes should have an additional NIC dedicated to public Internet traffic +* switch needs to support enough simultaneous VLANs for number of projects +* production network configured as 802.1q trunk on switch + + +Operation +~~~~~~~~~ + +The network node controls the project network configuration: + +* assigns each project a VLAN and private IP range +* starts dnsmasq on project VLAN to serve private IP range +* configures iptables on network node for default project access +* launches CloudPipe instance and configures iptables access + +When starting an instance the network node: + +* sets up a VLAN interface and bridge on each host as required when an + instance is started on that host +* assigns private IP to instance +* generates MAC address for instance +* update dnsmasq with IP/MAC for instance + +When starting an instance the compute node: + +* sets up a VLAN interface and bridge on each host as required when an + instance is started on that host + + +Setup +~~~~~ + +* Assign VLANs in the switch: + + * public Internet segment + * production network + * management network + * cluster DMZ + +* Assign a contiguous range of VLANs to Nova for project use. +* Configure management NIC ports as management VLAN access ports. +* Configure management VLAN with Internet access as required +* Configure production NIC ports as 802.1q trunk ports. +* Configure Nova (need to add specifics here) + + * public IPs + * instance IPs + * project network size + * DMZ network + +.. todo:: need specific Nova configuration added diff --git a/doc/source/runnova/nova.manage.rst b/doc/source/runnova/nova.manage.rst new file mode 100644 index 000000000..0e9a29b6b --- /dev/null +++ b/doc/source/runnova/nova.manage.rst @@ -0,0 +1,239 @@ +.. + Copyright 2010-2011 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. + + +The nova-manage command +======================= + +Introduction +~~~~~~~~~~~~ + +The nova-manage command is used to perform many essential functions for +administration and ongoing maintenance of nova, such as user creation, +vpn management, and much more. + +The standard pattern for executing a nova-manage command is: +``nova-manage []`` + +For example, to obtain a list of all projects: +``nova-manage project list`` + +Run without arguments to see a list of available command categories: +``nova-manage`` + +Categories are user, project, role, shell, vpn, and floating. Detailed descriptions are below. + +You can also run with a category argument such as user to see a list of all commands in that category: +``nova-manage user`` + +These sections describe the available categories and arguments for nova-manage. + +Nova Db +~~~~~~~ + +``nova-manage db version`` + + Print the current database version. + +``nova-manage db sync`` + + Sync the database up to the most recent version. This is the standard way to create the db as well. + +Nova User +~~~~~~~~~ + +``nova-manage user admin `` + + Create an admin user with the name . + +``nova-manage user create `` + + Create a normal user with the name . + +``nova-manage user delete `` + + Delete the user with the name . + +``nova-manage user exports `` + + Outputs a list of access key and secret keys for user to the screen + +``nova-manage user list`` + + Outputs a list of all the user names to the screen. + +``nova-manage user modify `` + + Updates the indicated user keys, indicating with T or F if the user is an admin user. Leave any argument blank if you do not want to update it. + +Nova Project +~~~~~~~~~~~~ + +``nova-manage project add `` + + Add a nova project with the name to the database. + +``nova-manage project create `` + + Create a new nova project with the name (you still need to do nova-manage project add to add it to the database). + +``nova-manage project delete `` + + Delete a nova project with the name . + +``nova-manage project environment `` + + Exports environment variables for the named project to a file named novarc. + +``nova-manage project list`` + + Outputs a list of all the projects to the screen. + +``nova-manage project quota `` + + Outputs the size and specs of the project's instances including gigabytes, instances, floating IPs, volumes, and cores. + +``nova-manage project remove `` + + Deletes the project with the name . + +``nova-manage project zipfile`` + + Compresses all related files for a created project into a zip file nova.zip. + +Nova Role +~~~~~~~~~ + +nova-manage role [] +``nova-manage role add <(optional) projectname>`` + + Add a user to either a global or project-based role with the indicated assigned to the named user. Role names can be one of the following five roles: admin, itsec, projectmanager, netadmin, developer. If you add the project name as the last argument then the role is assigned just for that project, otherwise the user is assigned the named role for all projects. + +``nova-manage role has `` + Checks the user or project and responds with True if the user has a global role with a particular project. + +``nova-manage role remove `` + Remove the indicated role from the user. + +Nova Shell +~~~~~~~~~~ + +``nova-manage shell bpython`` + + Starts a new bpython shell. + +``nova-manage shell ipython`` + + Starts a new ipython shell. + +``nova-manage shell python`` + + Starts a new python shell. + +``nova-manage shell run`` + + Starts a new shell using python. + +``nova-manage shell script `` + + Runs the named script from the specified path with flags set. + +Nova VPN +~~~~~~~~ + +``nova-manage vpn list`` + + Displays a list of projects, their IP prot numbers, and what state they're in. + +``nova-manage vpn run `` + + Starts the VPN for the named project. + +``nova-manage vpn spawn`` + + Runs all VPNs. + +Nova Floating IPs +~~~~~~~~~~~~~~~~~ + +``nova-manage floating create `` + + Creates floating IP addresses for the named host by the given range. + +``nova-manage floating delete `` + + Deletes floating IP addresses in the range given. + +``nova-manage floating list`` + + Displays a list of all floating IP addresses. + +Concept: Flags +-------------- + +python-gflags + + +Concept: Plugins +---------------- + +* Managers/Drivers: utils.import_object from string flag +* virt/connections: conditional loading from string flag +* db: LazyPluggable via string flag +* auth_manager: utils.import_class based on string flag +* Volumes: moving to pluggable driver instead of manager +* Network: pluggable managers +* Compute: same driver used, but pluggable at connection + + +Concept: IPC/RPC +---------------- + +Rabbit! + + +Concept: Fakes +-------------- + +* auth +* ldap + + +Concept: Scheduler +------------------ + +* simple +* random + + +Concept: Security Groups +------------------------ + +Security groups + + +Concept: Certificate Authority +------------------------------ + +Nova does a small amount of certificate management. These certificates are used for :ref:`project vpns <../cloudpipe>` and decrypting bundled images. + + +Concept: Images +--------------- + +* launching +* bundling diff --git a/test/.Python b/test/.Python new file mode 120000 index 000000000..6cce156bd --- /dev/null +++ b/test/.Python @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/Python \ No newline at end of file diff --git a/test/bin/activate b/test/bin/activate new file mode 100644 index 000000000..588b54105 --- /dev/null +++ b/test/bin/activate @@ -0,0 +1,76 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "$_OLD_VIRTUAL_PATH" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "$_OLD_VIRTUAL_PYTHONHOME" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r + fi + + if [ -n "$_OLD_VIRTUAL_PS1" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "$1" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelavent variables +deactivate nondestructive + +VIRTUAL_ENV="/Users/anne.gentle/src/nova/docslice/test" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "$PYTHONHOME" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then + _OLD_VIRTUAL_PS1="$PS1" + if [ "x" != x ] ; then + PS1="$PS1" + else + if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" + else + PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" + fi + fi + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r +fi diff --git a/test/bin/activate.csh b/test/bin/activate.csh new file mode 100644 index 000000000..d29ac339a --- /dev/null +++ b/test/bin/activate.csh @@ -0,0 +1,32 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelavent variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/Users/anne.gentle/src/nova/docslice/test" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if ("" != "") then + set env_name = "" +else + if (`basename "$VIRTUAL_ENV"` == "__") then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` + else + set env_name = `basename "$VIRTUAL_ENV"` + endif +endif +set prompt = "[$env_name] $prompt" +unset env_name + +rehash + diff --git a/test/bin/activate.fish b/test/bin/activate.fish new file mode 100644 index 000000000..de3a8901e --- /dev/null +++ b/test/bin/activate.fish @@ -0,0 +1,79 @@ +# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org) +# you cannot run it directly + +function deactivate -d "Exit virtualenv and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self destruct! + functions -e deactivate + end +end + +# unset irrelavent variables +deactivate nondestructive + +set -gx VIRTUAL_ENV "/Users/anne.gentle/src/nova/docslice/test" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# unset PYTHONHOME if set +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish shell uses a function, instead of env vars, + # to produce the prompt. Overriding the existing function is easy. + # However, adding to the current prompt, instead of clobbering it, + # is a little more work. + set -l oldpromptfile (tempfile) + if test $status + # save the current fish_prompt function... + echo "function _old_fish_prompt" >> $oldpromptfile + echo -n \# >> $oldpromptfile + functions fish_prompt >> $oldpromptfile + # we've made the "_old_fish_prompt" file, source it. + . $oldpromptfile + rm -f $oldpromptfile + + if test -n "" + # We've been given us a prompt override. + # + # FIXME: Unsure how to handle this *safely*. We could just eval() + # whatever is given, but the risk is a bit much. + echo "activate.fish: Alternative prompt prefix is not supported under fish-shell." 1>&2 + echo "activate.fish: Alter the fish_prompt in this file as needed." 1>&2 + end + + # with the original prompt function renamed, we can override with our own. + function fish_prompt + set -l _checkbase (basename "$VIRTUAL_ENV") + if test $_checkbase = "__" + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + printf "%s[%s]%s %s" (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) (_old_fish_prompt) + else + printf "%s(%s)%s%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) (_old_fish_prompt) + end + end + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + end +end + diff --git a/test/bin/activate_this.py b/test/bin/activate_this.py new file mode 100644 index 000000000..aff6927d6 --- /dev/null +++ b/test/bin/activate_this.py @@ -0,0 +1,32 @@ +"""By using execfile(this_file, dict(__file__=this_file)) you will +activate this virtualenv environment. + +This can be used when you must use an existing Python interpreter, not +the virtualenv bin/python +""" + +try: + __file__ +except NameError: + raise AssertionError( + "You must run this like execfile('path/to/activate_this.py', dict(__file__='path/to/activate_this.py'))") +import sys +import os + +base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if sys.platform == 'win32': + site_packages = os.path.join(base, 'Lib', 'site-packages') +else: + site_packages = os.path.join(base, 'lib', 'python%s' % sys.version[:3], 'site-packages') +prev_sys_path = list(sys.path) +import site +site.addsitedir(site_packages) +sys.real_prefix = sys.prefix +sys.prefix = base +# Move the added items to the front of the path: +new_sys_path = [] +for item in list(sys.path): + if item not in prev_sys_path: + new_sys_path.append(item) + sys.path.remove(item) +sys.path[:0] = new_sys_path diff --git a/test/bin/easy_install b/test/bin/easy_install new file mode 100755 index 000000000..8544573fc --- /dev/null +++ b/test/bin/easy_install @@ -0,0 +1,9 @@ +#!/Users/anne.gentle/src/nova/docslice/test/bin/python +# EASY-INSTALL-ENTRY-SCRIPT: 'setuptools==0.6c11','console_scripts','easy_install' +__requires__ = 'setuptools==0.6c11' +import sys +from pkg_resources import load_entry_point + +sys.exit( + load_entry_point('setuptools==0.6c11', 'console_scripts', 'easy_install')() +) diff --git a/test/bin/easy_install-2.6 b/test/bin/easy_install-2.6 new file mode 100755 index 000000000..bad01d2d7 --- /dev/null +++ b/test/bin/easy_install-2.6 @@ -0,0 +1,9 @@ +#!/Users/anne.gentle/src/nova/docslice/test/bin/python +# EASY-INSTALL-ENTRY-SCRIPT: 'setuptools==0.6c11','console_scripts','easy_install-2.6' +__requires__ = 'setuptools==0.6c11' +import sys +from pkg_resources import load_entry_point + +sys.exit( + load_entry_point('setuptools==0.6c11', 'console_scripts', 'easy_install-2.6')() +) diff --git a/test/bin/pip b/test/bin/pip new file mode 100755 index 000000000..a3d2ed311 --- /dev/null +++ b/test/bin/pip @@ -0,0 +1,9 @@ +#!/Users/anne.gentle/src/nova/docslice/test/bin/python +# EASY-INSTALL-ENTRY-SCRIPT: 'pip==0.8.1','console_scripts','pip' +__requires__ = 'pip==0.8.1' +import sys +from pkg_resources import load_entry_point + +sys.exit( + load_entry_point('pip==0.8.1', 'console_scripts', 'pip')() +) diff --git a/test/bin/pip-2.6 b/test/bin/pip-2.6 new file mode 100755 index 000000000..4ad06add1 --- /dev/null +++ b/test/bin/pip-2.6 @@ -0,0 +1,9 @@ +#!/Users/anne.gentle/src/nova/docslice/test/bin/python +# EASY-INSTALL-ENTRY-SCRIPT: 'pip==0.8.1','console_scripts','pip-2.6' +__requires__ = 'pip==0.8.1' +import sys +from pkg_resources import load_entry_point + +sys.exit( + load_entry_point('pip==0.8.1', 'console_scripts', 'pip-2.6')() +) diff --git a/test/bin/python b/test/bin/python new file mode 100755 index 000000000..271b6ca30 Binary files /dev/null and b/test/bin/python differ diff --git a/test/bin/python2.6 b/test/bin/python2.6 new file mode 120000 index 000000000..d8654aa0e --- /dev/null +++ b/test/bin/python2.6 @@ -0,0 +1 @@ +python \ No newline at end of file diff --git a/test/include/python2.6 b/test/include/python2.6 new file mode 120000 index 000000000..788ac55ba --- /dev/null +++ b/test/include/python2.6 @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/include/python2.6 \ No newline at end of file diff --git a/test/lib/python2.6/UserDict.py b/test/lib/python2.6/UserDict.py new file mode 120000 index 000000000..93e0864b1 --- /dev/null +++ b/test/lib/python2.6/UserDict.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/UserDict.py \ No newline at end of file diff --git a/test/lib/python2.6/_abcoll.py b/test/lib/python2.6/_abcoll.py new file mode 120000 index 000000000..ff1769246 --- /dev/null +++ b/test/lib/python2.6/_abcoll.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/_abcoll.py \ No newline at end of file diff --git a/test/lib/python2.6/abc.py b/test/lib/python2.6/abc.py new file mode 120000 index 000000000..79fa108b2 --- /dev/null +++ b/test/lib/python2.6/abc.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/abc.py \ No newline at end of file diff --git a/test/lib/python2.6/codecs.py b/test/lib/python2.6/codecs.py new file mode 120000 index 000000000..9e4bfb7fa --- /dev/null +++ b/test/lib/python2.6/codecs.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/codecs.py \ No newline at end of file diff --git a/test/lib/python2.6/config b/test/lib/python2.6/config new file mode 120000 index 000000000..eef498c62 --- /dev/null +++ b/test/lib/python2.6/config @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/config \ No newline at end of file diff --git a/test/lib/python2.6/copy_reg.py b/test/lib/python2.6/copy_reg.py new file mode 120000 index 000000000..7285fda0e --- /dev/null +++ b/test/lib/python2.6/copy_reg.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/copy_reg.py \ No newline at end of file diff --git a/test/lib/python2.6/distutils/__init__.py b/test/lib/python2.6/distutils/__init__.py new file mode 100644 index 000000000..7ebb41c04 --- /dev/null +++ b/test/lib/python2.6/distutils/__init__.py @@ -0,0 +1,91 @@ +import os +import sys +import warnings +import opcode # opcode is not a virtualenv module, so we can use it to find the stdlib + # Important! To work on pypy, this must be a module that resides in the + # lib-python/modified-x.y.z directory + +dirname = os.path.dirname + +distutils_path = os.path.join(os.path.dirname(opcode.__file__), 'distutils') +if os.path.normpath(distutils_path) == os.path.dirname(os.path.normpath(__file__)): + warnings.warn( + "The virtualenv distutils package at %s appears to be in the same location as the system distutils?") +else: + __path__.insert(0, distutils_path) + exec open(os.path.join(distutils_path, '__init__.py')).read() + +import dist +import sysconfig + + +## patch build_ext (distutils doesn't know how to get the libs directory +## path on windows - it hardcodes the paths around the patched sys.prefix) + +if sys.platform == 'win32': + from distutils.command.build_ext import build_ext as old_build_ext + class build_ext(old_build_ext): + def finalize_options (self): + if self.library_dirs is None: + self.library_dirs = [] + elif isinstance(self.library_dirs, basestring): + self.library_dirs = self.library_dirs.split(os.pathsep) + + self.library_dirs.insert(0, os.path.join(sys.real_prefix, "Libs")) + old_build_ext.finalize_options(self) + + from distutils.command import build_ext as build_ext_module + build_ext_module.build_ext = build_ext + +## distutils.dist patches: + +old_find_config_files = dist.Distribution.find_config_files +def find_config_files(self): + found = old_find_config_files(self) + system_distutils = os.path.join(distutils_path, 'distutils.cfg') + #if os.path.exists(system_distutils): + # found.insert(0, system_distutils) + # What to call the per-user config file + if os.name == 'posix': + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" + user_filename = os.path.join(sys.prefix, user_filename) + if os.path.isfile(user_filename): + for item in list(found): + if item.endswith('pydistutils.cfg'): + found.remove(item) + found.append(user_filename) + return found +dist.Distribution.find_config_files = find_config_files + +## distutils.sysconfig patches: + +old_get_python_inc = sysconfig.get_python_inc +def sysconfig_get_python_inc(plat_specific=0, prefix=None): + if prefix is None: + prefix = sys.real_prefix + return old_get_python_inc(plat_specific, prefix) +sysconfig_get_python_inc.__doc__ = old_get_python_inc.__doc__ +sysconfig.get_python_inc = sysconfig_get_python_inc + +old_get_python_lib = sysconfig.get_python_lib +def sysconfig_get_python_lib(plat_specific=0, standard_lib=0, prefix=None): + if standard_lib and prefix is None: + prefix = sys.real_prefix + return old_get_python_lib(plat_specific, standard_lib, prefix) +sysconfig_get_python_lib.__doc__ = old_get_python_lib.__doc__ +sysconfig.get_python_lib = sysconfig_get_python_lib + +old_get_config_vars = sysconfig.get_config_vars +def sysconfig_get_config_vars(*args): + real_vars = old_get_config_vars(*args) + if sys.platform == 'win32': + lib_dir = os.path.join(sys.real_prefix, "libs") + if isinstance(real_vars, dict) and 'LIBDIR' not in real_vars: + real_vars['LIBDIR'] = lib_dir # asked for all + elif isinstance(real_vars, list) and 'LIBDIR' in args: + real_vars = real_vars + [lib_dir] # asked for list + return real_vars +sysconfig_get_config_vars.__doc__ = old_get_config_vars.__doc__ +sysconfig.get_config_vars = sysconfig_get_config_vars diff --git a/test/lib/python2.6/distutils/distutils.cfg b/test/lib/python2.6/distutils/distutils.cfg new file mode 100644 index 000000000..1af230ec9 --- /dev/null +++ b/test/lib/python2.6/distutils/distutils.cfg @@ -0,0 +1,6 @@ +# This is a config file local to this virtualenv installation +# You may include options that will be used by all distutils commands, +# and by easy_install. For instance: +# +# [easy_install] +# find_links = http://mylocalsite diff --git a/test/lib/python2.6/encodings b/test/lib/python2.6/encodings new file mode 120000 index 000000000..89f28f824 --- /dev/null +++ b/test/lib/python2.6/encodings @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/encodings \ No newline at end of file diff --git a/test/lib/python2.6/fnmatch.py b/test/lib/python2.6/fnmatch.py new file mode 120000 index 000000000..cce0594f2 --- /dev/null +++ b/test/lib/python2.6/fnmatch.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/fnmatch.py \ No newline at end of file diff --git a/test/lib/python2.6/genericpath.py b/test/lib/python2.6/genericpath.py new file mode 120000 index 000000000..b14e1bc26 --- /dev/null +++ b/test/lib/python2.6/genericpath.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/genericpath.py \ No newline at end of file diff --git a/test/lib/python2.6/lib-dynload b/test/lib/python2.6/lib-dynload new file mode 120000 index 000000000..4644b7072 --- /dev/null +++ b/test/lib/python2.6/lib-dynload @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-dynload \ No newline at end of file diff --git a/test/lib/python2.6/linecache.py b/test/lib/python2.6/linecache.py new file mode 120000 index 000000000..783624da8 --- /dev/null +++ b/test/lib/python2.6/linecache.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/linecache.py \ No newline at end of file diff --git a/test/lib/python2.6/locale.py b/test/lib/python2.6/locale.py new file mode 120000 index 000000000..4e674c7b6 --- /dev/null +++ b/test/lib/python2.6/locale.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/locale.py \ No newline at end of file diff --git a/test/lib/python2.6/ntpath.py b/test/lib/python2.6/ntpath.py new file mode 120000 index 000000000..9b6b40f48 --- /dev/null +++ b/test/lib/python2.6/ntpath.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ntpath.py \ No newline at end of file diff --git a/test/lib/python2.6/orig-prefix.txt b/test/lib/python2.6/orig-prefix.txt new file mode 100644 index 000000000..535eb0f0e --- /dev/null +++ b/test/lib/python2.6/orig-prefix.txt @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6 \ No newline at end of file diff --git a/test/lib/python2.6/os.py b/test/lib/python2.6/os.py new file mode 120000 index 000000000..92e6e9a7c --- /dev/null +++ b/test/lib/python2.6/os.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/os.py \ No newline at end of file diff --git a/test/lib/python2.6/posixpath.py b/test/lib/python2.6/posixpath.py new file mode 120000 index 000000000..c095d16a1 --- /dev/null +++ b/test/lib/python2.6/posixpath.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/posixpath.py \ No newline at end of file diff --git a/test/lib/python2.6/re.py b/test/lib/python2.6/re.py new file mode 120000 index 000000000..b4710c5f7 --- /dev/null +++ b/test/lib/python2.6/re.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/re.py \ No newline at end of file diff --git a/test/lib/python2.6/site-packages/easy-install.pth b/test/lib/python2.6/site-packages/easy-install.pth new file mode 100644 index 000000000..7a6ae2b6d --- /dev/null +++ b/test/lib/python2.6/site-packages/easy-install.pth @@ -0,0 +1,4 @@ +import sys; sys.__plen = len(sys.path) +./setuptools-0.6c11-py2.6.egg +./pip-0.8.1-py2.6.egg +import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/PKG-INFO b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/PKG-INFO new file mode 100644 index 000000000..29c30cb8c --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,348 @@ +Metadata-Version: 1.0 +Name: pip +Version: 0.8.1 +Summary: pip installs packages. Python packages. An easy_install replacement +Home-page: http://pip.openplans.org +Author: Ian Bicking +Author-email: python-virtualenv@groups.google.com +License: MIT +Description: The main website for pip is `pip.openplans.org + `_. You can also install + the `in-development version `_ + of pip with ``easy_install pip==dev``. + + + Introduction + ------------ + + pip installs packages. Python packages. + + If you use `virtualenv `__ -- a tool + for installing libraries in a local and isolated manner -- you'll + automatically get a copy of pip. Free bonus! + + Once you have pip, you can use it like this:: + + $ pip install SomePackage + + SomePackage is some package you'll find on `PyPI + `_. This installs the package and all + its dependencies. + + pip does other stuff too, with packages, but install is the biggest + one. You can ``pip uninstall`` too. + + You can also install from a URL (that points to a tar or zip file), + install from some version control system (use URLs like + ``hg+http://domain/repo`` -- or prefix ``git+``, ``svn+`` etc). pip + knows a bunch of stuff about revisions and stuff, so if you need to do + things like install a very specific revision from a repository pip can + do that too. + + If you've ever used ``python setup.py develop``, you can do something + like that with ``pip install -e ./`` -- this works with packages that + use ``distutils`` too (usually this only works with Setuptools + projects). + + You can use ``pip install --upgrade SomePackage`` to upgrade to a + newer version, or ``pip install SomePackage==1.0.4`` to install a very + specific version. + + Pip Compared To easy_install + ---------------------------- + + pip is a replacement for `easy_install + `_. It uses mostly the + same techniques for finding packages, so packages that were made + easy_installable should be pip-installable as well. + + pip is meant to improve on easy_install. Some of the improvements: + + * All packages are downloaded before installation. Partially-completed + installation doesn't occur as a result. + + * Care is taken to present useful output on the console. + + * The reasons for actions are kept track of. For instance, if a package is + being installed, pip keeps track of why that package was required. + + * Error messages should be useful. + + * The code is relatively concise and cohesive, making it easier to use + programmatically. + + * Packages don't have to be installed as egg archives, they can be installed + flat (while keeping the egg metadata). + + * Native support for other version control systems (Git, Mercurial and Bazaar) + + * Uninstallation of packages. + + * Simple to define fixed sets of requirements and reliably reproduce a + set of packages. + + pip doesn't do everything that easy_install does. Specifically: + + * It cannot install from eggs. It only installs from source. (In the + future it would be good if it could install binaries from Windows ``.exe`` + or ``.msi`` -- binary install on other platforms is not a priority.) + + * It doesn't understand Setuptools extras (like ``package[test]``). This should + be added eventually. + + * It is incompatible with some packages that extensively customize distutils + or setuptools in their ``setup.py`` files. + + pip is complementary with `virtualenv + `__, and it is encouraged that you use + virtualenv to isolate your installation. + + Community + --------- + + The homepage for pip is temporarily located `on PyPI + `_ -- a more proper homepage will + follow. Bugs can go on the `pip issue tracker + `_. Discussion should happen on the + `virtualenv email group + `_. + + Uninstall + --------- + + pip is able to uninstall most installed packages with ``pip uninstall + package-name``. + + Known exceptions include pure-distutils packages installed with + ``python setup.py install`` (such packages leave behind no metadata allowing + determination of what files were installed), and script wrappers installed + by develop-installs (``python setup.py develop``). + + pip also performs an automatic uninstall of an old version of a package + before upgrading to a newer version, so outdated files (and egg-info data) + from conflicting versions aren't left hanging around to cause trouble. The + old version of the package is automatically restored if the new version + fails to download or install. + + .. _`requirements file`: + + Requirements Files + ------------------ + + When installing software, and Python packages in particular, it's common that + you get a lot of libraries installed. You just did ``easy_install MyPackage`` + and you get a dozen packages. Each of these packages has its own version. + + Maybe you ran that installation and it works. Great! Will it keep working? + Did you have to provide special options to get it to find everything? Did you + have to install a bunch of other optional pieces? Most of all, will you be able + to do it again? Requirements files give you a way to create an *environment*: + a *set* of packages that work together. + + If you've ever tried to setup an application on a new system, or with slightly + updated pieces, and had it fail, pip requirements are for you. If you + haven't had this problem then you will eventually, so pip requirements are + for you too -- requirements make explicit, repeatable installation of packages. + + So what are requirements files? They are very simple: lists of packages to + install. Instead of running something like ``pip MyApp`` and getting + whatever libraries come along, you can create a requirements file something like:: + + MyApp + Framework==0.9.4 + Library>=0.2 + + Then, regardless of what MyApp lists in ``setup.py``, you'll get a + specific version of Framework (0.9.4) and at least the 0.2 version of + Library. (You might think you could list these specific versions in + MyApp's ``setup.py`` -- but if you do that you'll have to edit MyApp + if you want to try a new version of Framework, or release a new + version of MyApp if you determine that Library 0.3 doesn't work with + your application.) You can also add optional libraries and support + tools that MyApp doesn't strictly require, giving people a set of + recommended libraries. + + You can also include "editable" packages -- packages that are checked out from + Subversion, Git, Mercurial and Bazaar. These are just like using the ``-e`` + option to pip. They look like:: + + -e svn+http://myrepo/svn/MyApp#egg=MyApp + + You have to start the URL with ``svn+`` (``git+``, ``hg+`` or ``bzr+``), and + you have to include ``#egg=Package`` so pip knows what to expect at that URL. + You can also include ``@rev`` in the URL, e.g., ``@275`` to check out + revision 275. + + Requirement files are mostly *flat*. Maybe ``MyApp`` requires + ``Framework``, and ``Framework`` requires ``Library``. I encourage + you to still list all these in a single requirement file; it is the + nature of Python programs that there are implicit bindings *directly* + between MyApp and Library. For instance, Framework might expose one + of Library's objects, and so if Library is updated it might directly + break MyApp. If that happens you can update the requirements file to + force an earlier version of Library, and you can do that without + having to re-release MyApp at all. + + Read the `requirements file format `_ to + learn about other features. + + Freezing Requirements + --------------------- + + So you have a working set of packages, and you want to be able to install them + elsewhere. `Requirements files`_ let you install exact versions, but it won't + tell you what all the exact versions are. + + To create a new requirements file from a known working environment, use:: + + $ pip freeze > stable-req.txt + + This will write a listing of *all* installed libraries to ``stable-req.txt`` + with exact versions for every library. You may want to edit the file down after + generating (e.g., to eliminate unnecessary libraries), but it'll give you a + stable starting point for constructing your requirements file. + + You can also give it an existing requirements file, and it will use that as a + sort of template for the new file. So if you do:: + + $ pip freeze -r devel-req.txt > stable-req.txt + + it will keep the packages listed in ``devel-req.txt`` in order and preserve + comments. + + Bundles + ------- + + Another way to distribute a set of libraries is a bundle format (specific to + pip). This format is not stable at this time (there simply hasn't been + any feedback, nor a great deal of thought). A bundle file contains all the + source for your package, and you can have pip install them all together. + Once you have the bundle file further network access won't be necessary. To + build a bundle file, do:: + + $ pip bundle MyApp.pybundle MyApp + + (Using a `requirements file`_ would be wise.) Then someone else can get the + file ``MyApp.pybundle`` and run:: + + $ pip install MyApp.pybundle + + This is *not* a binary format. This only packages source. If you have binary + packages, then the person who installs the files will have to have a compiler, + any necessary headers installed, etc. Binary packages are hard, this is + relatively easy. + + Using pip with virtualenv + ------------------------- + + pip is most nutritious when used with `virtualenv + `__. One of the reasons pip + doesn't install "multi-version" eggs is that virtualenv removes much of the need + for it. Because pip is installed by virtualenv, just use + ``path/to/my/environment/bin/pip`` to install things into that + specific environment. + + To tell pip to only run if there is a virtualenv currently activated, + and to bail if not, use:: + + export PIP_REQUIRE_VIRTUALENV=true + + To tell pip to automatically use the currently active virtualenv:: + + export PIP_RESPECT_VIRTUALENV=true + + Providing an environment with ``-E`` will be ignored. + + Using pip with virtualenvwrapper + --------------------------------- + + If you are using `virtualenvwrapper + `_, you might + want pip to automatically create its virtualenvs in your + ``$WORKON_HOME``. + + You can tell pip to do so by defining ``PIP_VIRTUALENV_BASE`` in your + environment and setting it to the same value as that of + ``$WORKON_HOME``. + + Do so by adding the line:: + + export PIP_VIRTUALENV_BASE=$WORKON_HOME + + in your .bashrc under the line starting with ``export WORKON_HOME``. + + Using pip with buildout + ----------------------- + + If you are using `zc.buildout + `_ you should look at + `gp.recipe.pip `_ as an + option to use pip and virtualenv in your buildouts. + + Command line completion + ----------------------- + + pip comes with support for command line completion in bash and zsh and + allows you tab complete commands and options. To enable it you simply + need copy the required shell script to the your shell startup file + (e.g. ``.profile`` or ``.zprofile``) by running the special ``completion`` + command, e.g. for bash:: + + $ pip completion --bash >> ~/.profile + + And for zsh:: + + $ pip completion --zsh >> ~/.zprofile + + Alternatively, you can use the result of the ``completion`` command + directly with the eval function of you shell, e.g. by adding:: + + eval "`pip completion --bash`" + + to your startup file. + + Searching for packages + ---------------------- + + pip can search the `Python Package Index `_ (PyPI) + for packages using the ``pip search`` command. To search, run:: + + $ pip search "query" + + The query will be used to search the names and summaries of all packages + indexed. + + pip searches http://pypi.python.org/pypi by default but alternative indexes + can be searched by using the ``--index`` flag. + + Mirror support + -------------- + + The `PyPI mirroring infrastructure `_ as + described in `PEP 381 `_ can be + used by passing the ``--use-mirrors`` option to the install command. + Alternatively, you can use the other ways to configure pip, e.g.:: + + $ export PIP_USE_MIRRORS=true + + If enabled, pip will automatically query the DNS entry of the mirror index URL + to find the list of mirrors to use. In case you want to override this list, + please use the ``--mirrors`` option of the install command, or add to your pip + configuration file:: + + [install] + use-mirrors = true + mirrors = + http://d.pypi.python.org + http://b.pypi.python.org + +Keywords: easy_install distutils setuptools egg virtualenv +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Build Tools +Classifier: Programming Language :: Python :: 2.4 +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/SOURCES.txt b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/SOURCES.txt new file mode 100644 index 000000000..3a068547e --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/SOURCES.txt @@ -0,0 +1,57 @@ +MANIFEST.in +setup.cfg +setup.py +docs/branches.txt +docs/ci-server-step-by-step.txt +docs/configuration.txt +docs/how-to-contribute.txt +docs/index.txt +docs/license.txt +docs/news.txt +docs/requirement-format.txt +docs/running-tests.txt +docs/_build/branches.html +docs/_build/ci-server-step-by-step.html +docs/_build/configuration.html +docs/_build/how-to-contribute.html +docs/_build/index.html +docs/_build/license.html +docs/_build/news.html +docs/_build/requirement-format.html +docs/_build/running-tests.html +docs/_build/search.html +pip/__init__.py +pip/_pkgutil.py +pip/backwardcompat.py +pip/basecommand.py +pip/baseparser.py +pip/download.py +pip/exceptions.py +pip/index.py +pip/locations.py +pip/log.py +pip/req.py +pip/runner.py +pip/util.py +pip/venv.py +pip.egg-info/PKG-INFO +pip.egg-info/SOURCES.txt +pip.egg-info/dependency_links.txt +pip.egg-info/entry_points.txt +pip.egg-info/not-zip-safe +pip.egg-info/top_level.txt +pip/commands/__init__.py +pip/commands/bundle.py +pip/commands/completion.py +pip/commands/freeze.py +pip/commands/help.py +pip/commands/install.py +pip/commands/search.py +pip/commands/uninstall.py +pip/commands/unzip.py +pip/commands/zip.py +pip/vcs/__init__.py +pip/vcs/bazaar.py +pip/vcs/git.py +pip/vcs/mercurial.py +pip/vcs/subversion.py \ No newline at end of file diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/dependency_links.txt b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/dependency_links.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/entry_points.txt b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/entry_points.txt new file mode 100644 index 000000000..2b0afba73 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/entry_points.txt @@ -0,0 +1,4 @@ +[console_scripts] +pip = pip:main +pip-2.6 = pip:main + diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/not-zip-safe b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/not-zip-safe new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/not-zip-safe @@ -0,0 +1 @@ + diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/top_level.txt b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/top_level.txt new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +pip diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/__init__.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/__init__.py new file mode 100644 index 000000000..c5de5c9a8 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/__init__.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python +import os +import optparse + +import subprocess +import sys +import re +import difflib + +from pip.basecommand import command_dict, load_command, load_all_commands, command_names +from pip.baseparser import parser +from pip.exceptions import InstallationError +from pip.log import logger +from pip.util import get_installed_distributions +from pip.backwardcompat import walk_packages + + +def autocomplete(): + """Command and option completion for the main option parser (and options) + and its subcommands (and options). + + Enable by sourcing one of the completion shell scripts (bash or zsh). + """ + # Don't complete if user hasn't sourced bash_completion file. + if 'PIP_AUTO_COMPLETE' not in os.environ: + return + cwords = os.environ['COMP_WORDS'].split()[1:] + cword = int(os.environ['COMP_CWORD']) + try: + current = cwords[cword-1] + except IndexError: + current = '' + load_all_commands() + subcommands = [cmd for cmd, cls in command_dict.items() if not cls.hidden] + options = [] + # subcommand + try: + subcommand_name = [w for w in cwords if w in subcommands][0] + except IndexError: + subcommand_name = None + # subcommand options + if subcommand_name: + # special case: 'help' subcommand has no options + if subcommand_name == 'help': + sys.exit(1) + # special case: list locally installed dists for uninstall command + if subcommand_name == 'uninstall' and not current.startswith('-'): + installed = [] + lc = current.lower() + for dist in get_installed_distributions(local_only=True): + if dist.key.startswith(lc) and dist.key not in cwords[1:]: + installed.append(dist.key) + # if there are no dists installed, fall back to option completion + if installed: + for dist in installed: + print dist + sys.exit(1) + subcommand = command_dict.get(subcommand_name) + options += [(opt.get_opt_string(), opt.nargs) + for opt in subcommand.parser.option_list + if opt.help != optparse.SUPPRESS_HELP] + # filter out previously specified options from available options + prev_opts = [x.split('=')[0] for x in cwords[1:cword-1]] + options = filter(lambda (x, v): x not in prev_opts, options) + # filter options by current input + options = [(k, v) for k, v in options if k.startswith(current)] + for option in options: + opt_label = option[0] + # append '=' to options which require args + if option[1]: + opt_label += '=' + print opt_label + else: + # show options of main parser only when necessary + if current.startswith('-') or current.startswith('--'): + subcommands += [opt.get_opt_string() + for opt in parser.option_list + if opt.help != optparse.SUPPRESS_HELP] + print ' '.join(filter(lambda x: x.startswith(current), subcommands)) + sys.exit(1) + + +def version_control(): + # Import all the version control support modules: + from pip import vcs + for importer, modname, ispkg in \ + walk_packages(path=vcs.__path__, prefix=vcs.__name__+'.'): + __import__(modname) + + +def main(initial_args=None): + if initial_args is None: + initial_args = sys.argv[1:] + autocomplete() + version_control() + options, args = parser.parse_args(initial_args) + if options.help and not args: + args = ['help'] + if not args: + parser.error('You must give a command (use "pip help" to see a list of commands)') + command = args[0].lower() + load_command(command) + if command not in command_dict: + close_commands = difflib.get_close_matches(command, command_names()) + if close_commands: + guess = close_commands[0] + if args[1:]: + guess = "%s %s" % (guess, " ".join(args[1:])) + else: + guess = 'install %s' % command + error_dict = {'arg': command, 'guess': guess, + 'script': os.path.basename(sys.argv[0])} + parser.error('No command by the name %(script)s %(arg)s\n ' + '(maybe you meant "%(script)s %(guess)s")' % error_dict) + command = command_dict[command] + return command.main(initial_args, args[1:], options) + + +############################################################ +## Writing freeze files + + +class FrozenRequirement(object): + + def __init__(self, name, req, editable, comments=()): + self.name = name + self.req = req + self.editable = editable + self.comments = comments + + _rev_re = re.compile(r'-r(\d+)$') + _date_re = re.compile(r'-(20\d\d\d\d\d\d)$') + + @classmethod + def from_dist(cls, dist, dependency_links, find_tags=False): + location = os.path.normcase(os.path.abspath(dist.location)) + comments = [] + from pip.vcs import vcs, get_src_requirement + if vcs.get_backend_name(location): + editable = True + req = get_src_requirement(dist, location, find_tags) + if req is None: + logger.warn('Could not determine repository location of %s' % location) + comments.append('## !! Could not determine repository location') + req = dist.as_requirement() + editable = False + else: + editable = False + req = dist.as_requirement() + specs = req.specs + assert len(specs) == 1 and specs[0][0] == '==' + version = specs[0][1] + ver_match = cls._rev_re.search(version) + date_match = cls._date_re.search(version) + if ver_match or date_match: + svn_backend = vcs.get_backend('svn') + if svn_backend: + svn_location = svn_backend( + ).get_location(dist, dependency_links) + if not svn_location: + logger.warn( + 'Warning: cannot find svn location for %s' % req) + comments.append('## FIXME: could not find svn URL in dependency_links for this package:') + else: + comments.append('# Installing as editable to satisfy requirement %s:' % req) + if ver_match: + rev = ver_match.group(1) + else: + rev = '{%s}' % date_match.group(1) + editable = True + req = '%s@%s#egg=%s' % (svn_location, rev, cls.egg_name(dist)) + return cls(dist.project_name, req, editable, comments) + + @staticmethod + def egg_name(dist): + name = dist.egg_name() + match = re.search(r'-py\d\.\d$', name) + if match: + name = name[:match.start()] + return name + + def __str__(self): + req = self.req + if self.editable: + req = '-e %s' % req + return '\n'.join(list(self.comments)+[str(req)])+'\n' + +############################################################ +## Requirement files + + +def call_subprocess(cmd, show_stdout=True, + filter_stdout=None, cwd=None, + raise_on_returncode=True, + command_level=logger.DEBUG, command_desc=None, + extra_environ=None): + if command_desc is None: + cmd_parts = [] + for part in cmd: + if ' ' in part or '\n' in part or '"' in part or "'" in part: + part = '"%s"' % part.replace('"', '\\"') + cmd_parts.append(part) + command_desc = ' '.join(cmd_parts) + if show_stdout: + stdout = None + else: + stdout = subprocess.PIPE + logger.log(command_level, "Running command %s" % command_desc) + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + try: + proc = subprocess.Popen( + cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, + cwd=cwd, env=env) + except Exception, e: + logger.fatal( + "Error %s while executing command %s" % (e, command_desc)) + raise + all_output = [] + if stdout is not None: + stdout = proc.stdout + while 1: + line = stdout.readline() + if not line: + break + line = line.rstrip() + all_output.append(line + '\n') + if filter_stdout: + level = filter_stdout(line) + if isinstance(level, tuple): + level, line = level + logger.log(level, line) + if not logger.stdout_level_matches(level): + logger.show_progress() + else: + logger.info(line) + else: + returned_stdout, returned_stderr = proc.communicate() + all_output = [returned_stdout or ''] + proc.wait() + if proc.returncode: + if raise_on_returncode: + if all_output: + logger.notify('Complete output from command %s:' % command_desc) + logger.notify('\n'.join(all_output) + '\n----------------------------------------') + raise InstallationError( + "Command %s failed with error code %s" + % (command_desc, proc.returncode)) + else: + logger.warn( + "Command %s had error code %s" + % (command_desc, proc.returncode)) + if stdout is not None: + return ''.join(all_output) + + +if __name__ == '__main__': + exit = main() + if exit: + sys.exit(exit) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/_pkgutil.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/_pkgutil.py new file mode 100644 index 000000000..f8fb8aa61 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/_pkgutil.py @@ -0,0 +1,589 @@ +"""Utilities to support packages.""" + +# NOTE: This module must remain compatible with Python 2.3, as it is shared +# by setuptools for distribution with Python 2.3 and up. + +import os +import sys +import imp +import os.path +from types import ModuleType + +__all__ = [ + 'get_importer', 'iter_importers', 'get_loader', 'find_loader', + 'walk_packages', 'iter_modules', + 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path', +] + + +def read_code(stream): + # This helper is needed in order for the PEP 302 emulation to + # correctly handle compiled files + import marshal + + magic = stream.read(4) + if magic != imp.get_magic(): + return None + + stream.read(4) # Skip timestamp + return marshal.load(stream) + + +def simplegeneric(func): + """Make a trivial single-dispatch generic function""" + registry = {} + + def wrapper(*args, **kw): + ob = args[0] + try: + cls = ob.__class__ + except AttributeError: + cls = type(ob) + try: + mro = cls.__mro__ + except AttributeError: + try: + + class cls(cls, object): + pass + + mro = cls.__mro__[1:] + except TypeError: + mro = object, # must be an ExtensionClass or some such :( + for t in mro: + if t in registry: + return registry[t](*args, **kw) + else: + return func(*args, **kw) + try: + wrapper.__name__ = func.__name__ + except (TypeError, AttributeError): + pass # Python 2.3 doesn't allow functions to be renamed + + def register(typ, func=None): + if func is None: + return lambda f: register(typ, f) + registry[typ] = func + return func + + wrapper.__dict__ = func.__dict__ + wrapper.__doc__ = func.__doc__ + wrapper.register = register + return wrapper + + +def walk_packages(path=None, prefix='', onerror=None): + """Yields (module_loader, name, ispkg) for all modules recursively + on path, or, if path is None, all accessible modules. + + 'path' should be either None or a list of paths to look for + modules in. + + 'prefix' is a string to output on the front of every module name + on output. + + Note that this function must import all *packages* (NOT all + modules!) on the given path, in order to access the __path__ + attribute to find submodules. + + 'onerror' is a function which gets called with one argument (the + name of the package which was being imported) if any exception + occurs while trying to import a package. If no onerror function is + supplied, ImportErrors are caught and ignored, while all other + exceptions are propagated, terminating the search. + + Examples: + + # list all modules python can access + walk_packages() + + # list all submodules of ctypes + walk_packages(ctypes.__path__, ctypes.__name__+'.') + """ + + def seen(p, m={}): + if p in m: + return True + m[p] = True + + for importer, name, ispkg in iter_modules(path, prefix): + yield importer, name, ispkg + + if ispkg: + try: + __import__(name) + except ImportError: + if onerror is not None: + onerror(name) + except Exception: + if onerror is not None: + onerror(name) + else: + raise + else: + path = getattr(sys.modules[name], '__path__', None) or [] + + # don't traverse path items we've seen before + path = [p for p in path if not seen(p)] + + for item in walk_packages(path, name+'.', onerror): + yield item + + +def iter_modules(path=None, prefix=''): + """Yields (module_loader, name, ispkg) for all submodules on path, + or, if path is None, all top-level modules on sys.path. + + 'path' should be either None or a list of paths to look for + modules in. + + 'prefix' is a string to output on the front of every module name + on output. + """ + + if path is None: + importers = iter_importers() + else: + importers = map(get_importer, path) + + yielded = {} + for i in importers: + for name, ispkg in iter_importer_modules(i, prefix): + if name not in yielded: + yielded[name] = 1 + yield i, name, ispkg + + +#@simplegeneric +def iter_importer_modules(importer, prefix=''): + if not hasattr(importer, 'iter_modules'): + return [] + return importer.iter_modules(prefix) + +iter_importer_modules = simplegeneric(iter_importer_modules) + + +class ImpImporter: + """PEP 302 Importer that wraps Python's "classic" import algorithm + + ImpImporter(dirname) produces a PEP 302 importer that searches that + directory. ImpImporter(None) produces a PEP 302 importer that searches + the current sys.path, plus any modules that are frozen or built-in. + + Note that ImpImporter does not currently support being used by placement + on sys.meta_path. + """ + + def __init__(self, path=None): + self.path = path + + def find_module(self, fullname, path=None): + # Note: we ignore 'path' argument since it is only used via meta_path + subname = fullname.split(".")[-1] + if subname != fullname and self.path is None: + return None + if self.path is None: + path = None + else: + path = [os.path.realpath(self.path)] + try: + file, filename, etc = imp.find_module(subname, path) + except ImportError: + return None + return ImpLoader(fullname, file, filename, etc) + + def iter_modules(self, prefix=''): + if self.path is None or not os.path.isdir(self.path): + return + + yielded = {} + import inspect + + filenames = os.listdir(self.path) + filenames.sort() # handle packages before same-named modules + + for fn in filenames: + modname = inspect.getmodulename(fn) + if modname=='__init__' or modname in yielded: + continue + + path = os.path.join(self.path, fn) + ispkg = False + + if not modname and os.path.isdir(path) and '.' not in fn: + modname = fn + for fn in os.listdir(path): + subname = inspect.getmodulename(fn) + if subname=='__init__': + ispkg = True + break + else: + continue # not a package + + if modname and '.' not in modname: + yielded[modname] = 1 + yield prefix + modname, ispkg + + +class ImpLoader: + """PEP 302 Loader that wraps Python's "classic" import algorithm + """ + code = source = None + + def __init__(self, fullname, file, filename, etc): + self.file = file + self.filename = filename + self.fullname = fullname + self.etc = etc + + def load_module(self, fullname): + self._reopen() + try: + mod = imp.load_module(fullname, self.file, self.filename, self.etc) + finally: + if self.file: + self.file.close() + # Note: we don't set __loader__ because we want the module to look + # normal; i.e. this is just a wrapper for standard import machinery + return mod + + def get_data(self, pathname): + return open(pathname, "rb").read() + + def _reopen(self): + if self.file and self.file.closed: + mod_type = self.etc[2] + if mod_type==imp.PY_SOURCE: + self.file = open(self.filename, 'rU') + elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION): + self.file = open(self.filename, 'rb') + + def _fix_name(self, fullname): + if fullname is None: + fullname = self.fullname + elif fullname != self.fullname: + raise ImportError("Loader for module %s cannot handle " + "module %s" % (self.fullname, fullname)) + return fullname + + def is_package(self, fullname): + fullname = self._fix_name(fullname) + return self.etc[2]==imp.PKG_DIRECTORY + + def get_code(self, fullname=None): + fullname = self._fix_name(fullname) + if self.code is None: + mod_type = self.etc[2] + if mod_type==imp.PY_SOURCE: + source = self.get_source(fullname) + self.code = compile(source, self.filename, 'exec') + elif mod_type==imp.PY_COMPILED: + self._reopen() + try: + self.code = read_code(self.file) + finally: + self.file.close() + elif mod_type==imp.PKG_DIRECTORY: + self.code = self._get_delegate().get_code() + return self.code + + def get_source(self, fullname=None): + fullname = self._fix_name(fullname) + if self.source is None: + mod_type = self.etc[2] + if mod_type==imp.PY_SOURCE: + self._reopen() + try: + self.source = self.file.read() + finally: + self.file.close() + elif mod_type==imp.PY_COMPILED: + if os.path.exists(self.filename[:-1]): + f = open(self.filename[:-1], 'rU') + self.source = f.read() + f.close() + elif mod_type==imp.PKG_DIRECTORY: + self.source = self._get_delegate().get_source() + return self.source + + def _get_delegate(self): + return ImpImporter(self.filename).find_module('__init__') + + def get_filename(self, fullname=None): + fullname = self._fix_name(fullname) + mod_type = self.etc[2] + if self.etc[2]==imp.PKG_DIRECTORY: + return self._get_delegate().get_filename() + elif self.etc[2] in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION): + return self.filename + return None + + +try: + import zipimport + from zipimport import zipimporter + + def iter_zipimport_modules(importer, prefix=''): + dirlist = zipimport._zip_directory_cache[importer.archive].keys() + dirlist.sort() + _prefix = importer.prefix + plen = len(_prefix) + yielded = {} + import inspect + for fn in dirlist: + if not fn.startswith(_prefix): + continue + + fn = fn[plen:].split(os.sep) + + if len(fn)==2 and fn[1].startswith('__init__.py'): + if fn[0] not in yielded: + yielded[fn[0]] = 1 + yield fn[0], True + + if len(fn)!=1: + continue + + modname = inspect.getmodulename(fn[0]) + if modname=='__init__': + continue + + if modname and '.' not in modname and modname not in yielded: + yielded[modname] = 1 + yield prefix + modname, False + + iter_importer_modules.register(zipimporter, iter_zipimport_modules) + +except ImportError: + pass + + +def get_importer(path_item): + """Retrieve a PEP 302 importer for the given path item + + The returned importer is cached in sys.path_importer_cache + if it was newly created by a path hook. + + If there is no importer, a wrapper around the basic import + machinery is returned. This wrapper is never inserted into + the importer cache (None is inserted instead). + + The cache (or part of it) can be cleared manually if a + rescan of sys.path_hooks is necessary. + """ + try: + importer = sys.path_importer_cache[path_item] + except KeyError: + for path_hook in sys.path_hooks: + try: + importer = path_hook(path_item) + break + except ImportError: + pass + else: + importer = None + sys.path_importer_cache.setdefault(path_item, importer) + + if importer is None: + try: + importer = ImpImporter(path_item) + except ImportError: + importer = None + return importer + + +def iter_importers(fullname=""): + """Yield PEP 302 importers for the given module name + + If fullname contains a '.', the importers will be for the package + containing fullname, otherwise they will be importers for sys.meta_path, + sys.path, and Python's "classic" import machinery, in that order. If + the named module is in a package, that package is imported as a side + effect of invoking this function. + + Non PEP 302 mechanisms (e.g. the Windows registry) used by the + standard import machinery to find files in alternative locations + are partially supported, but are searched AFTER sys.path. Normally, + these locations are searched BEFORE sys.path, preventing sys.path + entries from shadowing them. + + For this to cause a visible difference in behaviour, there must + be a module or package name that is accessible via both sys.path + and one of the non PEP 302 file system mechanisms. In this case, + the emulation will find the former version, while the builtin + import mechanism will find the latter. + + Items of the following types can be affected by this discrepancy: + imp.C_EXTENSION, imp.PY_SOURCE, imp.PY_COMPILED, imp.PKG_DIRECTORY + """ + if fullname.startswith('.'): + raise ImportError("Relative module names not supported") + if '.' in fullname: + # Get the containing package's __path__ + pkg = '.'.join(fullname.split('.')[:-1]) + if pkg not in sys.modules: + __import__(pkg) + path = getattr(sys.modules[pkg], '__path__', None) or [] + else: + for importer in sys.meta_path: + yield importer + path = sys.path + for item in path: + yield get_importer(item) + if '.' not in fullname: + yield ImpImporter() + + +def get_loader(module_or_name): + """Get a PEP 302 "loader" object for module_or_name + + If the module or package is accessible via the normal import + mechanism, a wrapper around the relevant part of that machinery + is returned. Returns None if the module cannot be found or imported. + If the named module is not already imported, its containing package + (if any) is imported, in order to establish the package __path__. + + This function uses iter_importers(), and is thus subject to the same + limitations regarding platform-specific special import locations such + as the Windows registry. + """ + if module_or_name in sys.modules: + module_or_name = sys.modules[module_or_name] + if isinstance(module_or_name, ModuleType): + module = module_or_name + loader = getattr(module, '__loader__', None) + if loader is not None: + return loader + fullname = module.__name__ + else: + fullname = module_or_name + return find_loader(fullname) + + +def find_loader(fullname): + """Find a PEP 302 "loader" object for fullname + + If fullname contains dots, path must be the containing package's __path__. + Returns None if the module cannot be found or imported. This function uses + iter_importers(), and is thus subject to the same limitations regarding + platform-specific special import locations such as the Windows registry. + """ + for importer in iter_importers(fullname): + loader = importer.find_module(fullname) + if loader is not None: + return loader + + return None + + +def extend_path(path, name): + """Extend a package's path. + + Intended use is to place the following code in a package's __init__.py: + + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) + + This will add to the package's __path__ all subdirectories of + directories on sys.path named after the package. This is useful + if one wants to distribute different parts of a single logical + package as multiple directories. + + It also looks for *.pkg files beginning where * matches the name + argument. This feature is similar to *.pth files (see site.py), + except that it doesn't special-case lines starting with 'import'. + A *.pkg file is trusted at face value: apart from checking for + duplicates, all entries found in a *.pkg file are added to the + path, regardless of whether they are exist the filesystem. (This + is a feature.) + + If the input path is not a list (as is the case for frozen + packages) it is returned unchanged. The input path is not + modified; an extended copy is returned. Items are only appended + to the copy at the end. + + It is assumed that sys.path is a sequence. Items of sys.path that + are not (unicode or 8-bit) strings referring to existing + directories are ignored. Unicode items of sys.path that cause + errors when used as filenames may cause this function to raise an + exception (in line with os.path.isdir() behavior). + """ + + if not isinstance(path, list): + # This could happen e.g. when this is called from inside a + # frozen package. Return the path unchanged in that case. + return path + + pname = os.path.join(*name.split('.')) # Reconstitute as relative path + # Just in case os.extsep != '.' + sname = os.extsep.join(name.split('.')) + sname_pkg = sname + os.extsep + "pkg" + init_py = "__init__" + os.extsep + "py" + + path = path[:] # Start with a copy of the existing path + + for dir in sys.path: + if not isinstance(dir, basestring) or not os.path.isdir(dir): + continue + subdir = os.path.join(dir, pname) + # XXX This may still add duplicate entries to path on + # case-insensitive filesystems + initfile = os.path.join(subdir, init_py) + if subdir not in path and os.path.isfile(initfile): + path.append(subdir) + # XXX Is this the right thing for subpackages like zope.app? + # It looks for a file named "zope.app.pkg" + pkgfile = os.path.join(dir, sname_pkg) + if os.path.isfile(pkgfile): + try: + f = open(pkgfile) + except IOError, msg: + sys.stderr.write("Can't open %s: %s\n" % + (pkgfile, msg)) + else: + for line in f: + line = line.rstrip('\n') + if not line or line.startswith('#'): + continue + path.append(line) # Don't check for existence! + f.close() + + return path + + +def get_data(package, resource): + """Get a resource from a package. + + This is a wrapper round the PEP 302 loader get_data API. The package + argument should be the name of a package, in standard module format + (foo.bar). The resource argument should be in the form of a relative + filename, using '/' as the path separator. The parent directory name '..' + is not allowed, and nor is a rooted name (starting with a '/'). + + The function returns a binary string, which is the contents of the + specified resource. + + For packages located in the filesystem, which have already been imported, + this is the rough equivalent of + + d = os.path.dirname(sys.modules[package].__file__) + data = open(os.path.join(d, resource), 'rb').read() + + If the package cannot be located or loaded, or it uses a PEP 302 loader + which does not support get_data(), then None is returned. + """ + + loader = get_loader(package) + if loader is None or not hasattr(loader, 'get_data'): + return None + mod = sys.modules.get(package) or loader.load_module(package) + if mod is None or not hasattr(mod, '__file__'): + return None + + # Modify the resource name to be compatible with the loader.get_data + # signature - an os.path format "filename" starting with the dirname of + # the package's __file__ + parts = resource.split('/') + parts.insert(0, os.path.dirname(mod.__file__)) + resource_name = os.path.join(*parts) + return loader.get_data(resource_name) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/backwardcompat.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/backwardcompat.py new file mode 100644 index 000000000..e7c11f1d3 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/backwardcompat.py @@ -0,0 +1,55 @@ +"""Stuff that isn't in some old versions of Python""" + +import sys +import os +import shutil + +__all__ = ['any', 'WindowsError', 'md5', 'copytree'] + +try: + WindowsError = WindowsError +except NameError: + WindowsError = None +try: + from hashlib import md5 +except ImportError: + import md5 as md5_module + md5 = md5_module.new + +try: + from pkgutil import walk_packages +except ImportError: + # let's fall back as long as we can + from _pkgutil import walk_packages + +try: + any = any +except NameError: + + def any(seq): + for item in seq: + if item: + return True + return False + + +def copytree(src, dst): + if sys.version_info < (2, 5): + before_last_dir = os.path.dirname(dst) + if not os.path.exists(before_last_dir): + os.makedirs(before_last_dir) + shutil.copytree(src, dst) + shutil.copymode(src, dst) + else: + shutil.copytree(src, dst) + + +def product(*args, **kwds): + # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy + # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111 + pools = map(tuple, args) * kwds.get('repeat', 1) + result = [[]] + for pool in pools: + result = [x+[y] for x in result for y in pool] + for prod in result: + yield tuple(prod) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/basecommand.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/basecommand.py new file mode 100644 index 000000000..f450e8393 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/basecommand.py @@ -0,0 +1,203 @@ +"""Base Command class, and related routines""" + +from cStringIO import StringIO +import getpass +import os +import socket +import sys +import traceback +import time +import urllib +import urllib2 + +from pip import commands +from pip.log import logger +from pip.baseparser import parser, ConfigOptionParser, UpdatingDefaultsHelpFormatter +from pip.download import urlopen +from pip.exceptions import BadCommand, InstallationError, UninstallationError +from pip.venv import restart_in_venv +from pip.backwardcompat import walk_packages + +__all__ = ['command_dict', 'Command', 'load_all_commands', + 'load_command', 'command_names'] + +command_dict = {} + +# for backwards compatibiliy +get_proxy = urlopen.get_proxy + + +class Command(object): + name = None + usage = None + hidden = False + + def __init__(self): + assert self.name + self.parser = ConfigOptionParser( + usage=self.usage, + prog='%s %s' % (sys.argv[0], self.name), + version=parser.version, + formatter=UpdatingDefaultsHelpFormatter(), + name=self.name) + for option in parser.option_list: + if not option.dest or option.dest == 'help': + # -h, --version, etc + continue + self.parser.add_option(option) + command_dict[self.name] = self + + def merge_options(self, initial_options, options): + # Make sure we have all global options carried over + for attr in ['log', 'venv', 'proxy', 'venv_base', 'require_venv', + 'respect_venv', 'log_explicit_levels', 'log_file', + 'timeout', 'default_vcs', 'skip_requirements_regex', + 'no_input']: + setattr(options, attr, getattr(initial_options, attr) or getattr(options, attr)) + options.quiet += initial_options.quiet + options.verbose += initial_options.verbose + + def setup_logging(self): + pass + + def main(self, complete_args, args, initial_options): + options, args = self.parser.parse_args(args) + self.merge_options(initial_options, options) + + level = 1 # Notify + level += options.verbose + level -= options.quiet + level = logger.level_for_integer(4-level) + complete_log = [] + logger.consumers.extend( + [(level, sys.stdout), + (logger.DEBUG, complete_log.append)]) + if options.log_explicit_levels: + logger.explicit_levels = True + + self.setup_logging() + + if options.require_venv and not options.venv: + # If a venv is required check if it can really be found + if not os.environ.get('VIRTUAL_ENV'): + logger.fatal('Could not find an activated virtualenv (required).') + sys.exit(3) + # Automatically install in currently activated venv if required + options.respect_venv = True + + if args and args[-1] == '___VENV_RESTART___': + ## FIXME: We don't do anything this this value yet: + args = args[:-2] + options.venv = None + else: + # If given the option to respect the activated environment + # check if no venv is given as a command line parameter + if options.respect_venv and os.environ.get('VIRTUAL_ENV'): + if options.venv and os.path.exists(options.venv): + # Make sure command line venv and environmental are the same + if (os.path.realpath(os.path.expanduser(options.venv)) != + os.path.realpath(os.environ.get('VIRTUAL_ENV'))): + logger.fatal("Given virtualenv (%s) doesn't match " + "currently activated virtualenv (%s)." + % (options.venv, os.environ.get('VIRTUAL_ENV'))) + sys.exit(3) + else: + options.venv = os.environ.get('VIRTUAL_ENV') + logger.info('Using already activated environment %s' % options.venv) + if options.venv: + logger.info('Running in environment %s' % options.venv) + site_packages=False + if options.site_packages: + site_packages=True + restart_in_venv(options.venv, options.venv_base, site_packages, + complete_args) + # restart_in_venv should actually never return, but for clarity... + return + + ## FIXME: not sure if this sure come before or after venv restart + if options.log: + log_fp = open_logfile(options.log, 'a') + logger.consumers.append((logger.DEBUG, log_fp)) + else: + log_fp = None + + socket.setdefaulttimeout(options.timeout or None) + + urlopen.setup(proxystr=options.proxy, prompting=not options.no_input) + + exit = 0 + try: + self.run(options, args) + except (InstallationError, UninstallationError), e: + logger.fatal(str(e)) + logger.info('Exception information:\n%s' % format_exc()) + exit = 1 + except BadCommand, e: + logger.fatal(str(e)) + logger.info('Exception information:\n%s' % format_exc()) + exit = 1 + except: + logger.fatal('Exception:\n%s' % format_exc()) + exit = 2 + + if log_fp is not None: + log_fp.close() + if exit: + log_fn = options.log_file + text = '\n'.join(complete_log) + logger.fatal('Storing complete log in %s' % log_fn) + log_fp = open_logfile(log_fn, 'w') + log_fp.write(text) + log_fp.close() + return exit + + + + +def format_exc(exc_info=None): + if exc_info is None: + exc_info = sys.exc_info() + out = StringIO() + traceback.print_exception(*exc_info, **dict(file=out)) + return out.getvalue() + + +def open_logfile(filename, mode='a'): + """Open the named log file in append mode. + + If the file already exists, a separator will also be printed to + the file to separate past activity from current activity. + """ + filename = os.path.expanduser(filename) + filename = os.path.abspath(filename) + dirname = os.path.dirname(filename) + if not os.path.exists(dirname): + os.makedirs(dirname) + exists = os.path.exists(filename) + + log_fp = open(filename, mode) + if exists: + print >> log_fp, '-'*60 + print >> log_fp, '%s run on %s' % (sys.argv[0], time.strftime('%c')) + return log_fp + + +def load_command(name): + full_name = 'pip.commands.%s' % name + if full_name in sys.modules: + return + try: + __import__(full_name) + except ImportError: + pass + + +def load_all_commands(): + for name in command_names(): + load_command(name) + + +def command_names(): + names = set((pkg[1] for pkg in walk_packages(path=commands.__path__))) + return list(names) + diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/baseparser.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/baseparser.py new file mode 100644 index 000000000..a8bd6ce4f --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/baseparser.py @@ -0,0 +1,231 @@ +"""Base option parser setup""" + +import sys +import optparse +import pkg_resources +import ConfigParser +import os +from distutils.util import strtobool +from pip.locations import default_config_file, default_log_file + + +class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter): + """Custom help formatter for use in ConfigOptionParser that updates + the defaults before expanding them, allowing them to show up correctly + in the help listing""" + + def expand_default(self, option): + if self.parser is not None: + self.parser.update_defaults(self.parser.defaults) + return optparse.IndentedHelpFormatter.expand_default(self, option) + + +class ConfigOptionParser(optparse.OptionParser): + """Custom option parser which updates its defaults by by checking the + configuration files and environmental variables""" + + def __init__(self, *args, **kwargs): + self.config = ConfigParser.RawConfigParser() + self.name = kwargs.pop('name') + self.files = self.get_config_files() + self.config.read(self.files) + assert self.name + optparse.OptionParser.__init__(self, *args, **kwargs) + + def get_config_files(self): + config_file = os.environ.get('PIP_CONFIG_FILE', False) + if config_file and os.path.exists(config_file): + return [config_file] + return [default_config_file] + + def update_defaults(self, defaults): + """Updates the given defaults with values from the config files and + the environ. Does a little special handling for certain types of + options (lists).""" + # Then go and look for the other sources of configuration: + config = {} + # 1. config files + for section in ('global', self.name): + config.update(dict(self.get_config_section(section))) + # 2. environmental variables + config.update(dict(self.get_environ_vars())) + # Then set the options with those values + for key, val in config.iteritems(): + key = key.replace('_', '-') + if not key.startswith('--'): + key = '--%s' % key # only prefer long opts + option = self.get_option(key) + if option is not None: + # ignore empty values + if not val: + continue + # handle multiline configs + if option.action == 'append': + val = val.split() + else: + option.nargs = 1 + if option.action in ('store_true', 'store_false', 'count'): + val = strtobool(val) + try: + val = option.convert_value(key, val) + except optparse.OptionValueError, e: + print ("An error occured during configuration: %s" % e) + sys.exit(3) + defaults[option.dest] = val + return defaults + + def get_config_section(self, name): + """Get a section of a configuration""" + if self.config.has_section(name): + return self.config.items(name) + return [] + + def get_environ_vars(self, prefix='PIP_'): + """Returns a generator with all environmental vars with prefix PIP_""" + for key, val in os.environ.iteritems(): + if key.startswith(prefix): + yield (key.replace(prefix, '').lower(), val) + + def get_default_values(self): + """Overridding to make updating the defaults after instantiation of + the option parser possible, update_defaults() does the dirty work.""" + if not self.process_default_values: + # Old, pre-Optik 1.5 behaviour. + return optparse.Values(self.defaults) + + defaults = self.update_defaults(self.defaults.copy()) # ours + for option in self._get_all_options(): + default = defaults.get(option.dest) + if isinstance(default, basestring): + opt_str = option.get_opt_string() + defaults[option.dest] = option.check_value(opt_str, default) + return optparse.Values(defaults) + +try: + pip_dist = pkg_resources.get_distribution('pip') + version = '%s from %s (python %s)' % ( + pip_dist, pip_dist.location, sys.version[:3]) +except pkg_resources.DistributionNotFound: + # when running pip.py without installing + version=None + +parser = ConfigOptionParser( + usage='%prog COMMAND [OPTIONS]', + version=version, + add_help_option=False, + formatter=UpdatingDefaultsHelpFormatter(), + name='global') + +parser.add_option( + '-h', '--help', + dest='help', + action='store_true', + help='Show help') +parser.add_option( + '-E', '--environment', + dest='venv', + metavar='DIR', + help='virtualenv environment to run pip in (either give the ' + 'interpreter or the environment base directory)') +parser.add_option( + '-s', '--enable-site-packages', + dest='site_packages', + action='store_true', + help='Include site-packages in virtualenv if one is to be ' + 'created. Ignored if --environment is not used or ' + 'the virtualenv already exists.') +parser.add_option( + # Defines a default root directory for virtualenvs, relative + # virtualenvs names/paths are considered relative to it. + '--virtualenv-base', + dest='venv_base', + type='str', + default='', + help=optparse.SUPPRESS_HELP) +parser.add_option( + # Run only if inside a virtualenv, bail if not. + '--require-virtualenv', '--require-venv', + dest='require_venv', + action='store_true', + default=False, + help=optparse.SUPPRESS_HELP) +parser.add_option( + # Use automatically an activated virtualenv instead of installing + # globally. -E will be ignored if used. + '--respect-virtualenv', '--respect-venv', + dest='respect_venv', + action='store_true', + default=False, + help=optparse.SUPPRESS_HELP) + +parser.add_option( + '-v', '--verbose', + dest='verbose', + action='count', + default=0, + help='Give more output') +parser.add_option( + '-q', '--quiet', + dest='quiet', + action='count', + default=0, + help='Give less output') +parser.add_option( + '--log', + dest='log', + metavar='FILENAME', + help='Log file where a complete (maximum verbosity) record will be kept') +parser.add_option( + # Writes the log levels explicitely to the log' + '--log-explicit-levels', + dest='log_explicit_levels', + action='store_true', + default=False, + help=optparse.SUPPRESS_HELP) +parser.add_option( + # The default log file + '--local-log', '--log-file', + dest='log_file', + metavar='FILENAME', + default=default_log_file, + help=optparse.SUPPRESS_HELP) +parser.add_option( + # Don't ask for input + '--no-input', + dest='no_input', + action='store_true', + default=False, + help=optparse.SUPPRESS_HELP) + +parser.add_option( + '--proxy', + dest='proxy', + type='str', + default='', + help="Specify a proxy in the form user:passwd@proxy.server:port. " + "Note that the user:password@ is optional and required only if you " + "are behind an authenticated proxy. If you provide " + "user@proxy.server:port then you will be prompted for a password.") +parser.add_option( + '--timeout', '--default-timeout', + metavar='SECONDS', + dest='timeout', + type='float', + default=15, + help='Set the socket timeout (default %default seconds)') +parser.add_option( + # The default version control system for editables, e.g. 'svn' + '--default-vcs', + dest='default_vcs', + type='str', + default='', + help=optparse.SUPPRESS_HELP) +parser.add_option( + # A regex to be used to skip requirements + '--skip-requirements-regex', + dest='skip_requirements_regex', + type='str', + default='', + help=optparse.SUPPRESS_HELP) + +parser.disable_interspersed_args() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/__init__.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/__init__.py new file mode 100644 index 000000000..792d60054 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/__init__.py @@ -0,0 +1 @@ +# diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/bundle.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/bundle.py new file mode 100644 index 000000000..fb0f75704 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/bundle.py @@ -0,0 +1,33 @@ +from pip.locations import build_prefix, src_prefix +from pip.util import display_path, backup_dir +from pip.log import logger +from pip.exceptions import InstallationError +from pip.commands.install import InstallCommand + + +class BundleCommand(InstallCommand): + name = 'bundle' + usage = '%prog [OPTIONS] BUNDLE_NAME.pybundle PACKAGE_NAMES...' + summary = 'Create pybundles (archives containing multiple packages)' + bundle = True + + def __init__(self): + super(BundleCommand, self).__init__() + + def run(self, options, args): + if not args: + raise InstallationError('You must give a bundle filename') + if not options.build_dir: + options.build_dir = backup_dir(build_prefix, '-bundle') + if not options.src_dir: + options.src_dir = backup_dir(src_prefix, '-bundle') + # We have to get everything when creating a bundle: + options.ignore_installed = True + logger.notify('Putting temporary build files in %s and source/develop files in %s' + % (display_path(options.build_dir), display_path(options.src_dir))) + self.bundle_filename = args.pop(0) + requirement_set = super(BundleCommand, self).run(options, args) + return requirement_set + + +BundleCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/completion.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/completion.py new file mode 100644 index 000000000..d003b9ae3 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/completion.py @@ -0,0 +1,60 @@ +import sys +from pip.basecommand import Command + +BASE_COMPLETION = """ +# pip %(shell)s completion start%(script)s# pip %(shell)s completion end +""" + +COMPLETION_SCRIPTS = { + 'bash': """ +_pip_completion() +{ + COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \\ + COMP_CWORD=$COMP_CWORD \\ + PIP_AUTO_COMPLETE=1 $1 ) ) +} +complete -o default -F _pip_completion pip +""", 'zsh': """ +function _pip_completion { + local words cword + read -Ac words + read -cn cword + reply=( $( COMP_WORDS="$words[*]" \\ + COMP_CWORD=$(( cword-1 )) \\ + PIP_AUTO_COMPLETE=1 $words[1] ) ) +} +compctl -K _pip_completion pip +"""} + + +class CompletionCommand(Command): + name = 'completion' + summary = 'A helper command to be used for command completion' + hidden = True + + def __init__(self): + super(CompletionCommand, self).__init__() + self.parser.add_option( + '--bash', '-b', + action='store_const', + const='bash', + dest='shell', + help='Emit completion code for bash') + self.parser.add_option( + '--zsh', '-z', + action='store_const', + const='zsh', + dest='shell', + help='Emit completion code for zsh') + + def run(self, options, args): + """Prints the completion code of the given shell""" + shells = COMPLETION_SCRIPTS.keys() + shell_options = ['--'+shell for shell in sorted(shells)] + if options.shell in shells: + script = COMPLETION_SCRIPTS.get(options.shell, '') + print BASE_COMPLETION % {'script': script, 'shell': options.shell} + else: + sys.stderr.write('ERROR: You must pass %s\n' % ' or '.join(shell_options)) + +CompletionCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/freeze.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/freeze.py new file mode 100644 index 000000000..01b5df934 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/freeze.py @@ -0,0 +1,109 @@ +import re +import sys +import pkg_resources +import pip +from pip.req import InstallRequirement +from pip.log import logger +from pip.basecommand import Command +from pip.util import get_installed_distributions + + +class FreezeCommand(Command): + name = 'freeze' + usage = '%prog [OPTIONS]' + summary = 'Output all currently installed packages (exact versions) to stdout' + + def __init__(self): + super(FreezeCommand, self).__init__() + self.parser.add_option( + '-r', '--requirement', + dest='requirement', + action='store', + default=None, + metavar='FILENAME', + help='Use the given requirements file as a hint about how to generate the new frozen requirements') + self.parser.add_option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='URL', + help='URL for finding packages, which will be added to the frozen requirements file') + self.parser.add_option( + '-l', '--local', + dest='local', + action='store_true', + default=False, + help='If in a virtualenv, do not report globally-installed packages') + + def setup_logging(self): + logger.move_stdout_to_stderr() + + def run(self, options, args): + requirement = options.requirement + find_links = options.find_links or [] + local_only = options.local + ## FIXME: Obviously this should be settable: + find_tags = False + skip_match = None + + skip_regex = options.skip_requirements_regex + if skip_regex: + skip_match = re.compile(skip_regex) + + dependency_links = [] + + f = sys.stdout + + for dist in pkg_resources.working_set: + if dist.has_metadata('dependency_links.txt'): + dependency_links.extend(dist.get_metadata_lines('dependency_links.txt')) + for link in find_links: + if '#egg=' in link: + dependency_links.append(link) + for link in find_links: + f.write('-f %s\n' % link) + installations = {} + for dist in get_installed_distributions(local_only=local_only): + req = pip.FrozenRequirement.from_dist(dist, dependency_links, find_tags=find_tags) + installations[req.name] = req + if requirement: + req_f = open(requirement) + for line in req_f: + if not line.strip() or line.strip().startswith('#'): + f.write(line) + continue + if skip_match and skip_match.search(line): + f.write(line) + continue + elif line.startswith('-e') or line.startswith('--editable'): + if line.startswith('-e'): + line = line[2:].strip() + else: + line = line[len('--editable'):].strip().lstrip('=') + line_req = InstallRequirement.from_editable(line, default_vcs=options.default_vcs) + elif (line.startswith('-r') or line.startswith('--requirement') + or line.startswith('-Z') or line.startswith('--always-unzip') + or line.startswith('-f') or line.startswith('-i') + or line.startswith('--extra-index-url')): + f.write(line) + continue + else: + line_req = InstallRequirement.from_line(line) + if not line_req.name: + logger.notify("Skipping line because it's not clear what it would install: %s" + % line.strip()) + logger.notify(" (add #egg=PackageName to the URL to avoid this warning)") + continue + if line_req.name not in installations: + logger.warn("Requirement file contains %s, but that package is not installed" + % line.strip()) + continue + f.write(str(installations[line_req.name])) + del installations[line_req.name] + f.write('## The following requirements were added by pip --freeze:\n') + for installation in sorted(installations.values(), key=lambda x: x.name): + f.write(str(installation)) + + +FreezeCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/help.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/help.py new file mode 100644 index 000000000..b0b366112 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/help.py @@ -0,0 +1,32 @@ +from pip.basecommand import Command, command_dict, load_all_commands +from pip.exceptions import InstallationError +from pip.baseparser import parser + + +class HelpCommand(Command): + name = 'help' + usage = '%prog' + summary = 'Show available commands' + + def run(self, options, args): + load_all_commands() + if args: + ## FIXME: handle errors better here + command = args[0] + if command not in command_dict: + raise InstallationError('No command with the name: %s' % command) + command = command_dict[command] + command.parser.print_help() + return + parser.print_help() + print + print 'Commands available:' + commands = list(set(command_dict.values())) + commands.sort(key=lambda x: x.name) + for command in commands: + if command.hidden: + continue + print ' %s: %s' % (command.name, command.summary) + + +HelpCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/install.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/install.py new file mode 100644 index 000000000..861c332bf --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/install.py @@ -0,0 +1,247 @@ +import os, sys +from pip.req import InstallRequirement, RequirementSet +from pip.req import parse_requirements +from pip.log import logger +from pip.locations import build_prefix, src_prefix +from pip.basecommand import Command +from pip.index import PackageFinder +from pip.exceptions import InstallationError + + +class InstallCommand(Command): + name = 'install' + usage = '%prog [OPTIONS] PACKAGE_NAMES...' + summary = 'Install packages' + bundle = False + + def __init__(self): + super(InstallCommand, self).__init__() + self.parser.add_option( + '-e', '--editable', + dest='editables', + action='append', + default=[], + metavar='VCS+REPOS_URL[@REV]#egg=PACKAGE', + help='Install a package directly from a checkout. Source will be checked ' + 'out into src/PACKAGE (lower-case) and installed in-place (using ' + 'setup.py develop). You can run this on an existing directory/checkout (like ' + 'pip install -e src/mycheckout). This option may be provided multiple times. ' + 'Possible values for VCS are: svn, git, hg and bzr.') + self.parser.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='FILENAME', + help='Install all the packages listed in the given requirements file. ' + 'This option can be used multiple times.') + self.parser.add_option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='URL', + help='URL to look for packages at') + self.parser.add_option( + '-i', '--index-url', '--pypi-url', + dest='index_url', + metavar='URL', + default='http://pypi.python.org/simple/', + help='Base URL of Python Package Index (default %default)') + self.parser.add_option( + '--extra-index-url', + dest='extra_index_urls', + metavar='URL', + action='append', + default=[], + help='Extra URLs of package indexes to use in addition to --index-url') + self.parser.add_option( + '--no-index', + dest='no_index', + action='store_true', + default=False, + help='Ignore package index (only looking at --find-links URLs instead)') + self.parser.add_option( + '-M', '--use-mirrors', + dest='use_mirrors', + action='store_true', + default=False, + help='Use the PyPI mirrors as a fallback in case the main index is down.') + self.parser.add_option( + '--mirrors', + dest='mirrors', + metavar='URL', + action='append', + default=[], + help='Specific mirror URLs to query when --use-mirrors is used') + + self.parser.add_option( + '-b', '--build', '--build-dir', '--build-directory', + dest='build_dir', + metavar='DIR', + default=None, + help='Unpack packages into DIR (default %s) and build from there' % build_prefix) + self.parser.add_option( + '-d', '--download', '--download-dir', '--download-directory', + dest='download_dir', + metavar='DIR', + default=None, + help='Download packages into DIR instead of installing them') + self.parser.add_option( + '--download-cache', + dest='download_cache', + metavar='DIR', + default=None, + help='Cache downloaded packages in DIR') + self.parser.add_option( + '--src', '--source', '--source-dir', '--source-directory', + dest='src_dir', + metavar='DIR', + default=None, + help='Check out --editable packages into DIR (default %s)' % src_prefix) + + self.parser.add_option( + '-U', '--upgrade', + dest='upgrade', + action='store_true', + help='Upgrade all packages to the newest available version') + self.parser.add_option( + '-I', '--ignore-installed', + dest='ignore_installed', + action='store_true', + help='Ignore the installed packages (reinstalling instead)') + self.parser.add_option( + '--no-deps', '--no-dependencies', + dest='ignore_dependencies', + action='store_true', + default=False, + help='Ignore package dependencies') + self.parser.add_option( + '--no-install', + dest='no_install', + action='store_true', + help="Download and unpack all packages, but don't actually install them") + self.parser.add_option( + '--no-download', + dest='no_download', + action="store_true", + help="Don't download any packages, just install the ones already downloaded " + "(completes an install run with --no-install)") + + self.parser.add_option( + '--install-option', + dest='install_options', + action='append', + help="Extra arguments to be supplied to the setup.py install " + "command (use like --install-option=\"--install-scripts=/usr/local/bin\"). " + "Use multiple --install-option options to pass multiple options to setup.py install. " + "If you are using an option with a directory path, be sure to use absolute path.") + + self.parser.add_option( + '--global-option', + dest='global_options', + action='append', + help="Extra global options to be supplied to the setup.py" + "call before the install command") + + self.parser.add_option( + '--user', + dest='use_user_site', + action='store_true', + help='Install to user-site') + + def _build_package_finder(self, options, index_urls): + """ + Create a package finder appropriate to this install command. + This method is meant to be overridden by subclasses, not + called directly. + """ + return PackageFinder(find_links=options.find_links, + index_urls=index_urls, + use_mirrors=options.use_mirrors, + mirrors=options.mirrors) + + def run(self, options, args): + if not options.build_dir: + options.build_dir = build_prefix + if not options.src_dir: + options.src_dir = src_prefix + if options.download_dir: + options.no_install = True + options.ignore_installed = True + options.build_dir = os.path.abspath(options.build_dir) + options.src_dir = os.path.abspath(options.src_dir) + install_options = options.install_options or [] + if options.use_user_site: + install_options.append('--user') + global_options = options.global_options or [] + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.notify('Ignoring indexes: %s' % ','.join(index_urls)) + index_urls = [] + + finder = self._build_package_finder(options, index_urls) + + requirement_set = RequirementSet( + build_dir=options.build_dir, + src_dir=options.src_dir, + download_dir=options.download_dir, + download_cache=options.download_cache, + upgrade=options.upgrade, + ignore_installed=options.ignore_installed, + ignore_dependencies=options.ignore_dependencies) + for name in args: + requirement_set.add_requirement( + InstallRequirement.from_line(name, None)) + for name in options.editables: + requirement_set.add_requirement( + InstallRequirement.from_editable(name, default_vcs=options.default_vcs)) + for filename in options.requirements: + for req in parse_requirements(filename, finder=finder, options=options): + requirement_set.add_requirement(req) + + if not requirement_set.has_requirements: + if options.find_links: + raise InstallationError('You must give at least one ' + 'requirement to %s (maybe you meant "pip install %s"?)' + % (self.name, " ".join(options.find_links))) + raise InstallationError('You must give at least one requirement ' + 'to %(name)s (see "pip help %(name)s")' % dict(name=self.name)) + + if (options.use_user_site and + sys.version_info < (2, 6)): + raise InstallationError('--user is only supported in Python version 2.6 and newer') + + import setuptools + if (options.use_user_site and + requirement_set.has_editables and + not getattr(setuptools, '_distribute', False)): + + raise InstallationError('--user --editable not supported with setuptools, use distribute') + + if not options.no_download: + requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle) + else: + requirement_set.locate_files() + + if not options.no_install and not self.bundle: + requirement_set.install(install_options, global_options) + installed = ' '.join([req.name for req in + requirement_set.successfully_installed]) + if installed: + logger.notify('Successfully installed %s' % installed) + elif not self.bundle: + downloaded = ' '.join([req.name for req in + requirement_set.successfully_downloaded]) + if downloaded: + logger.notify('Successfully downloaded %s' % downloaded) + elif self.bundle: + requirement_set.create_bundle(self.bundle_filename) + logger.notify('Created bundle in %s' % self.bundle_filename) + # Clean up + if not options.no_install: + requirement_set.cleanup_files(bundle=self.bundle) + return requirement_set + + +InstallCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/search.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/search.py new file mode 100644 index 000000000..73da58ac6 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/search.py @@ -0,0 +1,116 @@ +import sys +import xmlrpclib +import textwrap +import pkg_resources +import pip.download +from pip.basecommand import Command +from pip.util import get_terminal_size +from pip.log import logger +from distutils.version import StrictVersion, LooseVersion + + +class SearchCommand(Command): + name = 'search' + usage = '%prog QUERY' + summary = 'Search PyPI' + + def __init__(self): + super(SearchCommand, self).__init__() + self.parser.add_option( + '--index', + dest='index', + metavar='URL', + default='http://pypi.python.org/pypi', + help='Base URL of Python Package Index (default %default)') + + def run(self, options, args): + if not args: + logger.warn('ERROR: Missing required argument (search query).') + return + query = ' '.join(args) + index_url = options.index + + pypi_hits = self.search(query, index_url) + hits = transform_hits(pypi_hits) + + terminal_width = None + if sys.stdout.isatty(): + terminal_width = get_terminal_size()[0] + + print_results(hits, terminal_width=terminal_width) + + def search(self, query, index_url): + pypi = xmlrpclib.ServerProxy(index_url, pip.download.xmlrpclib_transport) + hits = pypi.search({'name': query, 'summary': query}, 'or') + return hits + + +def transform_hits(hits): + """ + The list from pypi is really a list of versions. We want a list of + packages with the list of versions stored inline. This converts the + list from pypi into one we can use. + """ + packages = {} + for hit in hits: + name = hit['name'] + summary = hit['summary'] + version = hit['version'] + score = hit['_pypi_ordering'] + + if name not in packages.keys(): + packages[name] = {'name': name, 'summary': summary, 'versions': [version], 'score': score} + else: + packages[name]['versions'].append(version) + + # if this is the highest version, replace summary and score + if version == highest_version(packages[name]['versions']): + packages[name]['summary'] = summary + packages[name]['score'] = score + + # each record has a unique name now, so we will convert the dict into a list sorted by score + package_list = sorted(packages.values(), lambda x, y: cmp(y['score'], x['score'])) + return package_list + + +def print_results(hits, name_column_width=25, terminal_width=None): + installed_packages = [p.project_name for p in pkg_resources.working_set] + for hit in hits: + name = hit['name'] + summary = hit['summary'] or '' + if terminal_width is not None: + # wrap and indent summary to fit terminal + summary = textwrap.wrap(summary, terminal_width - name_column_width - 5) + summary = ('\n' + ' ' * (name_column_width + 3)).join(summary) + line = '%s - %s' % (name.ljust(name_column_width), summary) + try: + logger.notify(line) + if name in installed_packages: + dist = pkg_resources.get_distribution(name) + logger.indent += 2 + try: + latest = highest_version(hit['versions']) + if dist.version == latest: + logger.notify('INSTALLED: %s (latest)' % dist.version) + else: + logger.notify('INSTALLED: %s' % dist.version) + logger.notify('LATEST: %s' % latest) + finally: + logger.indent -= 2 + except UnicodeEncodeError: + pass + + +def compare_versions(version1, version2): + try: + return cmp(StrictVersion(version1), StrictVersion(version2)) + # in case of abnormal version number, fall back to LooseVersion + except ValueError: + return cmp(LooseVersion(version1), LooseVersion(version2)) + + +def highest_version(versions): + return reduce((lambda v1, v2: compare_versions(v1, v2) == 1 and v1 or v2), versions) + + +SearchCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/uninstall.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/uninstall.py new file mode 100644 index 000000000..7effd844e --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/uninstall.py @@ -0,0 +1,42 @@ +from pip.req import InstallRequirement, RequirementSet, parse_requirements +from pip.basecommand import Command +from pip.exceptions import InstallationError + +class UninstallCommand(Command): + name = 'uninstall' + usage = '%prog [OPTIONS] PACKAGE_NAMES ...' + summary = 'Uninstall packages' + + def __init__(self): + super(UninstallCommand, self).__init__() + self.parser.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='FILENAME', + help='Uninstall all the packages listed in the given requirements file. ' + 'This option can be used multiple times.') + self.parser.add_option( + '-y', '--yes', + dest='yes', + action='store_true', + help="Don't ask for confirmation of uninstall deletions.") + + def run(self, options, args): + requirement_set = RequirementSet( + build_dir=None, + src_dir=None, + download_dir=None) + for name in args: + requirement_set.add_requirement( + InstallRequirement.from_line(name)) + for filename in options.requirements: + for req in parse_requirements(filename, options=options): + requirement_set.add_requirement(req) + if not requirement_set.has_requirements: + raise InstallationError('You must give at least one requirement ' + 'to %(name)s (see "pip help %(name)s")' % dict(name=self.name)) + requirement_set.uninstall(auto_confirm=options.yes) + +UninstallCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/unzip.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/unzip.py new file mode 100644 index 000000000..f83e18205 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/unzip.py @@ -0,0 +1,9 @@ +from pip.commands.zip import ZipCommand + + +class UnzipCommand(ZipCommand): + name = 'unzip' + summary = 'Unzip individual packages' + + +UnzipCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/zip.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/zip.py new file mode 100644 index 000000000..346fc0519 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/zip.py @@ -0,0 +1,346 @@ +import sys +import re +import fnmatch +import os +import shutil +import zipfile +from pip.util import display_path, backup_dir +from pip.log import logger +from pip.exceptions import InstallationError +from pip.basecommand import Command + + +class ZipCommand(Command): + name = 'zip' + usage = '%prog [OPTIONS] PACKAGE_NAMES...' + summary = 'Zip individual packages' + + def __init__(self): + super(ZipCommand, self).__init__() + if self.name == 'zip': + self.parser.add_option( + '--unzip', + action='store_true', + dest='unzip', + help='Unzip (rather than zip) a package') + else: + self.parser.add_option( + '--zip', + action='store_false', + dest='unzip', + default=True, + help='Zip (rather than unzip) a package') + self.parser.add_option( + '--no-pyc', + action='store_true', + dest='no_pyc', + help='Do not include .pyc files in zip files (useful on Google App Engine)') + self.parser.add_option( + '-l', '--list', + action='store_true', + dest='list', + help='List the packages available, and their zip status') + self.parser.add_option( + '--sort-files', + action='store_true', + dest='sort_files', + help='With --list, sort packages according to how many files they contain') + self.parser.add_option( + '--path', + action='append', + dest='paths', + help='Restrict operations to the given paths (may include wildcards)') + self.parser.add_option( + '-n', '--simulate', + action='store_true', + help='Do not actually perform the zip/unzip operation') + + def paths(self): + """All the entries of sys.path, possibly restricted by --path""" + if not self.select_paths: + return sys.path + result = [] + match_any = set() + for path in sys.path: + path = os.path.normcase(os.path.abspath(path)) + for match in self.select_paths: + match = os.path.normcase(os.path.abspath(match)) + if '*' in match: + if re.search(fnmatch.translate(match+'*'), path): + result.append(path) + match_any.add(match) + break + else: + if path.startswith(match): + result.append(path) + match_any.add(match) + break + else: + logger.debug("Skipping path %s because it doesn't match %s" + % (path, ', '.join(self.select_paths))) + for match in self.select_paths: + if match not in match_any and '*' not in match: + result.append(match) + logger.debug("Adding path %s because it doesn't match anything already on sys.path" + % match) + return result + + def run(self, options, args): + self.select_paths = options.paths + self.simulate = options.simulate + if options.list: + return self.list(options, args) + if not args: + raise InstallationError( + 'You must give at least one package to zip or unzip') + packages = [] + for arg in args: + module_name, filename = self.find_package(arg) + if options.unzip and os.path.isdir(filename): + raise InstallationError( + 'The module %s (in %s) is not a zip file; cannot be unzipped' + % (module_name, filename)) + elif not options.unzip and not os.path.isdir(filename): + raise InstallationError( + 'The module %s (in %s) is not a directory; cannot be zipped' + % (module_name, filename)) + packages.append((module_name, filename)) + last_status = None + for module_name, filename in packages: + if options.unzip: + last_status = self.unzip_package(module_name, filename) + else: + last_status = self.zip_package(module_name, filename, options.no_pyc) + return last_status + + def unzip_package(self, module_name, filename): + zip_filename = os.path.dirname(filename) + if not os.path.isfile(zip_filename) and zipfile.is_zipfile(zip_filename): + raise InstallationError( + 'Module %s (in %s) isn\'t located in a zip file in %s' + % (module_name, filename, zip_filename)) + package_path = os.path.dirname(zip_filename) + if not package_path in self.paths(): + logger.warn( + 'Unpacking %s into %s, but %s is not on sys.path' + % (display_path(zip_filename), display_path(package_path), + display_path(package_path))) + logger.notify('Unzipping %s (in %s)' % (module_name, display_path(zip_filename))) + if self.simulate: + logger.notify('Skipping remaining operations because of --simulate') + return + logger.indent += 2 + try: + ## FIXME: this should be undoable: + zip = zipfile.ZipFile(zip_filename) + to_save = [] + for name in zip.namelist(): + if name.startswith(module_name + os.path.sep): + content = zip.read(name) + dest = os.path.join(package_path, name) + if not os.path.exists(os.path.dirname(dest)): + os.makedirs(os.path.dirname(dest)) + if not content and dest.endswith(os.path.sep): + if not os.path.exists(dest): + os.makedirs(dest) + else: + f = open(dest, 'wb') + f.write(content) + f.close() + else: + to_save.append((name, zip.read(name))) + zip.close() + if not to_save: + logger.info('Removing now-empty zip file %s' % display_path(zip_filename)) + os.unlink(zip_filename) + self.remove_filename_from_pth(zip_filename) + else: + logger.info('Removing entries in %s/ from zip file %s' % (module_name, display_path(zip_filename))) + zip = zipfile.ZipFile(zip_filename, 'w') + for name, content in to_save: + zip.writestr(name, content) + zip.close() + finally: + logger.indent -= 2 + + def zip_package(self, module_name, filename, no_pyc): + orig_filename = filename + logger.notify('Zip %s (in %s)' % (module_name, display_path(filename))) + logger.indent += 2 + if filename.endswith('.egg'): + dest_filename = filename + else: + dest_filename = filename + '.zip' + try: + ## FIXME: I think this needs to be undoable: + if filename == dest_filename: + filename = backup_dir(orig_filename) + logger.notify('Moving %s aside to %s' % (orig_filename, filename)) + if not self.simulate: + shutil.move(orig_filename, filename) + try: + logger.info('Creating zip file in %s' % display_path(dest_filename)) + if not self.simulate: + zip = zipfile.ZipFile(dest_filename, 'w') + zip.writestr(module_name + '/', '') + for dirpath, dirnames, filenames in os.walk(filename): + if no_pyc: + filenames = [f for f in filenames + if not f.lower().endswith('.pyc')] + for fns, is_dir in [(dirnames, True), (filenames, False)]: + for fn in fns: + full = os.path.join(dirpath, fn) + dest = os.path.join(module_name, dirpath[len(filename):].lstrip(os.path.sep), fn) + if is_dir: + zip.writestr(dest+'/', '') + else: + zip.write(full, dest) + zip.close() + logger.info('Removing old directory %s' % display_path(filename)) + if not self.simulate: + shutil.rmtree(filename) + except: + ## FIXME: need to do an undo here + raise + ## FIXME: should also be undone: + self.add_filename_to_pth(dest_filename) + finally: + logger.indent -= 2 + + def remove_filename_from_pth(self, filename): + for pth in self.pth_files(): + f = open(pth, 'r') + lines = f.readlines() + f.close() + new_lines = [ + l for l in lines if l.strip() != filename] + if lines != new_lines: + logger.info('Removing reference to %s from .pth file %s' + % (display_path(filename), display_path(pth))) + if not filter(None, new_lines): + logger.info('%s file would be empty: deleting' % display_path(pth)) + if not self.simulate: + os.unlink(pth) + else: + if not self.simulate: + f = open(pth, 'wb') + f.writelines(new_lines) + f.close() + return + logger.warn('Cannot find a reference to %s in any .pth file' % display_path(filename)) + + def add_filename_to_pth(self, filename): + path = os.path.dirname(filename) + dest = os.path.join(path, filename + '.pth') + if path not in self.paths(): + logger.warn('Adding .pth file %s, but it is not on sys.path' % display_path(dest)) + if not self.simulate: + if os.path.exists(dest): + f = open(dest) + lines = f.readlines() + f.close() + if lines and not lines[-1].endswith('\n'): + lines[-1] += '\n' + lines.append(filename+'\n') + else: + lines = [filename + '\n'] + f = open(dest, 'wb') + f.writelines(lines) + f.close() + + def pth_files(self): + for path in self.paths(): + if not os.path.exists(path) or not os.path.isdir(path): + continue + for filename in os.listdir(path): + if filename.endswith('.pth'): + yield os.path.join(path, filename) + + def find_package(self, package): + for path in self.paths(): + full = os.path.join(path, package) + if os.path.exists(full): + return package, full + if not os.path.isdir(path) and zipfile.is_zipfile(path): + zip = zipfile.ZipFile(path, 'r') + try: + zip.read(os.path.join(package, '__init__.py')) + except KeyError: + pass + else: + zip.close() + return package, full + zip.close() + ## FIXME: need special error for package.py case: + raise InstallationError( + 'No package with the name %s found' % package) + + def list(self, options, args): + if args: + raise InstallationError( + 'You cannot give an argument with --list') + for path in sorted(self.paths()): + if not os.path.exists(path): + continue + basename = os.path.basename(path.rstrip(os.path.sep)) + if os.path.isfile(path) and zipfile.is_zipfile(path): + if os.path.dirname(path) not in self.paths(): + logger.notify('Zipped egg: %s' % display_path(path)) + continue + if (basename != 'site-packages' and basename != 'dist-packages' + and not path.replace('\\', '/').endswith('lib/python')): + continue + logger.notify('In %s:' % display_path(path)) + logger.indent += 2 + zipped = [] + unzipped = [] + try: + for filename in sorted(os.listdir(path)): + ext = os.path.splitext(filename)[1].lower() + if ext in ('.pth', '.egg-info', '.egg-link'): + continue + if ext == '.py': + logger.info('Not displaying %s: not a package' % display_path(filename)) + continue + full = os.path.join(path, filename) + if os.path.isdir(full): + unzipped.append((filename, self.count_package(full))) + elif zipfile.is_zipfile(full): + zipped.append(filename) + else: + logger.info('Unknown file: %s' % display_path(filename)) + if zipped: + logger.notify('Zipped packages:') + logger.indent += 2 + try: + for filename in zipped: + logger.notify(filename) + finally: + logger.indent -= 2 + else: + logger.notify('No zipped packages.') + if unzipped: + if options.sort_files: + unzipped.sort(key=lambda x: -x[1]) + logger.notify('Unzipped packages:') + logger.indent += 2 + try: + for filename, count in unzipped: + logger.notify('%s (%i files)' % (filename, count)) + finally: + logger.indent -= 2 + else: + logger.notify('No unzipped packages.') + finally: + logger.indent -= 2 + + def count_package(self, path): + total = 0 + for dirpath, dirnames, filenames in os.walk(path): + filenames = [f for f in filenames + if not f.lower().endswith('.pyc')] + total += len(filenames) + return total + + +ZipCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/download.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/download.py new file mode 100644 index 000000000..f1b63936b --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/download.py @@ -0,0 +1,470 @@ +import xmlrpclib +import re +import getpass +import urllib +import urllib2 +import urlparse +import os +import mimetypes +import shutil +import tempfile +from pip.backwardcompat import md5, copytree +from pip.exceptions import InstallationError +from pip.util import (splitext, + format_size, display_path, backup_dir, ask, + unpack_file, create_download_cache_folder, cache_download) +from pip.vcs import vcs +from pip.log import logger + + +__all__ = ['xmlrpclib_transport', 'get_file_content', 'urlopen', + 'is_url', 'url_to_path', 'path_to_url', 'path_to_url2', + 'geturl', 'is_archive_file', 'unpack_vcs_link', + 'unpack_file_url', 'is_vcs_url', 'is_file_url', 'unpack_http_url'] + + +xmlrpclib_transport = xmlrpclib.Transport() + + +def get_file_content(url, comes_from=None): + """Gets the content of a file; it may be a filename, file: URL, or + http: URL. Returns (location, content)""" + match = _scheme_re.search(url) + if match: + scheme = match.group(1).lower() + if (scheme == 'file' and comes_from + and comes_from.startswith('http')): + raise InstallationError( + 'Requirements file %s references URL %s, which is local' + % (comes_from, url)) + if scheme == 'file': + path = url.split(':', 1)[1] + path = path.replace('\\', '/') + match = _url_slash_drive_re.match(path) + if match: + path = match.group(1) + ':' + path.split('|', 1)[1] + path = urllib.unquote(path) + if path.startswith('/'): + path = '/' + path.lstrip('/') + url = path + else: + ## FIXME: catch some errors + resp = urlopen(url) + return geturl(resp), resp.read() + try: + f = open(url) + content = f.read() + except IOError, e: + raise InstallationError('Could not open requirements file: %s' % str(e)) + else: + f.close() + return url, content + + +_scheme_re = re.compile(r'^(http|https|file):', re.I) +_url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I) + +class URLOpener(object): + """ + pip's own URL helper that adds HTTP auth and proxy support + """ + def __init__(self): + self.passman = urllib2.HTTPPasswordMgrWithDefaultRealm() + + def __call__(self, url): + """ + If the given url contains auth info or if a normal request gets a 401 + response, an attempt is made to fetch the resource using basic HTTP + auth. + + """ + url, username, password = self.extract_credentials(url) + if username is None: + try: + response = urllib2.urlopen(self.get_request(url)) + except urllib2.HTTPError, e: + if e.code != 401: + raise + response = self.get_response(url) + else: + response = self.get_response(url, username, password) + return response + + def get_request(self, url): + """ + Wraps the URL to retrieve to protects against "creative" + interpretation of the RFC: http://bugs.python.org/issue8732 + """ + if isinstance(url, basestring): + url = urllib2.Request(url, headers={'Accept-encoding': 'identity'}) + return url + + def get_response(self, url, username=None, password=None): + """ + does the dirty work of actually getting the rsponse object using urllib2 + and its HTTP auth builtins. + """ + scheme, netloc, path, query, frag = urlparse.urlsplit(url) + pass_url = urlparse.urlunsplit(('_none_', netloc, path, query, frag)).replace('_none_://', '', 1) + req = self.get_request(url) + + stored_username, stored_password = self.passman.find_user_password(None, netloc) + # see if we have a password stored + if stored_username is None: + if username is None and self.prompting: + username = urllib.quote(raw_input('User for %s: ' % netloc)) + password = urllib.quote(getpass.getpass('Password: ')) + if username and password: + self.passman.add_password(None, netloc, username, password) + stored_username, stored_password = self.passman.find_user_password(None, netloc) + authhandler = urllib2.HTTPBasicAuthHandler(self.passman) + opener = urllib2.build_opener(authhandler) + # FIXME: should catch a 401 and offer to let the user reenter credentials + return opener.open(req) + + def setup(self, proxystr='', prompting=True): + """ + Sets the proxy handler given the option passed on the command + line. If an empty string is passed it looks at the HTTP_PROXY + environment variable. + """ + self.prompting = prompting + proxy = self.get_proxy(proxystr) + if proxy: + proxy_support = urllib2.ProxyHandler({"http": proxy, "ftp": proxy}) + opener = urllib2.build_opener(proxy_support, urllib2.CacheFTPHandler) + urllib2.install_opener(opener) + + def parse_credentials(self, netloc): + if "@" in netloc: + userinfo = netloc.rsplit("@", 1)[0] + if ":" in userinfo: + return userinfo.split(":", 1) + return userinfo, None + return None, None + + def extract_credentials(self, url): + """ + Extracts user/password from a url. + + Returns a tuple: + (url-without-auth, username, password) + """ + if isinstance(url, urllib2.Request): + result = urlparse.urlsplit(url.get_full_url()) + else: + result = urlparse.urlsplit(url) + scheme, netloc, path, query, frag = result + + username, password = self.parse_credentials(netloc) + if username is None: + return url, None, None + elif password is None and self.prompting: + # remove the auth credentials from the url part + netloc = netloc.replace('%s@' % username, '', 1) + # prompt for the password + prompt = 'Password for %s@%s: ' % (username, netloc) + password = urllib.quote(getpass.getpass(prompt)) + else: + # remove the auth credentials from the url part + netloc = netloc.replace('%s:%s@' % (username, password), '', 1) + + target_url = urlparse.urlunsplit((scheme, netloc, path, query, frag)) + return target_url, username, password + + def get_proxy(self, proxystr=''): + """ + Get the proxy given the option passed on the command line. + If an empty string is passed it looks at the HTTP_PROXY + environment variable. + """ + if not proxystr: + proxystr = os.environ.get('HTTP_PROXY', '') + if proxystr: + if '@' in proxystr: + user_password, server_port = proxystr.split('@', 1) + if ':' in user_password: + user, password = user_password.split(':', 1) + else: + user = user_password + prompt = 'Password for %s@%s: ' % (user, server_port) + password = urllib.quote(getpass.getpass(prompt)) + return '%s:%s@%s' % (user, password, server_port) + else: + return proxystr + else: + return None + +urlopen = URLOpener() + + +def is_url(name): + """Returns true if the name looks like a URL""" + if ':' not in name: + return False + scheme = name.split(':', 1)[0].lower() + return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes + + +def url_to_path(url): + """ + Convert a file: URL to a path. + """ + assert url.startswith('file:'), ( + "You can only turn file: urls into filenames (not %r)" % url) + path = url[len('file:'):].lstrip('/') + path = urllib.unquote(path) + if _url_drive_re.match(path): + path = path[0] + ':' + path[2:] + else: + path = '/' + path + return path + + +_drive_re = re.compile('^([a-z]):', re.I) +_url_drive_re = re.compile('^([a-z])[:|]', re.I) + + +def path_to_url(path): + """ + Convert a path to a file: URL. The path will be made absolute. + """ + path = os.path.normcase(os.path.abspath(path)) + if _drive_re.match(path): + path = path[0] + '|' + path[2:] + url = urllib.quote(path) + url = url.replace(os.path.sep, '/') + url = url.lstrip('/') + return 'file:///' + url + + +def path_to_url2(path): + """ + Convert a path to a file: URL. The path will be made absolute and have + quoted path parts. + """ + path = os.path.normpath(os.path.abspath(path)) + drive, path = os.path.splitdrive(path) + filepath = path.split(os.path.sep) + url = '/'.join([urllib.quote(part) for part in filepath]) + if not drive: + url = url.lstrip('/') + return 'file:///' + drive + url + + +def geturl(urllib2_resp): + """ + Use instead of urllib.addinfourl.geturl(), which appears to have + some issues with dropping the double slash for certain schemes + (e.g. file://). This implementation is probably over-eager, as it + always restores '://' if it is missing, and it appears some url + schemata aren't always followed by '//' after the colon, but as + far as I know pip doesn't need any of those. + The URI RFC can be found at: http://tools.ietf.org/html/rfc1630 + + This function assumes that + scheme:/foo/bar + is the same as + scheme:///foo/bar + """ + url = urllib2_resp.geturl() + scheme, rest = url.split(':', 1) + if rest.startswith('//'): + return url + else: + # FIXME: write a good test to cover it + return '%s://%s' % (scheme, rest) + + +def is_archive_file(name): + """Return True if `name` is a considered as an archive file.""" + archives = ('.zip', '.tar.gz', '.tar.bz2', '.tgz', '.tar', '.pybundle') + ext = splitext(name)[1].lower() + if ext in archives: + return True + return False + + +def unpack_vcs_link(link, location, only_download=False): + vcs_backend = _get_used_vcs_backend(link) + if only_download: + vcs_backend.export(location) + else: + vcs_backend.unpack(location) + + +def unpack_file_url(link, location): + source = url_to_path(link.url) + content_type = mimetypes.guess_type(source)[0] + if os.path.isdir(source): + # delete the location since shutil will create it again :( + if os.path.isdir(location): + shutil.rmtree(location) + copytree(source, location) + else: + unpack_file(source, location, content_type, link) + + +def _get_used_vcs_backend(link): + for backend in vcs.backends: + if link.scheme in backend.schemes: + vcs_backend = backend(link.url) + return vcs_backend + + +def is_vcs_url(link): + return bool(_get_used_vcs_backend(link)) + + +def is_file_url(link): + return link.url.lower().startswith('file:') + + +def _check_md5(download_hash, link): + download_hash = download_hash.hexdigest() + if download_hash != link.md5_hash: + logger.fatal("MD5 hash of the package %s (%s) doesn't match the expected hash %s!" + % (link, download_hash, link.md5_hash)) + raise InstallationError('Bad MD5 hash for package %s' % link) + + +def _get_md5_from_file(target_file, link): + download_hash = md5() + fp = open(target_file, 'rb') + while 1: + chunk = fp.read(4096) + if not chunk: + break + download_hash.update(chunk) + fp.close() + return download_hash + + +def _download_url(resp, link, temp_location): + fp = open(temp_location, 'wb') + download_hash = None + if link.md5_hash: + download_hash = md5() + try: + total_length = int(resp.info()['content-length']) + except (ValueError, KeyError): + total_length = 0 + downloaded = 0 + show_progress = total_length > 40*1000 or not total_length + show_url = link.show_url + try: + if show_progress: + ## FIXME: the URL can get really long in this message: + if total_length: + logger.start_progress('Downloading %s (%s): ' % (show_url, format_size(total_length))) + else: + logger.start_progress('Downloading %s (unknown size): ' % show_url) + else: + logger.notify('Downloading %s' % show_url) + logger.debug('Downloading from URL %s' % link) + + while 1: + chunk = resp.read(4096) + if not chunk: + break + downloaded += len(chunk) + if show_progress: + if not total_length: + logger.show_progress('%s' % format_size(downloaded)) + else: + logger.show_progress('%3i%% %s' % (100*downloaded/total_length, format_size(downloaded))) + if link.md5_hash: + download_hash.update(chunk) + fp.write(chunk) + fp.close() + finally: + if show_progress: + logger.end_progress('%s downloaded' % format_size(downloaded)) + return download_hash + + +def _copy_file(filename, location, content_type, link): + copy = True + download_location = os.path.join(location, link.filename) + if os.path.exists(download_location): + response = ask('The file %s exists. (i)gnore, (w)ipe, (b)ackup ' + % display_path(download_location), ('i', 'w', 'b')) + if response == 'i': + copy = False + elif response == 'w': + logger.warn('Deleting %s' % display_path(download_location)) + os.remove(download_location) + elif response == 'b': + dest_file = backup_dir(download_location) + logger.warn('Backing up %s to %s' + % (display_path(download_location), display_path(dest_file))) + shutil.move(download_location, dest_file) + if copy: + shutil.copy(filename, download_location) + logger.indent -= 2 + logger.notify('Saved %s' % display_path(download_location)) + + +def unpack_http_url(link, location, download_cache, only_download): + temp_dir = tempfile.mkdtemp('-unpack', 'pip-') + target_url = link.url.split('#', 1)[0] + target_file = None + download_hash = None + if download_cache: + target_file = os.path.join(download_cache, + urllib.quote(target_url, '')) + if not os.path.isdir(download_cache): + create_download_cache_folder(download_cache) + if (target_file + and os.path.exists(target_file) + and os.path.exists(target_file+'.content-type')): + fp = open(target_file+'.content-type') + content_type = fp.read().strip() + fp.close() + if link.md5_hash: + download_hash = _get_md5_from_file(target_file, link) + temp_location = target_file + logger.notify('Using download cache from %s' % target_file) + else: + resp = _get_response_from_url(target_url, link) + content_type = resp.info()['content-type'] + filename = link.filename + ext = splitext(filename)[1] + if not ext: + ext = mimetypes.guess_extension(content_type) + if ext: + filename += ext + if not ext and link.url != geturl(resp): + ext = os.path.splitext(geturl(resp))[1] + if ext: + filename += ext + temp_location = os.path.join(temp_dir, filename) + download_hash = _download_url(resp, link, temp_location) + if link.md5_hash: + _check_md5(download_hash, link) + if only_download: + _copy_file(temp_location, location, content_type, link) + else: + unpack_file(temp_location, location, content_type, link) + if target_file and target_file != temp_location: + cache_download(target_file, temp_location, content_type) + if target_file is None: + os.unlink(temp_location) + os.rmdir(temp_dir) + + +def _get_response_from_url(target_url, link): + try: + resp = urlopen(target_url) + except urllib2.HTTPError, e: + logger.fatal("HTTP error %s while getting %s" % (e.code, link)) + raise + except IOError, e: + # Typically an FTP error + logger.fatal("Error %s while getting %s" % (e, link)) + raise + return resp + +class Urllib2HeadRequest(urllib2.Request): + def get_method(self): + return "HEAD" diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/exceptions.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/exceptions.py new file mode 100644 index 000000000..1ad1a616d --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/exceptions.py @@ -0,0 +1,17 @@ +"""Exceptions used throughout package""" + + +class InstallationError(Exception): + """General exception during installation""" + + +class UninstallationError(Exception): + """General exception during uninstallation""" + + +class DistributionNotFound(InstallationError): + """Raised when a distribution cannot be found to satisfy a requirement""" + + +class BadCommand(Exception): + """Raised when virtualenv or a command is not found""" diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/index.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/index.py new file mode 100644 index 000000000..e42d8c868 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/index.py @@ -0,0 +1,686 @@ +"""Routines related to PyPI, indexes""" + +import sys +import os +import re +import mimetypes +import threading +import posixpath +import pkg_resources +import urllib +import urllib2 +import urlparse +import httplib +import random +import socket +import string +from Queue import Queue +from Queue import Empty as QueueEmpty +from pip.log import logger +from pip.util import Inf +from pip.util import normalize_name, splitext +from pip.exceptions import DistributionNotFound +from pip.backwardcompat import WindowsError, product +from pip.download import urlopen, path_to_url2, url_to_path, geturl, Urllib2HeadRequest + +__all__ = ['PackageFinder'] + + +DEFAULT_MIRROR_URL = "last.pypi.python.org" + + +class PackageFinder(object): + """This finds packages. + + This is meant to match easy_install's technique for looking for + packages, by reading pages and looking for appropriate links + """ + + def __init__(self, find_links, index_urls, + use_mirrors=False, mirrors=None, main_mirror_url=None): + self.find_links = find_links + self.index_urls = index_urls + self.dependency_links = [] + self.cache = PageCache() + # These are boring links that have already been logged somehow: + self.logged_links = set() + if use_mirrors: + self.mirror_urls = self._get_mirror_urls(mirrors, main_mirror_url) + logger.info('Using PyPI mirrors: %s' % ', '.join(self.mirror_urls)) + else: + self.mirror_urls = [] + + def add_dependency_links(self, links): + ## FIXME: this shouldn't be global list this, it should only + ## apply to requirements of the package that specifies the + ## dependency_links value + ## FIXME: also, we should track comes_from (i.e., use Link) + self.dependency_links.extend(links) + + @staticmethod + def _sort_locations(locations): + """ + Sort locations into "files" (archives) and "urls", and return + a pair of lists (files,urls) + """ + files = [] + urls = [] + + # puts the url for the given file path into the appropriate + # list + def sort_path(path): + url = path_to_url2(path) + if mimetypes.guess_type(url, strict=False)[0] == 'text/html': + urls.append(url) + else: + files.append(url) + + for url in locations: + if url.startswith('file:'): + path = url_to_path(url) + if os.path.isdir(path): + path = os.path.realpath(path) + for item in os.listdir(path): + sort_path(os.path.join(path, item)) + elif os.path.isfile(path): + sort_path(path) + else: + urls.append(url) + return files, urls + + def find_requirement(self, req, upgrade): + url_name = req.url_name + # Only check main index if index URL is given: + main_index_url = None + if self.index_urls: + # Check that we have the url_name correctly spelled: + main_index_url = Link(posixpath.join(self.index_urls[0], url_name)) + # This will also cache the page, so it's okay that we get it again later: + page = self._get_page(main_index_url, req) + if page is None: + url_name = self._find_url_name(Link(self.index_urls[0]), url_name, req) or req.url_name + + # Combine index URLs with mirror URLs here to allow + # adding more index URLs from requirements files + all_index_urls = self.index_urls + self.mirror_urls + + def mkurl_pypi_url(url): + loc = posixpath.join(url, url_name) + # For maximum compatibility with easy_install, ensure the path + # ends in a trailing slash. Although this isn't in the spec + # (and PyPI can handle it without the slash) some other index + # implementations might break if they relied on easy_install's behavior. + if not loc.endswith('/'): + loc = loc + '/' + return loc + if url_name is not None: + locations = [ + mkurl_pypi_url(url) + for url in all_index_urls] + self.find_links + else: + locations = list(self.find_links) + locations.extend(self.dependency_links) + for version in req.absolute_versions: + if url_name is not None and main_index_url is not None: + locations = [ + posixpath.join(main_index_url.url, version)] + locations + + file_locations, url_locations = self._sort_locations(locations) + + locations = [Link(url) for url in url_locations] + logger.debug('URLs to search for versions for %s:' % req) + for location in locations: + logger.debug('* %s' % location) + found_versions = [] + found_versions.extend( + self._package_versions( + [Link(url, '-f') for url in self.find_links], req.name.lower())) + page_versions = [] + for page in self._get_pages(locations, req): + logger.debug('Analyzing links from page %s' % page.url) + logger.indent += 2 + try: + page_versions.extend(self._package_versions(page.links, req.name.lower())) + finally: + logger.indent -= 2 + dependency_versions = list(self._package_versions( + [Link(url) for url in self.dependency_links], req.name.lower())) + if dependency_versions: + logger.info('dependency_links found: %s' % ', '.join([link.url for parsed, link, version in dependency_versions])) + file_versions = list(self._package_versions( + [Link(url) for url in file_locations], req.name.lower())) + if not found_versions and not page_versions and not dependency_versions and not file_versions: + logger.fatal('Could not find any downloads that satisfy the requirement %s' % req) + raise DistributionNotFound('No distributions at all found for %s' % req) + if req.satisfied_by is not None: + found_versions.append((req.satisfied_by.parsed_version, Inf, req.satisfied_by.version)) + if file_versions: + file_versions.sort(reverse=True) + logger.info('Local files found: %s' % ', '.join([url_to_path(link.url) for parsed, link, version in file_versions])) + found_versions = file_versions + found_versions + all_versions = found_versions + page_versions + dependency_versions + applicable_versions = [] + for (parsed_version, link, version) in all_versions: + if version not in req.req: + logger.info("Ignoring link %s, version %s doesn't match %s" + % (link, version, ','.join([''.join(s) for s in req.req.specs]))) + continue + applicable_versions.append((link, version)) + applicable_versions = sorted(applicable_versions, key=lambda v: pkg_resources.parse_version(v[1]), reverse=True) + existing_applicable = bool([link for link, version in applicable_versions if link is Inf]) + if not upgrade and existing_applicable: + if applicable_versions[0][1] is Inf: + logger.info('Existing installed version (%s) is most up-to-date and satisfies requirement' + % req.satisfied_by.version) + else: + logger.info('Existing installed version (%s) satisfies requirement (most up-to-date version is %s)' + % (req.satisfied_by.version, applicable_versions[0][1])) + return None + if not applicable_versions: + logger.fatal('Could not find a version that satisfies the requirement %s (from versions: %s)' + % (req, ', '.join([version for parsed_version, link, version in found_versions]))) + raise DistributionNotFound('No distributions matching the version for %s' % req) + if applicable_versions[0][0] is Inf: + # We have an existing version, and its the best version + logger.info('Installed version (%s) is most up-to-date (past versions: %s)' + % (req.satisfied_by.version, ', '.join([version for link, version in applicable_versions[1:]]) or 'none')) + return None + if len(applicable_versions) > 1: + logger.info('Using version %s (newest of versions: %s)' % + (applicable_versions[0][1], ', '.join([version for link, version in applicable_versions]))) + return applicable_versions[0][0] + + def _find_url_name(self, index_url, url_name, req): + """Finds the true URL name of a package, when the given name isn't quite correct. + This is usually used to implement case-insensitivity.""" + if not index_url.url.endswith('/'): + # Vaguely part of the PyPI API... weird but true. + ## FIXME: bad to modify this? + index_url.url += '/' + page = self._get_page(index_url, req) + if page is None: + logger.fatal('Cannot fetch index base URL %s' % index_url) + return + norm_name = normalize_name(req.url_name) + for link in page.links: + base = posixpath.basename(link.path.rstrip('/')) + if norm_name == normalize_name(base): + logger.notify('Real name of requirement %s is %s' % (url_name, base)) + return base + return None + + def _get_pages(self, locations, req): + """Yields (page, page_url) from the given locations, skipping + locations that have errors, and adding download/homepage links""" + pending_queue = Queue() + for location in locations: + pending_queue.put(location) + done = [] + seen = set() + threads = [] + for i in range(min(10, len(locations))): + t = threading.Thread(target=self._get_queued_page, args=(req, pending_queue, done, seen)) + t.setDaemon(True) + threads.append(t) + t.start() + for t in threads: + t.join() + return done + + _log_lock = threading.Lock() + + def _get_queued_page(self, req, pending_queue, done, seen): + while 1: + try: + location = pending_queue.get(False) + except QueueEmpty: + return + if location in seen: + continue + seen.add(location) + page = self._get_page(location, req) + if page is None: + continue + done.append(page) + for link in page.rel_links(): + pending_queue.put(link) + + _egg_fragment_re = re.compile(r'#egg=([^&]*)') + _egg_info_re = re.compile(r'([a-z0-9_.]+)-([a-z0-9_.-]+)', re.I) + _py_version_re = re.compile(r'-py([123]\.[0-9])$') + + def _sort_links(self, links): + "Returns elements of links in order, non-egg links first, egg links second, while eliminating duplicates" + eggs, no_eggs = [], [] + seen = set() + for link in links: + if link not in seen: + seen.add(link) + if link.egg_fragment: + eggs.append(link) + else: + no_eggs.append(link) + return no_eggs + eggs + + def _package_versions(self, links, search_name): + for link in self._sort_links(links): + for v in self._link_package_versions(link, search_name): + yield v + + def _link_package_versions(self, link, search_name): + """ + Return an iterable of triples (pkg_resources_version_key, + link, python_version) that can be extracted from the given + link. + + Meant to be overridden by subclasses, not called by clients. + """ + if link.egg_fragment: + egg_info = link.egg_fragment + else: + egg_info, ext = link.splitext() + if not ext: + if link not in self.logged_links: + logger.debug('Skipping link %s; not a file' % link) + self.logged_links.add(link) + return [] + if egg_info.endswith('.tar'): + # Special double-extension case: + egg_info = egg_info[:-4] + ext = '.tar' + ext + if ext not in ('.tar.gz', '.tar.bz2', '.tar', '.tgz', '.zip'): + if link not in self.logged_links: + logger.debug('Skipping link %s; unknown archive format: %s' % (link, ext)) + self.logged_links.add(link) + return [] + version = self._egg_info_matches(egg_info, search_name, link) + if version is None: + logger.debug('Skipping link %s; wrong project name (not %s)' % (link, search_name)) + return [] + match = self._py_version_re.search(version) + if match: + version = version[:match.start()] + py_version = match.group(1) + if py_version != sys.version[:3]: + logger.debug('Skipping %s because Python version is incorrect' % link) + return [] + logger.debug('Found link %s, version: %s' % (link, version)) + return [(pkg_resources.parse_version(version), + link, + version)] + + def _egg_info_matches(self, egg_info, search_name, link): + match = self._egg_info_re.search(egg_info) + if not match: + logger.debug('Could not parse version from link: %s' % link) + return None + name = match.group(0).lower() + # To match the "safe" name that pkg_resources creates: + name = name.replace('_', '-') + if name.startswith(search_name.lower()): + return match.group(0)[len(search_name):].lstrip('-') + else: + return None + + def _get_page(self, link, req): + return HTMLPage.get_page(link, req, cache=self.cache) + + def _get_mirror_urls(self, mirrors=None, main_mirror_url=None): + """Retrieves a list of URLs from the main mirror DNS entry + unless a list of mirror URLs are passed. + """ + if not mirrors: + mirrors = get_mirrors(main_mirror_url) + # Should this be made "less random"? E.g. netselect like? + random.shuffle(mirrors) + + mirror_urls = set() + for mirror_url in mirrors: + # Make sure we have a valid URL + if not ("http://" or "https://" or "file://") in mirror_url: + mirror_url = "http://%s" % mirror_url + if not mirror_url.endswith("/simple"): + mirror_url = "%s/simple/" % mirror_url + mirror_urls.add(mirror_url) + + return list(mirror_urls) + + +class PageCache(object): + """Cache of HTML pages""" + + failure_limit = 3 + + def __init__(self): + self._failures = {} + self._pages = {} + self._archives = {} + + def too_many_failures(self, url): + return self._failures.get(url, 0) >= self.failure_limit + + def get_page(self, url): + return self._pages.get(url) + + def is_archive(self, url): + return self._archives.get(url, False) + + def set_is_archive(self, url, value=True): + self._archives[url] = value + + def add_page_failure(self, url, level): + self._failures[url] = self._failures.get(url, 0)+level + + def add_page(self, urls, page): + for url in urls: + self._pages[url] = page + + +class HTMLPage(object): + """Represents one page, along with its URL""" + + ## FIXME: these regexes are horrible hacks: + _homepage_re = re.compile(r'\s*home\s*page', re.I) + _download_re = re.compile(r'\s*download\s+url', re.I) + ## These aren't so aweful: + _rel_re = re.compile("""<[^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*>""", re.I) + _href_re = re.compile('href=(?:"([^"]*)"|\'([^\']*)\'|([^>\\s\\n]*))', re.I|re.S) + _base_re = re.compile(r"""]+)""", re.I) + + def __init__(self, content, url, headers=None): + self.content = content + self.url = url + self.headers = headers + + def __str__(self): + return self.url + + @classmethod + def get_page(cls, link, req, cache=None, skip_archives=True): + url = link.url + url = url.split('#', 1)[0] + if cache.too_many_failures(url): + return None + + # Check for VCS schemes that do not support lookup as web pages. + from pip.vcs import VcsSupport + for scheme in VcsSupport.schemes: + if url.lower().startswith(scheme) and url[len(scheme)] in '+:': + logger.debug('Cannot look at %(scheme)s URL %(link)s' % locals()) + return None + + if cache is not None: + inst = cache.get_page(url) + if inst is not None: + return inst + try: + if skip_archives: + if cache is not None: + if cache.is_archive(url): + return None + filename = link.filename + for bad_ext in ['.tar', '.tar.gz', '.tar.bz2', '.tgz', '.zip']: + if filename.endswith(bad_ext): + content_type = cls._get_content_type(url) + if content_type.lower().startswith('text/html'): + break + else: + logger.debug('Skipping page %s because of Content-Type: %s' % (link, content_type)) + if cache is not None: + cache.set_is_archive(url) + return None + logger.debug('Getting page %s' % url) + + # Tack index.html onto file:// URLs that point to directories + (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url) + if scheme == 'file' and os.path.isdir(urllib.url2pathname(path)): + # add trailing slash if not present so urljoin doesn't trim final segment + if not url.endswith('/'): + url += '/' + url = urlparse.urljoin(url, 'index.html') + logger.debug(' file: URL is directory, getting %s' % url) + + resp = urlopen(url) + + real_url = geturl(resp) + headers = resp.info() + inst = cls(resp.read(), real_url, headers) + except (urllib2.HTTPError, urllib2.URLError, socket.timeout, socket.error, OSError, WindowsError), e: + desc = str(e) + if isinstance(e, socket.timeout): + log_meth = logger.info + level =1 + desc = 'timed out' + elif isinstance(e, urllib2.URLError): + log_meth = logger.info + if hasattr(e, 'reason') and isinstance(e.reason, socket.timeout): + desc = 'timed out' + level = 1 + else: + level = 2 + elif isinstance(e, urllib2.HTTPError) and e.code == 404: + ## FIXME: notify? + log_meth = logger.info + level = 2 + else: + log_meth = logger.info + level = 1 + log_meth('Could not fetch URL %s: %s' % (link, desc)) + log_meth('Will skip URL %s when looking for download links for %s' % (link.url, req)) + if cache is not None: + cache.add_page_failure(url, level) + return None + if cache is not None: + cache.add_page([url, real_url], inst) + return inst + + @staticmethod + def _get_content_type(url): + """Get the Content-Type of the given url, using a HEAD request""" + scheme, netloc, path, query, fragment = urlparse.urlsplit(url) + if not scheme in ('http', 'https', 'ftp', 'ftps'): + ## FIXME: some warning or something? + ## assertion error? + return '' + req = Urllib2HeadRequest(url, headers={'Host': netloc}) + resp = urlopen(req) + try: + if hasattr(resp, 'code') and resp.code != 200 and scheme not in ('ftp', 'ftps'): + ## FIXME: doesn't handle redirects + return '' + return resp.info().get('content-type', '') + finally: + resp.close() + + @property + def base_url(self): + if not hasattr(self, "_base_url"): + match = self._base_re.search(self.content) + if match: + self._base_url = match.group(1) + else: + self._base_url = self.url + return self._base_url + + @property + def links(self): + """Yields all links in the page""" + for match in self._href_re.finditer(self.content): + url = match.group(1) or match.group(2) or match.group(3) + url = self.clean_link(urlparse.urljoin(self.base_url, url)) + yield Link(url, self) + + def rel_links(self): + for url in self.explicit_rel_links(): + yield url + for url in self.scraped_rel_links(): + yield url + + def explicit_rel_links(self, rels=('homepage', 'download')): + """Yields all links with the given relations""" + for match in self._rel_re.finditer(self.content): + found_rels = match.group(1).lower().split() + for rel in rels: + if rel in found_rels: + break + else: + continue + match = self._href_re.search(match.group(0)) + if not match: + continue + url = match.group(1) or match.group(2) or match.group(3) + url = self.clean_link(urlparse.urljoin(self.base_url, url)) + yield Link(url, self) + + def scraped_rel_links(self): + for regex in (self._homepage_re, self._download_re): + match = regex.search(self.content) + if not match: + continue + href_match = self._href_re.search(self.content, pos=match.end()) + if not href_match: + continue + url = match.group(1) or match.group(2) or match.group(3) + if not url: + continue + url = self.clean_link(urlparse.urljoin(self.base_url, url)) + yield Link(url, self) + + _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) + + def clean_link(self, url): + """Makes sure a link is fully encoded. That is, if a ' ' shows up in + the link, it will be rewritten to %20 (while not over-quoting + % or other characters).""" + return self._clean_re.sub( + lambda match: '%%%2x' % ord(match.group(0)), url) + + +class Link(object): + + def __init__(self, url, comes_from=None): + self.url = url + self.comes_from = comes_from + + def __str__(self): + if self.comes_from: + return '%s (from %s)' % (self.url, self.comes_from) + else: + return self.url + + def __repr__(self): + return '' % self + + def __eq__(self, other): + return self.url == other.url + + def __hash__(self): + return hash(self.url) + + @property + def filename(self): + url = self.url + url = url.split('#', 1)[0] + url = url.split('?', 1)[0] + url = url.rstrip('/') + name = posixpath.basename(url) + assert name, ( + 'URL %r produced no filename' % url) + return name + + @property + def scheme(self): + return urlparse.urlsplit(self.url)[0] + + @property + def path(self): + return urlparse.urlsplit(self.url)[2] + + def splitext(self): + return splitext(posixpath.basename(self.path.rstrip('/'))) + + _egg_fragment_re = re.compile(r'#egg=([^&]*)') + + @property + def egg_fragment(self): + match = self._egg_fragment_re.search(self.url) + if not match: + return None + return match.group(1) + + _md5_re = re.compile(r'md5=([a-f0-9]+)') + + @property + def md5_hash(self): + match = self._md5_re.search(self.url) + if match: + return match.group(1) + return None + + @property + def show_url(self): + return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0]) + + +def get_requirement_from_url(url): + """Get a requirement from the URL, if possible. This looks for #egg + in the URL""" + link = Link(url) + egg_info = link.egg_fragment + if not egg_info: + egg_info = splitext(link.filename)[0] + return package_to_requirement(egg_info) + + +def package_to_requirement(package_name): + """Translate a name like Foo-1.2 to Foo==1.3""" + match = re.search(r'^(.*?)(-dev|-\d.*)', package_name) + if match: + name = match.group(1) + version = match.group(2) + else: + name = package_name + version = '' + if version: + return '%s==%s' % (name, version) + else: + return name + + +def get_mirrors(hostname=None): + """Return the list of mirrors from the last record found on the DNS + entry:: + + >>> from pip.index import get_mirrors + >>> get_mirrors() + ['a.pypi.python.org', 'b.pypi.python.org', 'c.pypi.python.org', + 'd.pypi.python.org'] + + Originally written for the distutils2 project by Alexis Metaireau. + """ + if hostname is None: + hostname = DEFAULT_MIRROR_URL + + # return the last mirror registered on PyPI. + try: + hostname = socket.gethostbyname_ex(hostname)[0] + except socket.gaierror: + return [] + end_letter = hostname.split(".", 1) + + # determine the list from the last one. + return ["%s.%s" % (s, end_letter[1]) for s in string_range(end_letter[0])] + + +def string_range(last): + """Compute the range of string between "a" and last. + + This works for simple "a to z" lists, but also for "a to zz" lists. + """ + for k in range(len(last)): + for x in product(string.ascii_lowercase, repeat=k+1): + result = ''.join(x) + yield result + if result == last: + return + diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/locations.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/locations.py new file mode 100644 index 000000000..4254ef2fb --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/locations.py @@ -0,0 +1,45 @@ +"""Locations where we look for configs, install stuff, etc""" + +import sys +import os +from distutils import sysconfig + + +def running_under_virtualenv(): + """ + Return True if we're running inside a virtualenv, False otherwise. + + """ + return hasattr(sys, 'real_prefix') + + +if running_under_virtualenv(): + ## FIXME: is build/ a good name? + build_prefix = os.path.join(sys.prefix, 'build') + src_prefix = os.path.join(sys.prefix, 'src') +else: + ## FIXME: this isn't a very good default + build_prefix = os.path.join(os.getcwd(), 'build') + src_prefix = os.path.join(os.getcwd(), 'src') + +# FIXME doesn't account for venv linked to global site-packages + +site_packages = sysconfig.get_python_lib() +user_dir = os.path.expanduser('~') +if sys.platform == 'win32': + bin_py = os.path.join(sys.prefix, 'Scripts') + # buildout uses 'bin' on Windows too? + if not os.path.exists(bin_py): + bin_py = os.path.join(sys.prefix, 'bin') + user_dir = os.environ.get('APPDATA', user_dir) # Use %APPDATA% for roaming + default_storage_dir = os.path.join(user_dir, 'pip') + default_config_file = os.path.join(default_storage_dir, 'pip.ini') + default_log_file = os.path.join(default_storage_dir, 'pip.log') +else: + bin_py = os.path.join(sys.prefix, 'bin') + default_storage_dir = os.path.join(user_dir, '.pip') + default_config_file = os.path.join(default_storage_dir, 'pip.conf') + default_log_file = os.path.join(default_storage_dir, 'pip.log') + # Forcing to use /usr/local/bin for standard Mac OS X framework installs + if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': + bin_py = '/usr/local/bin' diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/log.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/log.py new file mode 100644 index 000000000..0218ab1ae --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/log.py @@ -0,0 +1,181 @@ +"""Logging +""" + +import sys +import logging + + +class Logger(object): + + """ + Logging object for use in command-line script. Allows ranges of + levels, to avoid some redundancy of displayed information. + """ + + VERBOSE_DEBUG = logging.DEBUG-1 + DEBUG = logging.DEBUG + INFO = logging.INFO + NOTIFY = (logging.INFO+logging.WARN)/2 + WARN = WARNING = logging.WARN + ERROR = logging.ERROR + FATAL = logging.FATAL + + LEVELS = [VERBOSE_DEBUG, DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL] + + def __init__(self): + self.consumers = [] + self.indent = 0 + self.explicit_levels = False + self.in_progress = None + self.in_progress_hanging = False + + def debug(self, msg, *args, **kw): + self.log(self.DEBUG, msg, *args, **kw) + + def info(self, msg, *args, **kw): + self.log(self.INFO, msg, *args, **kw) + + def notify(self, msg, *args, **kw): + self.log(self.NOTIFY, msg, *args, **kw) + + def warn(self, msg, *args, **kw): + self.log(self.WARN, msg, *args, **kw) + + def error(self, msg, *args, **kw): + self.log(self.WARN, msg, *args, **kw) + + def fatal(self, msg, *args, **kw): + self.log(self.FATAL, msg, *args, **kw) + + def log(self, level, msg, *args, **kw): + if args: + if kw: + raise TypeError( + "You may give positional or keyword arguments, not both") + args = args or kw + rendered = None + for consumer_level, consumer in self.consumers: + if self.level_matches(level, consumer_level): + if (self.in_progress_hanging + and consumer in (sys.stdout, sys.stderr)): + self.in_progress_hanging = False + sys.stdout.write('\n') + sys.stdout.flush() + if rendered is None: + if args: + rendered = msg % args + else: + rendered = msg + rendered = ' '*self.indent + rendered + if self.explicit_levels: + ## FIXME: should this be a name, not a level number? + rendered = '%02i %s' % (level, rendered) + if hasattr(consumer, 'write'): + consumer.write(rendered+'\n') + else: + consumer(rendered) + + def start_progress(self, msg): + assert not self.in_progress, ( + "Tried to start_progress(%r) while in_progress %r" + % (msg, self.in_progress)) + if self.level_matches(self.NOTIFY, self._stdout_level()): + sys.stdout.write(' '*self.indent + msg) + sys.stdout.flush() + self.in_progress_hanging = True + else: + self.in_progress_hanging = False + self.in_progress = msg + self.last_message = None + + def end_progress(self, msg='done.'): + assert self.in_progress, ( + "Tried to end_progress without start_progress") + if self.stdout_level_matches(self.NOTIFY): + if not self.in_progress_hanging: + # Some message has been printed out since start_progress + sys.stdout.write('...' + self.in_progress + msg + '\n') + sys.stdout.flush() + else: + # These erase any messages shown with show_progress (besides .'s) + logger.show_progress('') + logger.show_progress('') + sys.stdout.write(msg + '\n') + sys.stdout.flush() + self.in_progress = None + self.in_progress_hanging = False + + def show_progress(self, message=None): + """If we are in a progress scope, and no log messages have been + shown, write out another '.'""" + if self.in_progress_hanging: + if message is None: + sys.stdout.write('.') + sys.stdout.flush() + else: + if self.last_message: + padding = ' ' * max(0, len(self.last_message)-len(message)) + else: + padding = '' + sys.stdout.write('\r%s%s%s%s' % (' '*self.indent, self.in_progress, message, padding)) + sys.stdout.flush() + self.last_message = message + + def stdout_level_matches(self, level): + """Returns true if a message at this level will go to stdout""" + return self.level_matches(level, self._stdout_level()) + + def _stdout_level(self): + """Returns the level that stdout runs at""" + for level, consumer in self.consumers: + if consumer is sys.stdout: + return level + return self.FATAL + + def level_matches(self, level, consumer_level): + """ + >>> l = Logger() + >>> l.level_matches(3, 4) + False + >>> l.level_matches(3, 2) + True + >>> l.level_matches(slice(None, 3), 3) + False + >>> l.level_matches(slice(None, 3), 2) + True + >>> l.level_matches(slice(1, 3), 1) + True + >>> l.level_matches(slice(2, 3), 1) + False + """ + if isinstance(level, slice): + start, stop = level.start, level.stop + if start is not None and start > consumer_level: + return False + if stop is not None or stop <= consumer_level: + return False + return True + else: + return level >= consumer_level + + @classmethod + def level_for_integer(cls, level): + levels = cls.LEVELS + if level < 0: + return levels[0] + if level >= len(levels): + return levels[-1] + return levels[level] + + def move_stdout_to_stderr(self): + to_remove = [] + to_add = [] + for consumer_level, consumer in self.consumers: + if consumer == sys.stdout: + to_remove.append((consumer_level, consumer)) + to_add.append((consumer_level, sys.stderr)) + for item in to_remove: + self.consumers.remove(item) + self.consumers.extend(to_add) + +logger = Logger() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/req.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/req.py new file mode 100644 index 000000000..444e7252b --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/req.py @@ -0,0 +1,1432 @@ +import sys +import os +import shutil +import re +import zipfile +import pkg_resources +import tempfile +import urlparse +import urllib2 +import urllib +import ConfigParser +from distutils.sysconfig import get_python_version +from email.FeedParser import FeedParser +from pip.locations import bin_py, running_under_virtualenv +from pip.exceptions import InstallationError, UninstallationError +from pip.vcs import vcs +from pip.log import logger +from pip.util import display_path, rmtree +from pip.util import ask, backup_dir +from pip.util import is_installable_dir, is_local, dist_is_local +from pip.util import renames, normalize_path, egg_link_path +from pip.util import make_path_relative +from pip import call_subprocess +from pip.backwardcompat import any, copytree +from pip.index import Link +from pip.locations import build_prefix +from pip.download import (get_file_content, is_url, url_to_path, + path_to_url, is_archive_file, + unpack_vcs_link, is_vcs_url, is_file_url, + unpack_file_url, unpack_http_url) + + +PIP_DELETE_MARKER_FILENAME = 'pip-delete-this-directory.txt' + + +class InstallRequirement(object): + + def __init__(self, req, comes_from, source_dir=None, editable=False, + url=None, update=True): + if isinstance(req, basestring): + req = pkg_resources.Requirement.parse(req) + self.req = req + self.comes_from = comes_from + self.source_dir = source_dir + self.editable = editable + self.url = url + self._egg_info_path = None + # This holds the pkg_resources.Distribution object if this requirement + # is already available: + self.satisfied_by = None + # This hold the pkg_resources.Distribution object if this requirement + # conflicts with another installed distribution: + self.conflicts_with = None + self._temp_build_dir = None + self._is_bundle = None + # True if the editable should be updated: + self.update = update + # Set to True after successful installation + self.install_succeeded = None + # UninstallPathSet of uninstalled distribution (for possible rollback) + self.uninstalled = None + + @classmethod + def from_editable(cls, editable_req, comes_from=None, default_vcs=None): + name, url = parse_editable(editable_req, default_vcs) + if url.startswith('file:'): + source_dir = url_to_path(url) + else: + source_dir = None + return cls(name, comes_from, source_dir=source_dir, editable=True, url=url) + + @classmethod + def from_line(cls, name, comes_from=None): + """Creates an InstallRequirement from a name, which might be a + requirement, directory containing 'setup.py', filename, or URL. + """ + url = None + name = name.strip() + req = name + path = os.path.normpath(os.path.abspath(name)) + + if is_url(name): + url = name + ## FIXME: I think getting the requirement here is a bad idea: + #req = get_requirement_from_url(url) + req = None + elif os.path.isdir(path) and (os.path.sep in name or name.startswith('.')): + if not is_installable_dir(path): + raise InstallationError("Directory %r is not installable. File 'setup.py' not found." + % name) + url = path_to_url(name) + #req = get_requirement_from_url(url) + req = None + elif is_archive_file(path): + if not os.path.isfile(path): + logger.warn('Requirement %r looks like a filename, but the file does not exist' + % name) + url = path_to_url(name) + #req = get_requirement_from_url(url) + req = None + return cls(req, comes_from, url=url) + + def __str__(self): + if self.req: + s = str(self.req) + if self.url: + s += ' from %s' % self.url + else: + s = self.url + if self.satisfied_by is not None: + s += ' in %s' % display_path(self.satisfied_by.location) + if self.comes_from: + if isinstance(self.comes_from, basestring): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += ' (from %s)' % comes_from + return s + + def from_path(self): + if self.req is None: + return None + s = str(self.req) + if self.comes_from: + if isinstance(self.comes_from, basestring): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += '->' + comes_from + return s + + def build_location(self, build_dir, unpack=True): + if self._temp_build_dir is not None: + return self._temp_build_dir + if self.req is None: + self._temp_build_dir = tempfile.mkdtemp('-build', 'pip-') + self._ideal_build_dir = build_dir + return self._temp_build_dir + if self.editable: + name = self.name.lower() + else: + name = self.name + # FIXME: Is there a better place to create the build_dir? (hg and bzr need this) + if not os.path.exists(build_dir): + _make_build_dir(build_dir) + return os.path.join(build_dir, name) + + def correct_build_location(self): + """If the build location was a temporary directory, this will move it + to a new more permanent location""" + if self.source_dir is not None: + return + assert self.req is not None + assert self._temp_build_dir + old_location = self._temp_build_dir + new_build_dir = self._ideal_build_dir + del self._ideal_build_dir + if self.editable: + name = self.name.lower() + else: + name = self.name + new_location = os.path.join(new_build_dir, name) + if not os.path.exists(new_build_dir): + logger.debug('Creating directory %s' % new_build_dir) + _make_build_dir(new_build_dir) + if os.path.exists(new_location): + raise InstallationError( + 'A package already exists in %s; please remove it to continue' + % display_path(new_location)) + logger.debug('Moving package %s from %s to new location %s' + % (self, display_path(old_location), display_path(new_location))) + shutil.move(old_location, new_location) + self._temp_build_dir = new_location + self.source_dir = new_location + self._egg_info_path = None + + @property + def name(self): + if self.req is None: + return None + return self.req.project_name + + @property + def url_name(self): + if self.req is None: + return None + return urllib.quote(self.req.unsafe_name) + + @property + def setup_py(self): + return os.path.join(self.source_dir, 'setup.py') + + def run_egg_info(self, force_root_egg_info=False): + assert self.source_dir + if self.name: + logger.notify('Running setup.py egg_info for package %s' % self.name) + else: + logger.notify('Running setup.py egg_info for package from %s' % self.url) + logger.indent += 2 + try: + script = self._run_setup_py + script = script.replace('__SETUP_PY__', repr(self.setup_py)) + script = script.replace('__PKG_NAME__', repr(self.name)) + # We can't put the .egg-info files at the root, because then the source code will be mistaken + # for an installed egg, causing problems + if self.editable or force_root_egg_info: + egg_base_option = [] + else: + egg_info_dir = os.path.join(self.source_dir, 'pip-egg-info') + if not os.path.exists(egg_info_dir): + os.makedirs(egg_info_dir) + egg_base_option = ['--egg-base', 'pip-egg-info'] + call_subprocess( + [sys.executable, '-c', script, 'egg_info'] + egg_base_option, + cwd=self.source_dir, filter_stdout=self._filter_install, show_stdout=False, + command_level=logger.VERBOSE_DEBUG, + command_desc='python setup.py egg_info') + finally: + logger.indent -= 2 + if not self.req: + self.req = pkg_resources.Requirement.parse(self.pkg_info()['Name']) + self.correct_build_location() + + ## FIXME: This is a lame hack, entirely for PasteScript which has + ## a self-provided entry point that causes this awkwardness + _run_setup_py = """ +__file__ = __SETUP_PY__ +from setuptools.command import egg_info +def replacement_run(self): + self.mkpath(self.egg_info) + installer = self.distribution.fetch_build_egg + for ep in egg_info.iter_entry_points('egg_info.writers'): + # require=False is the change we're making: + writer = ep.load(require=False) + if writer: + writer(self, ep.name, egg_info.os.path.join(self.egg_info,ep.name)) + self.find_sources() +egg_info.egg_info.run = replacement_run +execfile(__file__) +""" + + def egg_info_data(self, filename): + if self.satisfied_by is not None: + if not self.satisfied_by.has_metadata(filename): + return None + return self.satisfied_by.get_metadata(filename) + assert self.source_dir + filename = self.egg_info_path(filename) + if not os.path.exists(filename): + return None + fp = open(filename, 'r') + data = fp.read() + fp.close() + return data + + def egg_info_path(self, filename): + if self._egg_info_path is None: + if self.editable: + base = self.source_dir + else: + base = os.path.join(self.source_dir, 'pip-egg-info') + filenames = os.listdir(base) + if self.editable: + filenames = [] + for root, dirs, files in os.walk(base): + for dir in vcs.dirnames: + if dir in dirs: + dirs.remove(dir) + for dir in dirs: + # Don't search in anything that looks like a virtualenv environment + if (os.path.exists(os.path.join(root, dir, 'bin', 'python')) + or os.path.exists(os.path.join(root, dir, 'Scripts', 'Python.exe'))): + dirs.remove(dir) + # Also don't search through tests + if dir == 'test' or dir == 'tests': + dirs.remove(dir) + filenames.extend([os.path.join(root, dir) + for dir in dirs]) + filenames = [f for f in filenames if f.endswith('.egg-info')] + + if not filenames: + raise InstallationError('No files/directores in %s (from %s)' % (base, filename)) + assert filenames, "No files/directories in %s (from %s)" % (base, filename) + + # if we have more than one match, we pick the toplevel one. This can + # easily be the case if there is a dist folder which contains an + # extracted tarball for testing purposes. + if len(filenames) > 1: + filenames.sort(key=lambda x: x.count(os.path.sep) + + (os.path.altsep and + x.count(os.path.altsep) or 0)) + self._egg_info_path = os.path.join(base, filenames[0]) + return os.path.join(self._egg_info_path, filename) + + def egg_info_lines(self, filename): + data = self.egg_info_data(filename) + if not data: + return [] + result = [] + for line in data.splitlines(): + line = line.strip() + if not line or line.startswith('#'): + continue + result.append(line) + return result + + def pkg_info(self): + p = FeedParser() + data = self.egg_info_data('PKG-INFO') + if not data: + logger.warn('No PKG-INFO file found in %s' % display_path(self.egg_info_path('PKG-INFO'))) + p.feed(data or '') + return p.close() + + @property + def dependency_links(self): + return self.egg_info_lines('dependency_links.txt') + + _requirements_section_re = re.compile(r'\[(.*?)\]') + + def requirements(self, extras=()): + in_extra = None + for line in self.egg_info_lines('requires.txt'): + match = self._requirements_section_re.match(line) + if match: + in_extra = match.group(1) + continue + if in_extra and in_extra not in extras: + # Skip requirement for an extra we aren't requiring + continue + yield line + + @property + def absolute_versions(self): + for qualifier, version in self.req.specs: + if qualifier == '==': + yield version + + @property + def installed_version(self): + return self.pkg_info()['version'] + + def assert_source_matches_version(self): + assert self.source_dir + if self.comes_from is None: + # We don't check the versions of things explicitly installed. + # This makes, e.g., "pip Package==dev" possible + return + version = self.installed_version + if version not in self.req: + logger.fatal( + 'Source in %s has the version %s, which does not match the requirement %s' + % (display_path(self.source_dir), version, self)) + raise InstallationError( + 'Source in %s has version %s that conflicts with %s' + % (display_path(self.source_dir), version, self)) + else: + logger.debug('Source in %s has version %s, which satisfies requirement %s' + % (display_path(self.source_dir), version, self)) + + def update_editable(self, obtain=True): + if not self.url: + logger.info("Cannot update repository at %s; repository location is unknown" % self.source_dir) + return + assert self.editable + assert self.source_dir + if self.url.startswith('file:'): + # Static paths don't get updated + return + assert '+' in self.url, "bad url: %r" % self.url + if not self.update: + return + vc_type, url = self.url.split('+', 1) + backend = vcs.get_backend(vc_type) + if backend: + vcs_backend = backend(self.url) + if obtain: + vcs_backend.obtain(self.source_dir) + else: + vcs_backend.export(self.source_dir) + else: + assert 0, ( + 'Unexpected version control type (in %s): %s' + % (self.url, vc_type)) + + def uninstall(self, auto_confirm=False): + """ + Uninstall the distribution currently satisfying this requirement. + + Prompts before removing or modifying files unless + ``auto_confirm`` is True. + + Refuses to delete or modify files outside of ``sys.prefix`` - + thus uninstallation within a virtual environment can only + modify that virtual environment, even if the virtualenv is + linked to global site-packages. + + """ + if not self.check_if_exists(): + raise UninstallationError("Cannot uninstall requirement %s, not installed" % (self.name,)) + dist = self.satisfied_by or self.conflicts_with + + paths_to_remove = UninstallPathSet(dist) + + pip_egg_info_path = os.path.join(dist.location, + dist.egg_name()) + '.egg-info' + easy_install_egg = dist.egg_name() + '.egg' + develop_egg_link = egg_link_path(dist) + if os.path.exists(pip_egg_info_path): + # package installed by pip + paths_to_remove.add(pip_egg_info_path) + if dist.has_metadata('installed-files.txt'): + for installed_file in dist.get_metadata('installed-files.txt').splitlines(): + path = os.path.normpath(os.path.join(pip_egg_info_path, installed_file)) + paths_to_remove.add(path) + if dist.has_metadata('top_level.txt'): + if dist.has_metadata('namespace_packages.txt'): + namespaces = dist.get_metadata('namespace_packages.txt') + else: + namespaces = [] + for top_level_pkg in [p for p + in dist.get_metadata('top_level.txt').splitlines() + if p and p not in namespaces]: + path = os.path.join(dist.location, top_level_pkg) + paths_to_remove.add(path) + paths_to_remove.add(path + '.py') + paths_to_remove.add(path + '.pyc') + + elif dist.location.endswith(easy_install_egg): + # package installed by easy_install + paths_to_remove.add(dist.location) + easy_install_pth = os.path.join(os.path.dirname(dist.location), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) + + elif os.path.isfile(develop_egg_link): + # develop egg + fh = open(develop_egg_link, 'r') + link_pointer = os.path.normcase(fh.readline().strip()) + fh.close() + assert (link_pointer == dist.location), 'Egg-link %s does not match installed location of %s (at %s)' % (link_pointer, self.name, dist.location) + paths_to_remove.add(develop_egg_link) + easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, dist.location) + + # find distutils scripts= scripts + if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): + for script in dist.metadata_listdir('scripts'): + paths_to_remove.add(os.path.join(bin_py, script)) + if sys.platform == 'win32': + paths_to_remove.add(os.path.join(bin_py, script) + '.bat') + + # find console_scripts + if dist.has_metadata('entry_points.txt'): + config = ConfigParser.SafeConfigParser() + config.readfp(FakeFile(dist.get_metadata_lines('entry_points.txt'))) + if config.has_section('console_scripts'): + for name, value in config.items('console_scripts'): + paths_to_remove.add(os.path.join(bin_py, name)) + if sys.platform == 'win32': + paths_to_remove.add(os.path.join(bin_py, name) + '.exe') + paths_to_remove.add(os.path.join(bin_py, name) + '.exe.manifest') + paths_to_remove.add(os.path.join(bin_py, name) + '-script.py') + + paths_to_remove.remove(auto_confirm) + self.uninstalled = paths_to_remove + + def rollback_uninstall(self): + if self.uninstalled: + self.uninstalled.rollback() + else: + logger.error("Can't rollback %s, nothing uninstalled." + % (self.project_name,)) + + def commit_uninstall(self): + if self.uninstalled: + self.uninstalled.commit() + else: + logger.error("Can't commit %s, nothing uninstalled." + % (self.project_name,)) + + def archive(self, build_dir): + assert self.source_dir + create_archive = True + archive_name = '%s-%s.zip' % (self.name, self.installed_version) + archive_path = os.path.join(build_dir, archive_name) + if os.path.exists(archive_path): + response = ask('The file %s exists. (i)gnore, (w)ipe, (b)ackup ' + % display_path(archive_path), ('i', 'w', 'b')) + if response == 'i': + create_archive = False + elif response == 'w': + logger.warn('Deleting %s' % display_path(archive_path)) + os.remove(archive_path) + elif response == 'b': + dest_file = backup_dir(archive_path) + logger.warn('Backing up %s to %s' + % (display_path(archive_path), display_path(dest_file))) + shutil.move(archive_path, dest_file) + if create_archive: + zip = zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) + dir = os.path.normcase(os.path.abspath(self.source_dir)) + for dirpath, dirnames, filenames in os.walk(dir): + if 'pip-egg-info' in dirnames: + dirnames.remove('pip-egg-info') + for dirname in dirnames: + dirname = os.path.join(dirpath, dirname) + name = self._clean_zip_name(dirname, dir) + zipdir = zipfile.ZipInfo(self.name + '/' + name + '/') + zipdir.external_attr = 0755 << 16L + zip.writestr(zipdir, '') + for filename in filenames: + if filename == PIP_DELETE_MARKER_FILENAME: + continue + filename = os.path.join(dirpath, filename) + name = self._clean_zip_name(filename, dir) + zip.write(filename, self.name + '/' + name) + zip.close() + logger.indent -= 2 + logger.notify('Saved %s' % display_path(archive_path)) + + def _clean_zip_name(self, name, prefix): + assert name.startswith(prefix+os.path.sep), ( + "name %r doesn't start with prefix %r" % (name, prefix)) + name = name[len(prefix)+1:] + name = name.replace(os.path.sep, '/') + return name + + def install(self, install_options, global_options=()): + if self.editable: + self.install_editable(install_options, global_options) + return + temp_location = tempfile.mkdtemp('-record', 'pip-') + record_filename = os.path.join(temp_location, 'install-record.txt') + try: + + install_args = [ + sys.executable, '-c', + "import setuptools;__file__=%r;"\ + "execfile(__file__)" % self.setup_py] +\ + list(global_options) + [ + 'install', + '--single-version-externally-managed', + '--record', record_filename] + + if running_under_virtualenv(): + ## FIXME: I'm not sure if this is a reasonable location; probably not + ## but we can't put it in the default location, as that is a virtualenv symlink that isn't writable + install_args += ['--install-headers', + os.path.join(sys.prefix, 'include', 'site', + 'python' + get_python_version())] + logger.notify('Running setup.py install for %s' % self.name) + logger.indent += 2 + try: + call_subprocess(install_args + install_options, + cwd=self.source_dir, filter_stdout=self._filter_install, show_stdout=False) + finally: + logger.indent -= 2 + if not os.path.exists(record_filename): + logger.notify('Record file %s not found' % record_filename) + return + self.install_succeeded = True + f = open(record_filename) + for line in f: + line = line.strip() + if line.endswith('.egg-info'): + egg_info_dir = line + break + else: + logger.warn('Could not find .egg-info directory in install record for %s' % self) + ## FIXME: put the record somewhere + ## FIXME: should this be an error? + return + f.close() + new_lines = [] + f = open(record_filename) + for line in f: + filename = line.strip() + if os.path.isdir(filename): + filename += os.path.sep + new_lines.append(make_path_relative(filename, egg_info_dir)) + f.close() + f = open(os.path.join(egg_info_dir, 'installed-files.txt'), 'w') + f.write('\n'.join(new_lines)+'\n') + f.close() + finally: + if os.path.exists(record_filename): + os.remove(record_filename) + os.rmdir(temp_location) + + def remove_temporary_source(self): + """Remove the source files from this requirement, if they are marked + for deletion""" + if self.is_bundle or os.path.exists(self.delete_marker_filename): + logger.info('Removing source in %s' % self.source_dir) + if self.source_dir: + rmtree(self.source_dir) + self.source_dir = None + if self._temp_build_dir and os.path.exists(self._temp_build_dir): + rmtree(self._temp_build_dir) + self._temp_build_dir = None + + def install_editable(self, install_options, global_options=()): + logger.notify('Running setup.py develop for %s' % self.name) + logger.indent += 2 + try: + ## FIXME: should we do --install-headers here too? + call_subprocess( + [sys.executable, '-c', + "import setuptools; __file__=%r; execfile(%r)" % (self.setup_py, self.setup_py)] + + list(global_options) + ['develop', '--no-deps'] + list(install_options), + + cwd=self.source_dir, filter_stdout=self._filter_install, + show_stdout=False) + finally: + logger.indent -= 2 + self.install_succeeded = True + + def _filter_install(self, line): + level = logger.NOTIFY + for regex in [r'^running .*', r'^writing .*', '^creating .*', '^[Cc]opying .*', + r'^reading .*', r"^removing .*\.egg-info' \(and everything under it\)$", + r'^byte-compiling ', + # Not sure what this warning is, but it seems harmless: + r"^warning: manifest_maker: standard file '-c' not found$"]: + if re.search(regex, line.strip()): + level = logger.INFO + break + return (level, line) + + def check_if_exists(self): + """Find an installed distribution that satisfies or conflicts + with this requirement, and set self.satisfied_by or + self.conflicts_with appropriately.""" + if self.req is None: + return False + try: + self.satisfied_by = pkg_resources.get_distribution(self.req) + except pkg_resources.DistributionNotFound: + return False + except pkg_resources.VersionConflict: + self.conflicts_with = pkg_resources.get_distribution(self.req.project_name) + return True + + @property + def is_bundle(self): + if self._is_bundle is not None: + return self._is_bundle + base = self._temp_build_dir + if not base: + ## FIXME: this doesn't seem right: + return False + self._is_bundle = (os.path.exists(os.path.join(base, 'pip-manifest.txt')) + or os.path.exists(os.path.join(base, 'pyinstall-manifest.txt'))) + return self._is_bundle + + def bundle_requirements(self): + for dest_dir in self._bundle_editable_dirs: + package = os.path.basename(dest_dir) + ## FIXME: svnism: + for vcs_backend in vcs.backends: + url = rev = None + vcs_bundle_file = os.path.join( + dest_dir, vcs_backend.bundle_file) + if os.path.exists(vcs_bundle_file): + vc_type = vcs_backend.name + fp = open(vcs_bundle_file) + content = fp.read() + fp.close() + url, rev = vcs_backend().parse_vcs_bundle_file(content) + break + if url: + url = '%s+%s@%s' % (vc_type, url, rev) + else: + url = None + yield InstallRequirement( + package, self, editable=True, url=url, + update=False, source_dir=dest_dir) + for dest_dir in self._bundle_build_dirs: + package = os.path.basename(dest_dir) + yield InstallRequirement( + package, self, + source_dir=dest_dir) + + def move_bundle_files(self, dest_build_dir, dest_src_dir): + base = self._temp_build_dir + assert base + src_dir = os.path.join(base, 'src') + build_dir = os.path.join(base, 'build') + bundle_build_dirs = [] + bundle_editable_dirs = [] + for source_dir, dest_dir, dir_collection in [ + (src_dir, dest_src_dir, bundle_editable_dirs), + (build_dir, dest_build_dir, bundle_build_dirs)]: + if os.path.exists(source_dir): + for dirname in os.listdir(source_dir): + dest = os.path.join(dest_dir, dirname) + dir_collection.append(dest) + if os.path.exists(dest): + logger.warn('The directory %s (containing package %s) already exists; cannot move source from bundle %s' + % (dest, dirname, self)) + continue + if not os.path.exists(dest_dir): + logger.info('Creating directory %s' % dest_dir) + os.makedirs(dest_dir) + shutil.move(os.path.join(source_dir, dirname), dest) + if not os.listdir(source_dir): + os.rmdir(source_dir) + self._temp_build_dir = None + self._bundle_build_dirs = bundle_build_dirs + self._bundle_editable_dirs = bundle_editable_dirs + + @property + def delete_marker_filename(self): + assert self.source_dir + return os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME) + + +DELETE_MARKER_MESSAGE = '''\ +This file is placed here by pip to indicate the source was put +here by pip. + +Once this package is successfully installed this source code will be +deleted (unless you remove this file). +''' + + +class RequirementSet(object): + + def __init__(self, build_dir, src_dir, download_dir, download_cache=None, + upgrade=False, ignore_installed=False, + ignore_dependencies=False): + self.build_dir = build_dir + self.src_dir = src_dir + self.download_dir = download_dir + self.download_cache = download_cache + self.upgrade = upgrade + self.ignore_installed = ignore_installed + self.requirements = {} + # Mapping of alias: real_name + self.requirement_aliases = {} + self.unnamed_requirements = [] + self.ignore_dependencies = ignore_dependencies + self.successfully_downloaded = [] + self.successfully_installed = [] + self.reqs_to_cleanup = [] + + def __str__(self): + reqs = [req for req in self.requirements.values() + if not req.comes_from] + reqs.sort(key=lambda req: req.name.lower()) + return ' '.join([str(req.req) for req in reqs]) + + def add_requirement(self, install_req): + name = install_req.name + if not name: + self.unnamed_requirements.append(install_req) + else: + if self.has_requirement(name): + raise InstallationError( + 'Double requirement given: %s (aready in %s, name=%r)' + % (install_req, self.get_requirement(name), name)) + self.requirements[name] = install_req + ## FIXME: what about other normalizations? E.g., _ vs. -? + if name.lower() != name: + self.requirement_aliases[name.lower()] = name + + def has_requirement(self, project_name): + for name in project_name, project_name.lower(): + if name in self.requirements or name in self.requirement_aliases: + return True + return False + + @property + def has_requirements(self): + return self.requirements.values() or self.unnamed_requirements + + @property + def has_editables(self): + if any(req.editable for req in self.requirements.values()): + return True + if any(req.editable for req in self.unnamed_requirements): + return True + return False + + @property + def is_download(self): + if self.download_dir: + self.download_dir = os.path.expanduser(self.download_dir) + if os.path.exists(self.download_dir): + return True + else: + logger.fatal('Could not find download directory') + raise InstallationError( + "Could not find or access download directory '%s'" + % display_path(self.download_dir)) + return False + + def get_requirement(self, project_name): + for name in project_name, project_name.lower(): + if name in self.requirements: + return self.requirements[name] + if name in self.requirement_aliases: + return self.requirements[self.requirement_aliases[name]] + raise KeyError("No project with the name %r" % project_name) + + def uninstall(self, auto_confirm=False): + for req in self.requirements.values(): + req.uninstall(auto_confirm=auto_confirm) + req.commit_uninstall() + + def locate_files(self): + ## FIXME: duplicates code from install_files; relevant code should + ## probably be factored out into a separate method + unnamed = list(self.unnamed_requirements) + reqs = self.requirements.values() + while reqs or unnamed: + if unnamed: + req_to_install = unnamed.pop(0) + else: + req_to_install = reqs.pop(0) + install_needed = True + if not self.ignore_installed and not req_to_install.editable: + req_to_install.check_if_exists() + if req_to_install.satisfied_by: + if self.upgrade: + req_to_install.conflicts_with = req_to_install.satisfied_by + req_to_install.satisfied_by = None + else: + install_needed = False + if req_to_install.satisfied_by: + logger.notify('Requirement already satisfied ' + '(use --upgrade to upgrade): %s' + % req_to_install) + + if req_to_install.editable: + if req_to_install.source_dir is None: + req_to_install.source_dir = req_to_install.build_location(self.src_dir) + elif install_needed: + req_to_install.source_dir = req_to_install.build_location(self.build_dir, not self.is_download) + + if req_to_install.source_dir is not None and not os.path.isdir(req_to_install.source_dir): + raise InstallationError('Could not install requirement %s ' + 'because source folder %s does not exist ' + '(perhaps --no-download was used without first running ' + 'an equivalent install with --no-install?)' + % (req_to_install, req_to_install.source_dir)) + + def prepare_files(self, finder, force_root_egg_info=False, bundle=False): + """Prepare process. Create temp directories, download and/or unpack files.""" + unnamed = list(self.unnamed_requirements) + reqs = self.requirements.values() + while reqs or unnamed: + if unnamed: + req_to_install = unnamed.pop(0) + else: + req_to_install = reqs.pop(0) + install = True + if not self.ignore_installed and not req_to_install.editable: + req_to_install.check_if_exists() + if req_to_install.satisfied_by: + if self.upgrade: + req_to_install.conflicts_with = req_to_install.satisfied_by + req_to_install.satisfied_by = None + else: + install = False + if req_to_install.satisfied_by: + logger.notify('Requirement already satisfied ' + '(use --upgrade to upgrade): %s' + % req_to_install) + if req_to_install.editable: + logger.notify('Obtaining %s' % req_to_install) + elif install: + if req_to_install.url and req_to_install.url.lower().startswith('file:'): + logger.notify('Unpacking %s' % display_path(url_to_path(req_to_install.url))) + else: + logger.notify('Downloading/unpacking %s' % req_to_install) + logger.indent += 2 + try: + is_bundle = False + if req_to_install.editable: + if req_to_install.source_dir is None: + location = req_to_install.build_location(self.src_dir) + req_to_install.source_dir = location + else: + location = req_to_install.source_dir + if not os.path.exists(self.build_dir): + _make_build_dir(self.build_dir) + req_to_install.update_editable(not self.is_download) + if self.is_download: + req_to_install.run_egg_info() + req_to_install.archive(self.download_dir) + else: + req_to_install.run_egg_info() + elif install: + ##@@ if filesystem packages are not marked + ##editable in a req, a non deterministic error + ##occurs when the script attempts to unpack the + ##build directory + + location = req_to_install.build_location(self.build_dir, not self.is_download) + ## FIXME: is the existance of the checkout good enough to use it? I don't think so. + unpack = True + if not os.path.exists(os.path.join(location, 'setup.py')): + ## FIXME: this won't upgrade when there's an existing package unpacked in `location` + if req_to_install.url is None: + url = finder.find_requirement(req_to_install, upgrade=self.upgrade) + else: + ## FIXME: should req_to_install.url already be a link? + url = Link(req_to_install.url) + assert url + if url: + try: + self.unpack_url(url, location, self.is_download) + except urllib2.HTTPError, e: + logger.fatal('Could not install requirement %s because of error %s' + % (req_to_install, e)) + raise InstallationError( + 'Could not install requirement %s because of HTTP error %s for URL %s' + % (req_to_install, e, url)) + else: + unpack = False + if unpack: + is_bundle = req_to_install.is_bundle + url = None + if is_bundle: + req_to_install.move_bundle_files(self.build_dir, self.src_dir) + for subreq in req_to_install.bundle_requirements(): + reqs.append(subreq) + self.add_requirement(subreq) + elif self.is_download: + req_to_install.source_dir = location + if url and url.scheme in vcs.all_schemes: + req_to_install.run_egg_info() + req_to_install.archive(self.download_dir) + else: + req_to_install.source_dir = location + req_to_install.run_egg_info() + if force_root_egg_info: + # We need to run this to make sure that the .egg-info/ + # directory is created for packing in the bundle + req_to_install.run_egg_info(force_root_egg_info=True) + req_to_install.assert_source_matches_version() + #@@ sketchy way of identifying packages not grabbed from an index + if bundle and req_to_install.url: + self.copy_to_build_dir(req_to_install) + if not is_bundle and not self.is_download: + ## FIXME: shouldn't be globally added: + finder.add_dependency_links(req_to_install.dependency_links) + ## FIXME: add extras in here: + if not self.ignore_dependencies: + for req in req_to_install.requirements(): + try: + name = pkg_resources.Requirement.parse(req).project_name + except ValueError, e: + ## FIXME: proper warning + logger.error('Invalid requirement: %r (%s) in requirement %s' % (req, e, req_to_install)) + continue + if self.has_requirement(name): + ## FIXME: check for conflict + continue + subreq = InstallRequirement(req, req_to_install) + reqs.append(subreq) + self.add_requirement(subreq) + if req_to_install.name not in self.requirements: + self.requirements[req_to_install.name] = req_to_install + else: + self.reqs_to_cleanup.append(req_to_install) + if install: + self.successfully_downloaded.append(req_to_install) + if bundle and (req_to_install.url and req_to_install.url.startswith('file:///')): + self.copy_to_build_dir(req_to_install) + finally: + logger.indent -= 2 + + def cleanup_files(self, bundle=False): + """Clean up files, remove builds.""" + logger.notify('Cleaning up...') + logger.indent += 2 + for req in self.reqs_to_cleanup: + req.remove_temporary_source() + + remove_dir = [] + if self._pip_has_created_build_dir(): + remove_dir.append(self.build_dir) + + # The source dir of a bundle can always be removed. + if bundle: + remove_dir.append(self.src_dir) + + for dir in remove_dir: + if os.path.exists(dir): + logger.info('Removing temporary dir %s...' % dir) + rmtree(dir) + + logger.indent -= 2 + + def _pip_has_created_build_dir(self): + return (self.build_dir == build_prefix and + os.path.exists(os.path.join(self.build_dir, PIP_DELETE_MARKER_FILENAME))) + + def copy_to_build_dir(self, req_to_install): + target_dir = req_to_install.editable and self.src_dir or self.build_dir + logger.info("Copying %s to %s" %(req_to_install.name, target_dir)) + dest = os.path.join(target_dir, req_to_install.name) + copytree(req_to_install.source_dir, dest) + call_subprocess(["python", "%s/setup.py"%dest, "clean"]) + + def unpack_url(self, link, location, only_download=False): + if only_download: + location = self.download_dir + if is_vcs_url(link): + return unpack_vcs_link(link, location, only_download) + elif is_file_url(link): + return unpack_file_url(link, location) + else: + if self.download_cache: + self.download_cache = os.path.expanduser(self.download_cache) + return unpack_http_url(link, location, self.download_cache, only_download) + + def install(self, install_options, global_options=()): + """Install everything in this set (after having downloaded and unpacked the packages)""" + to_install = sorted([r for r in self.requirements.values() + if self.upgrade or not r.satisfied_by], + key=lambda p: p.name.lower()) + if to_install: + logger.notify('Installing collected packages: %s' % (', '.join([req.name for req in to_install]))) + logger.indent += 2 + try: + for requirement in to_install: + if requirement.conflicts_with: + logger.notify('Found existing installation: %s' + % requirement.conflicts_with) + logger.indent += 2 + try: + requirement.uninstall(auto_confirm=True) + finally: + logger.indent -= 2 + try: + requirement.install(install_options, global_options) + except: + # if install did not succeed, rollback previous uninstall + if requirement.conflicts_with and not requirement.install_succeeded: + requirement.rollback_uninstall() + raise + else: + if requirement.conflicts_with and requirement.install_succeeded: + requirement.commit_uninstall() + requirement.remove_temporary_source() + finally: + logger.indent -= 2 + self.successfully_installed = to_install + + def create_bundle(self, bundle_filename): + ## FIXME: can't decide which is better; zip is easier to read + ## random files from, but tar.bz2 is smaller and not as lame a + ## format. + + ## FIXME: this file should really include a manifest of the + ## packages, maybe some other metadata files. It would make + ## it easier to detect as well. + zip = zipfile.ZipFile(bundle_filename, 'w', zipfile.ZIP_DEFLATED) + vcs_dirs = [] + for dir, basename in (self.build_dir, 'build'), (self.src_dir, 'src'): + dir = os.path.normcase(os.path.abspath(dir)) + for dirpath, dirnames, filenames in os.walk(dir): + for backend in vcs.backends: + vcs_backend = backend() + vcs_url = vcs_rev = None + if vcs_backend.dirname in dirnames: + for vcs_dir in vcs_dirs: + if dirpath.startswith(vcs_dir): + # vcs bundle file already in parent directory + break + else: + vcs_url, vcs_rev = vcs_backend.get_info( + os.path.join(dir, dirpath)) + vcs_dirs.append(dirpath) + vcs_bundle_file = vcs_backend.bundle_file + vcs_guide = vcs_backend.guide % {'url': vcs_url, + 'rev': vcs_rev} + dirnames.remove(vcs_backend.dirname) + break + if 'pip-egg-info' in dirnames: + dirnames.remove('pip-egg-info') + for dirname in dirnames: + dirname = os.path.join(dirpath, dirname) + name = self._clean_zip_name(dirname, dir) + zip.writestr(basename + '/' + name + '/', '') + for filename in filenames: + if filename == PIP_DELETE_MARKER_FILENAME: + continue + filename = os.path.join(dirpath, filename) + name = self._clean_zip_name(filename, dir) + zip.write(filename, basename + '/' + name) + if vcs_url: + name = os.path.join(dirpath, vcs_bundle_file) + name = self._clean_zip_name(name, dir) + zip.writestr(basename + '/' + name, vcs_guide) + + zip.writestr('pip-manifest.txt', self.bundle_requirements()) + zip.close() + + BUNDLE_HEADER = '''\ +# This is a pip bundle file, that contains many source packages +# that can be installed as a group. You can install this like: +# pip this_file.zip +# The rest of the file contains a list of all the packages included: +''' + + def bundle_requirements(self): + parts = [self.BUNDLE_HEADER] + for req in sorted( + [req for req in self.requirements.values() + if not req.comes_from], + key=lambda x: x.name): + parts.append('%s==%s\n' % (req.name, req.installed_version)) + parts.append('# These packages were installed to satisfy the above requirements:\n') + for req in sorted( + [req for req in self.requirements.values() + if req.comes_from], + key=lambda x: x.name): + parts.append('%s==%s\n' % (req.name, req.installed_version)) + ## FIXME: should we do something with self.unnamed_requirements? + return ''.join(parts) + + def _clean_zip_name(self, name, prefix): + assert name.startswith(prefix+os.path.sep), ( + "name %r doesn't start with prefix %r" % (name, prefix)) + name = name[len(prefix)+1:] + name = name.replace(os.path.sep, '/') + return name + + +def _make_build_dir(build_dir): + os.makedirs(build_dir) + _write_delete_marker_message(os.path.join(build_dir, PIP_DELETE_MARKER_FILENAME)) + + +def _write_delete_marker_message(filepath): + marker_fp = open(filepath, 'w') + marker_fp.write(DELETE_MARKER_MESSAGE) + marker_fp.close() + + +_scheme_re = re.compile(r'^(http|https|file):', re.I) + + +def parse_requirements(filename, finder=None, comes_from=None, options=None): + skip_match = None + skip_regex = options.skip_requirements_regex + if skip_regex: + skip_match = re.compile(skip_regex) + filename, content = get_file_content(filename, comes_from=comes_from) + for line_number, line in enumerate(content.splitlines()): + line_number += 1 + line = line.strip() + if not line or line.startswith('#'): + continue + if skip_match and skip_match.search(line): + continue + if line.startswith('-r') or line.startswith('--requirement'): + if line.startswith('-r'): + req_url = line[2:].strip() + else: + req_url = line[len('--requirement'):].strip().strip('=') + if _scheme_re.search(filename): + # Relative to a URL + req_url = urlparse.urljoin(req_url, filename) + elif not _scheme_re.search(req_url): + req_url = os.path.join(os.path.dirname(filename), req_url) + for item in parse_requirements(req_url, finder, comes_from=filename, options=options): + yield item + elif line.startswith('-Z') or line.startswith('--always-unzip'): + # No longer used, but previously these were used in + # requirement files, so we'll ignore. + pass + elif line.startswith('-f') or line.startswith('--find-links'): + if line.startswith('-f'): + line = line[2:].strip() + else: + line = line[len('--find-links'):].strip().lstrip('=') + ## FIXME: it would be nice to keep track of the source of + ## the find_links: + if finder: + finder.find_links.append(line) + elif line.startswith('-i') or line.startswith('--index-url'): + if line.startswith('-i'): + line = line[2:].strip() + else: + line = line[len('--index-url'):].strip().lstrip('=') + if finder: + finder.index_urls = [line] + elif line.startswith('--extra-index-url'): + line = line[len('--extra-index-url'):].strip().lstrip('=') + if finder: + finder.index_urls.append(line) + else: + comes_from = '-r %s (line %s)' % (filename, line_number) + if line.startswith('-e') or line.startswith('--editable'): + if line.startswith('-e'): + line = line[2:].strip() + else: + line = line[len('--editable'):].strip() + req = InstallRequirement.from_editable( + line, comes_from=comes_from, default_vcs=options.default_vcs) + else: + req = InstallRequirement.from_line(line, comes_from) + yield req + + +def parse_editable(editable_req, default_vcs=None): + """Parses svn+http://blahblah@rev#egg=Foobar into a requirement + (Foobar) and a URL""" + url = editable_req + if os.path.isdir(url) and os.path.exists(os.path.join(url, 'setup.py')): + # Treating it as code that has already been checked out + url = path_to_url(url) + if url.lower().startswith('file:'): + return None, url + for version_control in vcs: + if url.lower().startswith('%s:' % version_control): + url = '%s+%s' % (version_control, url) + if '+' not in url: + if default_vcs: + url = default_vcs + '+' + url + else: + raise InstallationError( + '--editable=%s should be formatted with svn+URL, git+URL, hg+URL or bzr+URL' % editable_req) + vc_type = url.split('+', 1)[0].lower() + if not vcs.get_backend(vc_type): + raise InstallationError( + 'For --editable=%s only svn (svn+URL), Git (git+URL), Mercurial (hg+URL) and Bazaar (bzr+URL) is currently supported' % editable_req) + match = re.search(r'(?:#|#.*?&)egg=([^&]*)', editable_req) + if (not match or not match.group(1)) and vcs.get_backend(vc_type): + parts = [p for p in editable_req.split('#', 1)[0].split('/') if p] + if parts[-2] in ('tags', 'branches', 'tag', 'branch'): + req = parts[-3] + elif parts[-1] == 'trunk': + req = parts[-2] + else: + raise InstallationError( + '--editable=%s is not the right format; it must have #egg=Package' + % editable_req) + else: + req = match.group(1) + ## FIXME: use package_to_requirement? + match = re.search(r'^(.*?)(?:-dev|-\d.*)', req) + if match: + # Strip off -dev, -0.2, etc. + req = match.group(1) + return req, url + + +class UninstallPathSet(object): + """A set of file paths to be removed in the uninstallation of a + requirement.""" + def __init__(self, dist): + self.paths = set() + self._refuse = set() + self.pth = {} + self.dist = dist + self.save_dir = None + self._moved_paths = [] + + def _permitted(self, path): + """ + Return True if the given path is one we are permitted to + remove/modify, False otherwise. + + """ + return is_local(path) + + def _can_uninstall(self): + if not dist_is_local(self.dist): + logger.notify("Not uninstalling %s at %s, outside environment %s" + % (self.dist.project_name, normalize_path(self.dist.location), sys.prefix)) + return False + return True + + def add(self, path): + path = normalize_path(path) + if not os.path.exists(path): + return + if self._permitted(path): + self.paths.add(path) + else: + self._refuse.add(path) + + def add_pth(self, pth_file, entry): + pth_file = normalize_path(pth_file) + if self._permitted(pth_file): + if pth_file not in self.pth: + self.pth[pth_file] = UninstallPthEntries(pth_file) + self.pth[pth_file].add(entry) + else: + self._refuse.add(pth_file) + + def compact(self, paths): + """Compact a path set to contain the minimal number of paths + necessary to contain all paths in the set. If /a/path/ and + /a/path/to/a/file.txt are both in the set, leave only the + shorter path.""" + short_paths = set() + for path in sorted(paths, key=len): + if not any([(path.startswith(shortpath) and + path[len(shortpath.rstrip(os.path.sep))] == os.path.sep) + for shortpath in short_paths]): + short_paths.add(path) + return short_paths + + def _stash(self, path): + return os.path.join( + self.save_dir, os.path.splitdrive(path)[1].lstrip(os.path.sep)) + + def remove(self, auto_confirm=False): + """Remove paths in ``self.paths`` with confirmation (unless + ``auto_confirm`` is True).""" + if not self._can_uninstall(): + return + logger.notify('Uninstalling %s:' % self.dist.project_name) + logger.indent += 2 + paths = sorted(self.compact(self.paths)) + try: + if auto_confirm: + response = 'y' + else: + for path in paths: + logger.notify(path) + response = ask('Proceed (y/n)? ', ('y', 'n')) + if self._refuse: + logger.notify('Not removing or modifying (outside of prefix):') + for path in self.compact(self._refuse): + logger.notify(path) + if response == 'y': + self.save_dir = tempfile.mkdtemp(suffix='-uninstall', + prefix='pip-') + for path in paths: + new_path = self._stash(path) + logger.info('Removing file or directory %s' % path) + self._moved_paths.append(path) + renames(path, new_path) + for pth in self.pth.values(): + pth.remove() + logger.notify('Successfully uninstalled %s' % self.dist.project_name) + + finally: + logger.indent -= 2 + + def rollback(self): + """Rollback the changes previously made by remove().""" + if self.save_dir is None: + logger.error("Can't roll back %s; was not uninstalled" % self.dist.project_name) + return False + logger.notify('Rolling back uninstall of %s' % self.dist.project_name) + for path in self._moved_paths: + tmp_path = self._stash(path) + logger.info('Replacing %s' % path) + renames(tmp_path, path) + for pth in self.pth: + pth.rollback() + + def commit(self): + """Remove temporary save dir: rollback will no longer be possible.""" + if self.save_dir is not None: + shutil.rmtree(self.save_dir) + self.save_dir = None + self._moved_paths = [] + + +class UninstallPthEntries(object): + def __init__(self, pth_file): + if not os.path.isfile(pth_file): + raise UninstallationError("Cannot remove entries from nonexistent file %s" % pth_file) + self.file = pth_file + self.entries = set() + self._saved_lines = None + + def add(self, entry): + entry = os.path.normcase(entry) + # On Windows, os.path.normcase converts the entry to use + # backslashes. This is correct for entries that describe absolute + # paths outside of site-packages, but all the others use forward + # slashes. + if sys.platform == 'win32' and not os.path.splitdrive(entry)[0]: + entry = entry.replace('\\', '/') + self.entries.add(entry) + + def remove(self): + logger.info('Removing pth entries from %s:' % self.file) + fh = open(self.file, 'r') + lines = fh.readlines() + self._saved_lines = lines + fh.close() + try: + for entry in self.entries: + logger.info('Removing entry: %s' % entry) + try: + lines.remove(entry + '\n') + except ValueError: + pass + finally: + pass + fh = open(self.file, 'wb') + fh.writelines(lines) + fh.close() + + def rollback(self): + if self._saved_lines is None: + logger.error('Cannot roll back changes to %s, none were made' % self.file) + return False + logger.info('Rolling %s back to previous state' % self.file) + fh = open(self.file, 'wb') + fh.writelines(self._saved_lines) + fh.close() + return True + + +class FakeFile(object): + """Wrap a list of lines in an object with readline() to make + ConfigParser happy.""" + def __init__(self, lines): + self._gen = (l for l in lines) + + def readline(self): + try: + return self._gen.next() + except StopIteration: + return '' diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/runner.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/runner.py new file mode 100644 index 000000000..be830ad9a --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/runner.py @@ -0,0 +1,18 @@ +import sys +import os + + +def run(): + base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + ## FIXME: this is kind of crude; if we could create a fake pip + ## module, then exec into it and update pip.__path__ properly, we + ## wouldn't have to update sys.path: + sys.path.insert(0, base) + import pip + return pip.main() + + +if __name__ == '__main__': + exit = run() + if exit: + sys.exit(exit) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/util.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/util.py new file mode 100644 index 000000000..1eab34c06 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/util.py @@ -0,0 +1,479 @@ +import sys +import shutil +import os +import stat +import re +import posixpath +import pkg_resources +import zipfile +import tarfile +from pip.exceptions import InstallationError +from pip.backwardcompat import WindowsError +from pip.locations import site_packages, running_under_virtualenv +from pip.log import logger + +__all__ = ['rmtree', 'display_path', 'backup_dir', + 'find_command', 'ask', 'Inf', + 'normalize_name', 'splitext', + 'format_size', 'is_installable_dir', + 'is_svn_page', 'file_contents', + 'split_leading_dir', 'has_leading_dir', + 'make_path_relative', 'normalize_path', + 'renames', 'get_terminal_size', + 'unzip_file', 'untar_file', 'create_download_cache_folder', + 'cache_download', 'unpack_file'] + + +def rmtree(dir): + shutil.rmtree(dir, ignore_errors=True, + onerror=rmtree_errorhandler) + + +def rmtree_errorhandler(func, path, exc_info): + """On Windows, the files in .svn are read-only, so when rmtree() tries to + remove them, an exception is thrown. We catch that here, remove the + read-only attribute, and hopefully continue without problems.""" + exctype, value = exc_info[:2] + # lookin for a windows error + if exctype is not WindowsError or 'Access is denied' not in str(value): + raise + # file type should currently be read only + if ((os.stat(path).st_mode & stat.S_IREAD) != stat.S_IREAD): + raise + # convert to read/write + os.chmod(path, stat.S_IWRITE) + # use the original function to repeat the operation + func(path) + + +def display_path(path): + """Gives the display value for a given path, making it relative to cwd + if possible.""" + path = os.path.normcase(os.path.abspath(path)) + if path.startswith(os.getcwd() + os.path.sep): + path = '.' + path[len(os.getcwd()):] + return path + + +def backup_dir(dir, ext='.bak'): + """Figure out the name of a directory to back up the given dir to + (adding .bak, .bak2, etc)""" + n = 1 + extension = ext + while os.path.exists(dir + extension): + n += 1 + extension = ext + str(n) + return dir + extension + + +def find_command(cmd, paths=None, pathext=None): + """Searches the PATH for the given command and returns its path""" + if paths is None: + paths = os.environ.get('PATH', []).split(os.pathsep) + if isinstance(paths, basestring): + paths = [paths] + # check if there are funny path extensions for executables, e.g. Windows + if pathext is None: + pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD') + pathext = [ext for ext in pathext.lower().split(os.pathsep)] + # don't use extensions if the command ends with one of them + if os.path.splitext(cmd)[1].lower() in pathext: + pathext = [''] + # check if we find the command on PATH + for path in paths: + # try without extension first + cmd_path = os.path.join(path, cmd) + for ext in pathext: + # then including the extension + cmd_path_ext = cmd_path + ext + if os.path.exists(cmd_path_ext): + return cmd_path_ext + if os.path.exists(cmd_path): + return cmd_path + return None + + +def ask(message, options): + """Ask the message interactively, with the given possible responses""" + while 1: + if os.environ.get('PIP_NO_INPUT'): + raise Exception('No input was expected ($PIP_NO_INPUT set); question: %s' % message) + response = raw_input(message) + response = response.strip().lower() + if response not in options: + print 'Your response (%r) was not one of the expected responses: %s' % ( + response, ', '.join(options)) + else: + return response + + +class _Inf(object): + """I am bigger than everything!""" + def __cmp__(self, a): + if self is a: + return 0 + return 1 + + def __repr__(self): + return 'Inf' + +Inf = _Inf() +del _Inf + + +_normalize_re = re.compile(r'[^a-z]', re.I) + + +def normalize_name(name): + return _normalize_re.sub('-', name.lower()) + + +def format_size(bytes): + if bytes > 1000*1000: + return '%.1fMb' % (bytes/1000.0/1000) + elif bytes > 10*1000: + return '%iKb' % (bytes/1000) + elif bytes > 1000: + return '%.1fKb' % (bytes/1000.0) + else: + return '%ibytes' % bytes + + +def is_installable_dir(path): + """Return True if `path` is a directory containing a setup.py file.""" + if not os.path.isdir(path): + return False + setup_py = os.path.join(path, 'setup.py') + if os.path.isfile(setup_py): + return True + return False + + +def is_svn_page(html): + """Returns true if the page appears to be the index page of an svn repository""" + return (re.search(r'[^<]*Revision \d+:', html) + and re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I)) + + +def file_contents(filename): + fp = open(filename, 'rb') + try: + return fp.read() + finally: + fp.close() + + +def split_leading_dir(path): + path = str(path) + path = path.lstrip('/').lstrip('\\') + if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) + or '\\' not in path): + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) + else: + return path, '' + + +def has_leading_dir(paths): + """Returns true if all the paths have the same leading path name + (i.e., everything is in one subdirectory in an archive)""" + common_prefix = None + for path in paths: + prefix, rest = split_leading_dir(path) + if not prefix: + return False + elif common_prefix is None: + common_prefix = prefix + elif prefix != common_prefix: + return False + return True + + +def make_path_relative(path, rel_to): + """ + Make a filename relative, where the filename path, and it is + relative to rel_to + + >>> make_relative_path('/usr/share/something/a-file.pth', + ... '/usr/share/another-place/src/Directory') + '../../../something/a-file.pth' + >>> make_relative_path('/usr/share/something/a-file.pth', + ... '/home/user/src/Directory') + '../../../usr/share/something/a-file.pth' + >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/') + 'a-file.pth' + """ + path_filename = os.path.basename(path) + path = os.path.dirname(path) + path = os.path.normpath(os.path.abspath(path)) + rel_to = os.path.normpath(os.path.abspath(rel_to)) + path_parts = path.strip(os.path.sep).split(os.path.sep) + rel_to_parts = rel_to.strip(os.path.sep).split(os.path.sep) + while path_parts and rel_to_parts and path_parts[0] == rel_to_parts[0]: + path_parts.pop(0) + rel_to_parts.pop(0) + full_parts = ['..']*len(rel_to_parts) + path_parts + [path_filename] + if full_parts == ['']: + return '.' + os.path.sep + return os.path.sep.join(full_parts) + + +def normalize_path(path): + """ + Convert a path to its canonical, case-normalized, absolute version. + + """ + return os.path.normcase(os.path.realpath(path)) + + +def splitext(path): + """Like os.path.splitext, but take off .tar too""" + base, ext = posixpath.splitext(path) + if base.lower().endswith('.tar'): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + + +def renames(old, new): + """Like os.renames(), but handles renaming across devices.""" + # Implementation borrowed from os.renames(). + head, tail = os.path.split(new) + if head and tail and not os.path.exists(head): + os.makedirs(head) + + shutil.move(old, new) + + head, tail = os.path.split(old) + if head and tail: + try: + os.removedirs(head) + except OSError: + pass + + +def is_local(path): + """ + Return True if path is within sys.prefix, if we're running in a virtualenv. + + If we're not in a virtualenv, all paths are considered "local." + + """ + if not running_under_virtualenv(): + return True + return normalize_path(path).startswith(normalize_path(sys.prefix)) + + +def dist_is_local(dist): + """ + Return True if given Distribution object is installed locally + (i.e. within current virtualenv). + + Always True if we're not in a virtualenv. + + """ + return is_local(dist_location(dist)) + + +def get_installed_distributions(local_only=True, skip=('setuptools', 'pip', 'python')): + """ + Return a list of installed Distribution objects. + + If ``local_only`` is True (default), only return installations + local to the current virtualenv, if in a virtualenv. + + ``skip`` argument is an iterable of lower-case project names to + ignore; defaults to ('setuptools', 'pip', 'python'). [FIXME also + skip virtualenv?] + + """ + if local_only: + local_test = dist_is_local + else: + local_test = lambda d: True + return [d for d in pkg_resources.working_set if local_test(d) and d.key not in skip] + + +def egg_link_path(dist): + """ + Return the path where we'd expect to find a .egg-link file for + this distribution. (There doesn't seem to be any metadata in the + Distribution object for a develop egg that points back to its + .egg-link and easy-install.pth files). + + This won't find a globally-installed develop egg if we're in a + virtualenv. + + """ + return os.path.join(site_packages, dist.project_name) + '.egg-link' + + +def dist_location(dist): + """ + Get the site-packages location of this distribution. Generally + this is dist.location, except in the case of develop-installed + packages, where dist.location is the source code location, and we + want to know where the egg-link file is. + + """ + egg_link = egg_link_path(dist) + if os.path.exists(egg_link): + return egg_link + return dist.location + + +def get_terminal_size(): + """Returns a tuple (x, y) representing the width(x) and the height(x) + in characters of the terminal window.""" + def ioctl_GWINSZ(fd): + try: + import fcntl + import termios + import struct + cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, + '1234')) + except: + return None + if cr == (0, 0): + return None + if cr == (0, 0): + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + return int(cr[1]), int(cr[0]) + + +def unzip_file(filename, location, flatten=True): + """Unzip the file (zip file located at filename) to the destination + location""" + if not os.path.exists(location): + os.makedirs(location) + zipfp = open(filename, 'rb') + try: + zip = zipfile.ZipFile(zipfp) + leading = has_leading_dir(zip.namelist()) and flatten + for name in zip.namelist(): + data = zip.read(name) + fn = name + if leading: + fn = split_leading_dir(name)[1] + fn = os.path.join(location, fn) + dir = os.path.dirname(fn) + if not os.path.exists(dir): + os.makedirs(dir) + if fn.endswith('/') or fn.endswith('\\'): + # A directory + if not os.path.exists(fn): + os.makedirs(fn) + else: + fp = open(fn, 'wb') + try: + fp.write(data) + finally: + fp.close() + finally: + zipfp.close() + + +def untar_file(filename, location): + """Untar the file (tar file located at filename) to the destination location""" + if not os.path.exists(location): + os.makedirs(location) + if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): + mode = 'r:gz' + elif filename.lower().endswith('.bz2') or filename.lower().endswith('.tbz'): + mode = 'r:bz2' + elif filename.lower().endswith('.tar'): + mode = 'r' + else: + logger.warn('Cannot determine compression type for file %s' % filename) + mode = 'r:*' + tar = tarfile.open(filename, mode) + try: + # note: python<=2.5 doesnt seem to know about pax headers, filter them + leading = has_leading_dir([ + member.name for member in tar.getmembers() + if member.name != 'pax_global_header' + ]) + for member in tar.getmembers(): + fn = member.name + if fn == 'pax_global_header': + continue + if leading: + fn = split_leading_dir(fn)[1] + path = os.path.join(location, fn) + if member.isdir(): + if not os.path.exists(path): + os.makedirs(path) + else: + try: + fp = tar.extractfile(member) + except (KeyError, AttributeError), e: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warn( + 'In the tar file %s the member %s is invalid: %s' + % (filename, member.name, e)) + continue + if not os.path.exists(os.path.dirname(path)): + os.makedirs(os.path.dirname(path)) + destfp = open(path, 'wb') + try: + shutil.copyfileobj(fp, destfp) + finally: + destfp.close() + fp.close() + finally: + tar.close() + + +def create_download_cache_folder(folder): + logger.indent -= 2 + logger.notify('Creating supposed download cache at %s' % folder) + logger.indent += 2 + os.makedirs(folder) + + +def cache_download(target_file, temp_location, content_type): + logger.notify('Storing download in cache at %s' % display_path(target_file)) + shutil.copyfile(temp_location, target_file) + fp = open(target_file+'.content-type', 'w') + fp.write(content_type) + fp.close() + os.unlink(temp_location) + + +def unpack_file(filename, location, content_type, link): + if (content_type == 'application/zip' + or filename.endswith('.zip') + or filename.endswith('.pybundle') + or zipfile.is_zipfile(filename)): + unzip_file(filename, location, flatten=not filename.endswith('.pybundle')) + elif (content_type == 'application/x-gzip' + or tarfile.is_tarfile(filename) + or splitext(filename)[1].lower() in ('.tar', '.tar.gz', '.tar.bz2', '.tgz', '.tbz')): + untar_file(filename, location) + elif (content_type and content_type.startswith('text/html') + and is_svn_page(file_contents(filename))): + # We don't really care about this + from pip.vcs.subversion import Subversion + Subversion('svn+' + link.url).unpack(location) + else: + ## FIXME: handle? + ## FIXME: magic signatures? + logger.fatal('Cannot unpack file %s (downloaded from %s, content-type: %s); cannot detect archive format' + % (filename, location, content_type)) + raise InstallationError('Cannot determine archive format of %s' % location) + + + diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/__init__.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/__init__.py new file mode 100644 index 000000000..e110440c7 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/__init__.py @@ -0,0 +1,238 @@ +"""Handles all VCS (version control) support""" + +import os +import shutil +import urlparse +import urllib + +from pip.exceptions import BadCommand +from pip.log import logger +from pip.util import display_path, backup_dir, find_command, ask + + +__all__ = ['vcs', 'get_src_requirement', 'import_vcs_support'] + + +class VcsSupport(object): + _registry = {} + schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp'] + + def __init__(self): + # Register more schemes with urlparse for various version control systems + urlparse.uses_netloc.extend(self.schemes) + urlparse.uses_fragment.extend(self.schemes) + super(VcsSupport, self).__init__() + + def __iter__(self): + return self._registry.__iter__() + + @property + def backends(self): + return self._registry.values() + + @property + def dirnames(self): + return [backend.dirname for backend in self.backends] + + @property + def all_schemes(self): + schemes = [] + for backend in self.backends: + schemes.extend(backend.schemes) + return schemes + + def register(self, cls): + if not hasattr(cls, 'name'): + logger.warn('Cannot register VCS %s' % cls.__name__) + return + if cls.name not in self._registry: + self._registry[cls.name] = cls + + def unregister(self, cls=None, name=None): + if name in self._registry: + del self._registry[name] + elif cls in self._registry.values(): + del self._registry[cls.name] + else: + logger.warn('Cannot unregister because no class or name given') + + def get_backend_name(self, location): + """ + Return the name of the version control backend if found at given + location, e.g. vcs.get_backend_name('/path/to/vcs/checkout') + """ + for vc_type in self._registry.values(): + path = os.path.join(location, vc_type.dirname) + if os.path.exists(path): + return vc_type.name + return None + + def get_backend(self, name): + name = name.lower() + if name in self._registry: + return self._registry[name] + + def get_backend_from_location(self, location): + vc_type = self.get_backend_name(location) + if vc_type: + return self.get_backend(vc_type) + return None + + +vcs = VcsSupport() + + +class VersionControl(object): + name = '' + dirname = '' + + def __init__(self, url=None, *args, **kwargs): + self.url = url + self._cmd = None + super(VersionControl, self).__init__(*args, **kwargs) + + def _filter(self, line): + return (logger.INFO, line) + + def _is_local_repository(self, repo): + """ + posix absolute paths start with os.path.sep, + win32 ones ones start with drive (like c:\\folder) + """ + drive, tail = os.path.splitdrive(repo) + return repo.startswith(os.path.sep) or drive + + @property + def cmd(self): + if self._cmd is not None: + return self._cmd + command = find_command(self.name) + if command is None: + raise BadCommand('Cannot find command %r' % self.name) + logger.info('Found command %r at %r' % (self.name, command)) + self._cmd = command + return command + + def get_url_rev(self): + """ + Returns the correct repository URL and revision by parsing the given + repository URL + """ + url = self.url.split('+', 1)[1] + scheme, netloc, path, query, frag = urlparse.urlsplit(url) + rev = None + if '@' in path: + path, rev = path.rsplit('@', 1) + url = urlparse.urlunsplit((scheme, netloc, path, query, '')) + return url, rev + + def get_info(self, location): + """ + Returns (url, revision), where both are strings + """ + assert not location.rstrip('/').endswith(self.dirname), 'Bad directory: %s' % location + return self.get_url(location), self.get_revision(location) + + def normalize_url(self, url): + """ + Normalize a URL for comparison by unquoting it and removing any trailing slash. + """ + return urllib.unquote(url).rstrip('/') + + def compare_urls(self, url1, url2): + """ + Compare two repo URLs for identity, ignoring incidental differences. + """ + return (self.normalize_url(url1) == self.normalize_url(url2)) + + def parse_vcs_bundle_file(self, content): + """ + Takes the contents of the bundled text file that explains how to revert + the stripped off version control data of the given package and returns + the URL and revision of it. + """ + raise NotImplementedError + + def obtain(self, dest): + """ + Called when installing or updating an editable package, takes the + source path of the checkout. + """ + raise NotImplementedError + + def switch(self, dest, url, rev_options): + """ + Switch the repo at ``dest`` to point to ``URL``. + """ + raise NotImplemented + + def update(self, dest, rev_options): + """ + Update an already-existing repo to the given ``rev_options``. + """ + raise NotImplementedError + + def check_destination(self, dest, url, rev_options, rev_display): + """ + Prepare a location to receive a checkout/clone. + + Return True if the location is ready for (and requires) a + checkout/clone, False otherwise. + """ + checkout = True + prompt = False + if os.path.exists(dest): + checkout = False + if os.path.exists(os.path.join(dest, self.dirname)): + existing_url = self.get_url(dest) + if self.compare_urls(existing_url, url): + logger.info('%s in %s exists, and has correct URL (%s)' + % (self.repo_name.title(), display_path(dest), url)) + logger.notify('Updating %s %s%s' + % (display_path(dest), self.repo_name, rev_display)) + self.update(dest, rev_options) + else: + logger.warn('%s %s in %s exists with URL %s' + % (self.name, self.repo_name, display_path(dest), existing_url)) + prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', ('s', 'i', 'w', 'b')) + else: + logger.warn('Directory %s already exists, and is not a %s %s.' + % (dest, self.name, self.repo_name)) + prompt = ('(i)gnore, (w)ipe, (b)ackup ', ('i', 'w', 'b')) + if prompt: + logger.warn('The plan is to install the %s repository %s' + % (self.name, url)) + response = ask('What to do? %s' % prompt[0], prompt[1]) + + if response == 's': + logger.notify('Switching %s %s to %s%s' + % (self.repo_name, display_path(dest), url, rev_display)) + self.switch(dest, url, rev_options) + elif response == 'i': + # do nothing + pass + elif response == 'w': + logger.warn('Deleting %s' % display_path(dest)) + shutil.rmtree(dest) + checkout = True + elif response == 'b': + dest_dir = backup_dir(dest) + logger.warn('Backing up %s to %s' + % (display_path(dest), dest_dir)) + shutil.move(dest, dest_dir) + checkout = True + return checkout + + def unpack(self, location): + raise NotImplementedError + + def get_src_requirement(self, dist, location, find_tags=False): + raise NotImplementedError + + +def get_src_requirement(dist, location, find_tags): + version_control = vcs.get_backend_from_location(location) + if version_control: + return version_control().get_src_requirement(dist, location, find_tags) + logger.warn('cannot determine version of editable source in %s (is not SVN checkout, Git clone, Mercurial clone or Bazaar branch)' % location) + return dist.as_requirement() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/bazaar.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/bazaar.py new file mode 100644 index 000000000..3b6ea8f04 --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/bazaar.py @@ -0,0 +1,138 @@ +import os +import shutil +import tempfile +import re +from pip import call_subprocess +from pip.log import logger +from pip.util import rmtree, display_path +from pip.vcs import vcs, VersionControl +from pip.download import path_to_url2 + + +class Bazaar(VersionControl): + name = 'bzr' + dirname = '.bzr' + repo_name = 'branch' + bundle_file = 'bzr-branch.txt' + schemes = ('bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp') + guide = ('# This was a Bazaar branch; to make it a branch again run:\n' + 'bzr branch -r %(rev)s %(url)s .\n') + + def parse_vcs_bundle_file(self, content): + url = rev = None + for line in content.splitlines(): + if not line.strip() or line.strip().startswith('#'): + continue + match = re.search(r'^bzr\s*branch\s*-r\s*(\d*)', line) + if match: + rev = match.group(1).strip() + url = line[match.end():].strip().split(None, 1)[0] + if url and rev: + return url, rev + return None, None + + def unpack(self, location): + """Get the bzr branch at the url to the destination location""" + url, rev = self.get_url_rev() + logger.notify('Checking out bzr repository %s to %s' % (url, location)) + logger.indent += 2 + try: + if os.path.exists(location): + os.rmdir(location) + call_subprocess( + [self.cmd, 'branch', url, location], + filter_stdout=self._filter, show_stdout=False) + finally: + logger.indent -= 2 + + def export(self, location): + """Export the Bazaar repository at the url to the destination location""" + temp_dir = tempfile.mkdtemp('-export', 'pip-') + self.unpack(temp_dir) + if os.path.exists(location): + # Remove the location to make sure Bazaar can export it correctly + rmtree(location) + try: + call_subprocess([self.cmd, 'export', location], cwd=temp_dir, + filter_stdout=self._filter, show_stdout=False) + finally: + shutil.rmtree(temp_dir) + + def switch(self, dest, url, rev_options): + call_subprocess([self.cmd, 'switch', url], cwd=dest) + + def update(self, dest, rev_options): + call_subprocess( + [self.cmd, 'pull', '-q'] + rev_options, cwd=dest) + + def obtain(self, dest): + url, rev = self.get_url_rev() + if rev: + rev_options = ['-r', rev] + rev_display = ' (to revision %s)' % rev + else: + rev_options = [] + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.notify('Checking out %s%s to %s' + % (url, rev_display, display_path(dest))) + call_subprocess( + [self.cmd, 'branch', '-q'] + rev_options + [url, dest]) + + def get_url_rev(self): + # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it + url, rev = super(Bazaar, self).get_url_rev() + if url.startswith('ssh://'): + url = 'bzr+' + url + return url, rev + + def get_url(self, location): + urls = call_subprocess( + [self.cmd, 'info'], show_stdout=False, cwd=location) + for line in urls.splitlines(): + line = line.strip() + for x in ('checkout of branch: ', + 'parent branch: '): + if line.startswith(x): + repo = line.split(x)[1] + if self._is_local_repository(repo): + return path_to_url2(repo) + return repo + return None + + def get_revision(self, location): + revision = call_subprocess( + [self.cmd, 'revno'], show_stdout=False, cwd=location) + return revision.splitlines()[-1] + + def get_tag_revs(self, location): + tags = call_subprocess( + [self.cmd, 'tags'], show_stdout=False, cwd=location) + tag_revs = [] + for line in tags.splitlines(): + tags_match = re.search(r'([.\w-]+)\s*(.*)$', line) + if tags_match: + tag = tags_match.group(1) + rev = tags_match.group(2) + tag_revs.append((rev.strip(), tag.strip())) + return dict(tag_revs) + + def get_src_requirement(self, dist, location, find_tags): + repo = self.get_url(location) + if not repo.lower().startswith('bzr:'): + repo = 'bzr+' + repo + egg_project_name = dist.egg_name().split('-', 1)[0] + if not repo: + return None + current_rev = self.get_revision(location) + tag_revs = self.get_tag_revs(location) + + if current_rev in tag_revs: + # It's a tag + full_egg_name = '%s-%s' % (egg_project_name, tag_revs[current_rev]) + else: + full_egg_name = '%s-dev_r%s' % (dist.egg_name(), current_rev) + return '%s@%s#egg=%s' % (repo, current_rev, full_egg_name) + + +vcs.register(Bazaar) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/git.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/git.py new file mode 100644 index 000000000..0701e49ec --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/git.py @@ -0,0 +1,204 @@ +import os +import shutil +import tempfile +import re +from pip import call_subprocess +from pip.util import display_path +from pip.vcs import vcs, VersionControl +from pip.log import logger +from urllib import url2pathname +from urlparse import urlsplit, urlunsplit + + +class Git(VersionControl): + name = 'git' + dirname = '.git' + repo_name = 'clone' + schemes = ('git', 'git+http', 'git+ssh', 'git+git', 'git+file') + bundle_file = 'git-clone.txt' + guide = ('# This was a Git repo; to make it a repo again run:\n' + 'git init\ngit remote add origin %(url)s -f\ngit checkout %(rev)s\n') + + def __init__(self, url=None, *args, **kwargs): + + # Works around an apparent Git bug + # (see http://article.gmane.org/gmane.comp.version-control.git/146500) + if url: + scheme, netloc, path, query, fragment = urlsplit(url) + if scheme.endswith('file'): + initial_slashes = path[:-len(path.lstrip('/'))] + newpath = initial_slashes + url2pathname(path).replace('\\', '/').lstrip('/') + url = urlunsplit((scheme, netloc, newpath, query, fragment)) + after_plus = scheme.find('+')+1 + url = scheme[:after_plus]+ urlunsplit((scheme[after_plus:], netloc, newpath, query, fragment)) + + super(Git, self).__init__(url, *args, **kwargs) + + def parse_vcs_bundle_file(self, content): + url = rev = None + for line in content.splitlines(): + if not line.strip() or line.strip().startswith('#'): + continue + url_match = re.search(r'git\s*remote\s*add\s*origin(.*)\s*-f', line) + if url_match: + url = url_match.group(1).strip() + rev_match = re.search(r'^git\s*checkout\s*-q\s*(.*)\s*', line) + if rev_match: + rev = rev_match.group(1).strip() + if url and rev: + return url, rev + return None, None + + def unpack(self, location): + """Clone the Git repository at the url to the destination location""" + url, rev = self.get_url_rev() + logger.notify('Cloning Git repository %s to %s' % (url, location)) + logger.indent += 2 + try: + if os.path.exists(location): + os.rmdir(location) + call_subprocess( + [self.cmd, 'clone', url, location], + filter_stdout=self._filter, show_stdout=False) + finally: + logger.indent -= 2 + + def export(self, location): + """Export the Git repository at the url to the destination location""" + temp_dir = tempfile.mkdtemp('-export', 'pip-') + self.unpack(temp_dir) + try: + if not location.endswith('/'): + location = location + '/' + call_subprocess( + [self.cmd, 'checkout-index', '-a', '-f', '--prefix', location], + filter_stdout=self._filter, show_stdout=False, cwd=temp_dir) + finally: + shutil.rmtree(temp_dir) + + def check_rev_options(self, rev, dest, rev_options): + """Check the revision options before checkout to compensate that tags + and branches may need origin/ as a prefix. + Returns the SHA1 of the branch or tag if found. + """ + revisions = self.get_tag_revs(dest) + revisions.update(self.get_branch_revs(dest)) + inverse_revisions = dict((v, k) for k, v in revisions.iteritems()) + # Check if rev is a branch name + origin_rev = 'origin/%s' % rev + if origin_rev in inverse_revisions: + return [inverse_revisions[origin_rev]] + elif rev in inverse_revisions: + return [inverse_revisions[rev]] + else: + logger.warn("Could not find a tag or branch '%s', assuming commit." % rev) + return rev_options + + def switch(self, dest, url, rev_options): + call_subprocess( + [self.cmd, 'config', 'remote.origin.url', url], cwd=dest) + call_subprocess( + [self.cmd, 'checkout', '-q'] + rev_options, cwd=dest) + + def update(self, dest, rev_options): + call_subprocess([self.cmd, 'pull', '-q'], cwd=dest) + call_subprocess( + [self.cmd, 'checkout', '-q', '-f'] + rev_options, cwd=dest) + + def obtain(self, dest): + url, rev = self.get_url_rev() + if rev: + rev_options = [rev] + rev_display = ' (to %s)' % rev + else: + rev_options = ['master'] + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.notify('Cloning %s%s to %s' % (url, rev_display, display_path(dest))) + call_subprocess([self.cmd, 'clone', '-q', url, dest]) + if rev: + rev_options = self.check_rev_options(rev, dest, rev_options) + # Only do a checkout if rev_options differs from HEAD + if not self.get_revision(dest).startswith(rev_options[0]): + call_subprocess([self.cmd, 'checkout', '-q'] + rev_options, cwd=dest) + + def get_url(self, location): + url = call_subprocess( + [self.cmd, 'config', 'remote.origin.url'], + show_stdout=False, cwd=location) + return url.strip() + + def get_revision(self, location): + current_rev = call_subprocess( + [self.cmd, 'rev-parse', 'HEAD'], show_stdout=False, cwd=location) + return current_rev.strip() + + def get_tag_revs(self, location): + tags = call_subprocess( + [self.cmd, 'tag', '-l'], + show_stdout=False, raise_on_returncode=False, cwd=location) + tag_revs = [] + for line in tags.splitlines(): + tag = line.strip() + rev = call_subprocess( + [self.cmd, 'rev-parse', tag], show_stdout=False, cwd=location) + tag_revs.append((rev.strip(), tag)) + tag_revs = dict(tag_revs) + return tag_revs + + def get_branch_revs(self, location): + branches = call_subprocess( + [self.cmd, 'branch', '-r'], show_stdout=False, cwd=location) + branch_revs = [] + for line in branches.splitlines(): + line = line.split('->')[0].strip() + branch = "".join([b for b in line.split() if b != '*']) + rev = call_subprocess( + [self.cmd, 'rev-parse', branch], show_stdout=False, cwd=location) + branch_revs.append((rev.strip(), branch)) + branch_revs = dict(branch_revs) + return branch_revs + + def get_src_requirement(self, dist, location, find_tags): + repo = self.get_url(location) + if not repo.lower().startswith('git:'): + repo = 'git+' + repo + egg_project_name = dist.egg_name().split('-', 1)[0] + if not repo: + return None + current_rev = self.get_revision(location) + tag_revs = self.get_tag_revs(location) + branch_revs = self.get_branch_revs(location) + + if current_rev in tag_revs: + # It's a tag + full_egg_name = '%s-%s' % (egg_project_name, tag_revs[current_rev]) + elif (current_rev in branch_revs and + branch_revs[current_rev] != 'origin/master'): + # It's the head of a branch + full_egg_name = '%s-%s' % (dist.egg_name(), + branch_revs[current_rev].replace('origin/', '')) + else: + full_egg_name = '%s-dev' % dist.egg_name() + + return '%s@%s#egg=%s' % (repo, current_rev, full_egg_name) + + def get_url_rev(self): + """ + Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. + That's required because although they use SSH they sometimes doesn't + work with a ssh:// scheme (e.g. Github). But we need a scheme for + parsing. Hence we remove it again afterwards and return it as a stub. + """ + if not '://' in self.url: + assert not 'file:' in self.url + self.url = self.url.replace('git+', 'git+ssh://') + url, rev = super(Git, self).get_url_rev() + url = url.replace('ssh://', '') + else: + url, rev = super(Git, self).get_url_rev() + + return url, rev + + +vcs.register(Git) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/mercurial.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/mercurial.py new file mode 100644 index 000000000..70c8c833d --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/mercurial.py @@ -0,0 +1,162 @@ +import os +import shutil +import tempfile +import re +import ConfigParser +from pip import call_subprocess +from pip.util import display_path +from pip.log import logger +from pip.vcs import vcs, VersionControl +from pip.download import path_to_url2 + + +class Mercurial(VersionControl): + name = 'hg' + dirname = '.hg' + repo_name = 'clone' + schemes = ('hg', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http') + bundle_file = 'hg-clone.txt' + guide = ('# This was a Mercurial repo; to make it a repo again run:\n' + 'hg init\nhg pull %(url)s\nhg update -r %(rev)s\n') + + def parse_vcs_bundle_file(self, content): + url = rev = None + for line in content.splitlines(): + if not line.strip() or line.strip().startswith('#'): + continue + url_match = re.search(r'hg\s*pull\s*(.*)\s*', line) + if url_match: + url = url_match.group(1).strip() + rev_match = re.search(r'^hg\s*update\s*-r\s*(.*)\s*', line) + if rev_match: + rev = rev_match.group(1).strip() + if url and rev: + return url, rev + return None, None + + def unpack(self, location): + """Clone the Hg repository at the url to the destination location""" + url, rev = self.get_url_rev() + logger.notify('Cloning Mercurial repository %s to %s' % (url, location)) + logger.indent += 2 + try: + if os.path.exists(location): + os.rmdir(location) + call_subprocess( + [self.cmd, 'clone', url, location], + filter_stdout=self._filter, show_stdout=False) + finally: + logger.indent -= 2 + + def export(self, location): + """Export the Hg repository at the url to the destination location""" + temp_dir = tempfile.mkdtemp('-export', 'pip-') + self.unpack(temp_dir) + try: + call_subprocess( + [self.cmd, 'archive', location], + filter_stdout=self._filter, show_stdout=False, cwd=temp_dir) + finally: + shutil.rmtree(temp_dir) + + def switch(self, dest, url, rev_options): + repo_config = os.path.join(dest, self.dirname, 'hgrc') + config = ConfigParser.SafeConfigParser() + try: + config.read(repo_config) + config.set('paths', 'default', url) + config_file = open(repo_config, 'w') + config.write(config_file) + config_file.close() + except (OSError, ConfigParser.NoSectionError), e: + logger.warn( + 'Could not switch Mercurial repository to %s: %s' + % (url, e)) + else: + call_subprocess([self.cmd, 'update', '-q'] + rev_options, cwd=dest) + + def update(self, dest, rev_options): + call_subprocess([self.cmd, 'pull', '-q'], cwd=dest) + call_subprocess( + [self.cmd, 'update', '-q'] + rev_options, cwd=dest) + + def obtain(self, dest): + url, rev = self.get_url_rev() + if rev: + rev_options = [rev] + rev_display = ' (to revision %s)' % rev + else: + rev_options = [] + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.notify('Cloning hg %s%s to %s' + % (url, rev_display, display_path(dest))) + call_subprocess([self.cmd, 'clone', '--noupdate', '-q', url, dest]) + call_subprocess([self.cmd, 'update', '-q'] + rev_options, cwd=dest) + + def get_url(self, location): + url = call_subprocess( + [self.cmd, 'showconfig', 'paths.default'], + show_stdout=False, cwd=location).strip() + if self._is_local_repository(url): + url = path_to_url2(url) + return url.strip() + + def get_tag_revs(self, location): + tags = call_subprocess( + [self.cmd, 'tags'], show_stdout=False, cwd=location) + tag_revs = [] + for line in tags.splitlines(): + tags_match = re.search(r'([\w\d\.-]+)\s*([\d]+):.*$', line) + if tags_match: + tag = tags_match.group(1) + rev = tags_match.group(2) + tag_revs.append((rev.strip(), tag.strip())) + return dict(tag_revs) + + def get_branch_revs(self, location): + branches = call_subprocess( + [self.cmd, 'branches'], show_stdout=False, cwd=location) + branch_revs = [] + for line in branches.splitlines(): + branches_match = re.search(r'([\w\d\.-]+)\s*([\d]+):.*$', line) + if branches_match: + branch = branches_match.group(1) + rev = branches_match.group(2) + branch_revs.append((rev.strip(), branch.strip())) + return dict(branch_revs) + + def get_revision(self, location): + current_revision = call_subprocess( + [self.cmd, 'parents', '--template={rev}'], + show_stdout=False, cwd=location).strip() + return current_revision + + def get_revision_hash(self, location): + current_rev_hash = call_subprocess( + [self.cmd, 'parents', '--template={node}'], + show_stdout=False, cwd=location).strip() + return current_rev_hash + + def get_src_requirement(self, dist, location, find_tags): + repo = self.get_url(location) + if not repo.lower().startswith('hg:'): + repo = 'hg+' + repo + egg_project_name = dist.egg_name().split('-', 1)[0] + if not repo: + return None + current_rev = self.get_revision(location) + current_rev_hash = self.get_revision_hash(location) + tag_revs = self.get_tag_revs(location) + branch_revs = self.get_branch_revs(location) + if current_rev in tag_revs: + # It's a tag + full_egg_name = '%s-%s' % (egg_project_name, tag_revs[current_rev]) + elif current_rev in branch_revs: + # It's the tip of a branch + full_egg_name = '%s-%s' % (dist.egg_name(), branch_revs[current_rev]) + else: + full_egg_name = '%s-dev' % dist.egg_name() + return '%s@%s#egg=%s' % (repo, current_rev_hash, full_egg_name) + +vcs.register(Mercurial) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/subversion.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/subversion.py new file mode 100644 index 000000000..85715d97a --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/subversion.py @@ -0,0 +1,260 @@ +import os +import re +from pip import call_subprocess +from pip.index import Link +from pip.util import rmtree, display_path +from pip.log import logger +from pip.vcs import vcs, VersionControl + +_svn_xml_url_re = re.compile('url="([^"]+)"') +_svn_rev_re = re.compile('committed-rev="(\d+)"') +_svn_url_re = re.compile(r'URL: (.+)') +_svn_revision_re = re.compile(r'Revision: (.+)') + + +class Subversion(VersionControl): + name = 'svn' + dirname = '.svn' + repo_name = 'checkout' + schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https') + bundle_file = 'svn-checkout.txt' + guide = ('# This was an svn checkout; to make it a checkout again run:\n' + 'svn checkout --force -r %(rev)s %(url)s .\n') + + def get_info(self, location): + """Returns (url, revision), where both are strings""" + assert not location.rstrip('/').endswith(self.dirname), 'Bad directory: %s' % location + output = call_subprocess( + [self.cmd, 'info', location], show_stdout=False, extra_environ={'LANG': 'C'}) + match = _svn_url_re.search(output) + if not match: + logger.warn('Cannot determine URL of svn checkout %s' % display_path(location)) + logger.info('Output that cannot be parsed: \n%s' % output) + return None, None + url = match.group(1).strip() + match = _svn_revision_re.search(output) + if not match: + logger.warn('Cannot determine revision of svn checkout %s' % display_path(location)) + logger.info('Output that cannot be parsed: \n%s' % output) + return url, None + return url, match.group(1) + + def parse_vcs_bundle_file(self, content): + for line in content.splitlines(): + if not line.strip() or line.strip().startswith('#'): + continue + match = re.search(r'^-r\s*([^ ])?', line) + if not match: + return None, None + rev = match.group(1) + rest = line[match.end():].strip().split(None, 1)[0] + return rest, rev + return None, None + + def unpack(self, location): + """Check out the svn repository at the url to the destination location""" + url, rev = self.get_url_rev() + logger.notify('Checking out svn repository %s to %s' % (url, location)) + logger.indent += 2 + try: + if os.path.exists(location): + # Subversion doesn't like to check out over an existing directory + # --force fixes this, but was only added in svn 1.5 + rmtree(location) + call_subprocess( + [self.cmd, 'checkout', url, location], + filter_stdout=self._filter, show_stdout=False) + finally: + logger.indent -= 2 + + def export(self, location): + """Export the svn repository at the url to the destination location""" + url, rev = self.get_url_rev() + logger.notify('Exporting svn repository %s to %s' % (url, location)) + logger.indent += 2 + try: + if os.path.exists(location): + # Subversion doesn't like to check out over an existing directory + # --force fixes this, but was only added in svn 1.5 + rmtree(location) + call_subprocess( + [self.cmd, 'export', url, location], + filter_stdout=self._filter, show_stdout=False) + finally: + logger.indent -= 2 + + def switch(self, dest, url, rev_options): + call_subprocess( + [self.cmd, 'switch'] + rev_options + [url, dest]) + + def update(self, dest, rev_options): + call_subprocess( + [self.cmd, 'update'] + rev_options + [dest]) + + def obtain(self, dest): + url, rev = self.get_url_rev() + if rev: + rev_options = ['-r', rev] + rev_display = ' (to revision %s)' % rev + else: + rev_options = [] + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.notify('Checking out %s%s to %s' + % (url, rev_display, display_path(dest))) + call_subprocess( + [self.cmd, 'checkout', '-q'] + rev_options + [url, dest]) + + def get_location(self, dist, dependency_links): + for url in dependency_links: + egg_fragment = Link(url).egg_fragment + if not egg_fragment: + continue + if '-' in egg_fragment: + ## FIXME: will this work when a package has - in the name? + key = '-'.join(egg_fragment.split('-')[:-1]).lower() + else: + key = egg_fragment + if key == dist.key: + return url.split('#', 1)[0] + return None + + def get_revision(self, location): + """ + Return the maximum revision for all files under a given location + """ + # Note: taken from setuptools.command.egg_info + revision = 0 + + for base, dirs, files in os.walk(location): + if self.dirname not in dirs: + dirs[:] = [] + continue # no sense walking uncontrolled subdirs + dirs.remove(self.dirname) + entries_fn = os.path.join(base, self.dirname, 'entries') + if not os.path.exists(entries_fn): + ## FIXME: should we warn? + continue + f = open(entries_fn) + data = f.read() + f.close() + + if data.startswith('8') or data.startswith('9') or data.startswith('10'): + data = map(str.splitlines, data.split('\n\x0c\n')) + del data[0][0] # get rid of the '8' + dirurl = data[0][3] + revs = [int(d[9]) for d in data if len(d)>9 and d[9]]+[0] + if revs: + localrev = max(revs) + else: + localrev = 0 + elif data.startswith('<?xml'): + dirurl = _svn_xml_url_re.search(data).group(1) # get repository URL + revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)]+[0] + if revs: + localrev = max(revs) + else: + localrev = 0 + else: + logger.warn("Unrecognized .svn/entries format; skipping %s", base) + dirs[:] = [] + continue + if base == location: + base_url = dirurl+'/' # save the root url + elif not dirurl.startswith(base_url): + dirs[:] = [] + continue # not part of the same svn tree, skip it + revision = max(revision, localrev) + return revision + + def get_url_rev(self): + # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it + url, rev = super(Subversion, self).get_url_rev() + if url.startswith('ssh://'): + url = 'svn+' + url + return url, rev + + def get_url(self, location): + # In cases where the source is in a subdirectory, not alongside setup.py + # we have to look up in the location until we find a real setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without finding setup.py + logger.warn("Could not find setup.py for directory %s (tried all parent directories)" + % orig_location) + return None + f = open(os.path.join(location, self.dirname, 'entries')) + data = f.read() + f.close() + if data.startswith('8') or data.startswith('9') or data.startswith('10'): + data = map(str.splitlines, data.split('\n\x0c\n')) + del data[0][0] # get rid of the '8' + return data[0][3] + elif data.startswith('<?xml'): + match = _svn_xml_url_re.search(data) + if not match: + raise ValueError('Badly formatted data: %r' % data) + return match.group(1) # get repository URL + else: + logger.warn("Unrecognized .svn/entries format in %s" % location) + # Or raise exception? + return None + + def get_tag_revs(self, svn_tag_url): + stdout = call_subprocess( + [self.cmd, 'ls', '-v', svn_tag_url], show_stdout=False) + results = [] + for line in stdout.splitlines(): + parts = line.split() + rev = int(parts[0]) + tag = parts[-1].strip('/') + results.append((tag, rev)) + return results + + def find_tag_match(self, rev, tag_revs): + best_match_rev = None + best_tag = None + for tag, tag_rev in tag_revs: + if (tag_rev > rev and + (best_match_rev is None or best_match_rev > tag_rev)): + # FIXME: Is best_match > tag_rev really possible? + # or is it a sign something is wacky? + best_match_rev = tag_rev + best_tag = tag + return best_tag + + def get_src_requirement(self, dist, location, find_tags=False): + repo = self.get_url(location) + if repo is None: + return None + parts = repo.split('/') + ## FIXME: why not project name? + egg_project_name = dist.egg_name().split('-', 1)[0] + rev = self.get_revision(location) + if parts[-2] in ('tags', 'tag'): + # It's a tag, perfect! + full_egg_name = '%s-%s' % (egg_project_name, parts[-1]) + elif parts[-2] in ('branches', 'branch'): + # It's a branch :( + full_egg_name = '%s-%s-r%s' % (dist.egg_name(), parts[-1], rev) + elif parts[-1] == 'trunk': + # Trunk :-/ + full_egg_name = '%s-dev_r%s' % (dist.egg_name(), rev) + if find_tags: + tag_url = '/'.join(parts[:-1]) + '/tags' + tag_revs = self.get_tag_revs(tag_url) + match = self.find_tag_match(rev, tag_revs) + if match: + logger.notify('trunk checkout %s seems to be equivalent to tag %s' % match) + repo = '%s/%s' % (tag_url, match) + full_egg_name = '%s-%s' % (egg_project_name, match) + else: + # Don't know what it is + logger.warn('svn URL does not fit normal structure (tags/branches/trunk): %s' % repo) + full_egg_name = '%s-dev_r%s' % (egg_project_name, rev) + return 'svn+%s@%s#egg=%s' % (repo, rev, full_egg_name) + +vcs.register(Subversion) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/venv.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/venv.py new file mode 100644 index 000000000..708abb05b --- /dev/null +++ b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/venv.py @@ -0,0 +1,53 @@ +"""Tools for working with virtualenv environments""" + +import os +import sys +import subprocess +from pip.exceptions import BadCommand +from pip.log import logger + + +def restart_in_venv(venv, base, site_packages, args): + """ + Restart this script using the interpreter in the given virtual environment + """ + if base and not os.path.isabs(venv) and not venv.startswith('~'): + base = os.path.expanduser(base) + # ensure we have an abs basepath at this point: + # a relative one makes no sense (or does it?) + if os.path.isabs(base): + venv = os.path.join(base, venv) + + if venv.startswith('~'): + venv = os.path.expanduser(venv) + + if not os.path.exists(venv): + try: + import virtualenv + except ImportError: + print 'The virtual environment does not exist: %s' % venv + print 'and virtualenv is not installed, so a new environment cannot be created' + sys.exit(3) + print 'Creating new virtualenv environment in %s' % venv + virtualenv.logger = logger + logger.indent += 2 + virtualenv.create_environment(venv, site_packages=site_packages) + if sys.platform == 'win32': + python = os.path.join(venv, 'Scripts', 'python.exe') + # check for bin directory which is used in buildouts + if not os.path.exists(python): + python = os.path.join(venv, 'bin', 'python.exe') + else: + python = os.path.join(venv, 'bin', 'python') + if not os.path.exists(python): + python = venv + if not os.path.exists(python): + raise BadCommand('Cannot find virtual environment interpreter at %s' % python) + base = os.path.dirname(os.path.dirname(python)) + file = os.path.join(os.path.dirname(__file__), 'runner.py') + if file.endswith('.pyc'): + file = file[:-1] + proc = subprocess.Popen( + [python, file] + args + [base, '___VENV_RESTART___']) + proc.wait() + sys.exit(proc.returncode) diff --git a/test/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg b/test/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg new file mode 100644 index 000000000..3c72d15b5 Binary files /dev/null and b/test/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg differ diff --git a/test/lib/python2.6/site-packages/setuptools.pth b/test/lib/python2.6/site-packages/setuptools.pth new file mode 100644 index 000000000..bf7602056 --- /dev/null +++ b/test/lib/python2.6/site-packages/setuptools.pth @@ -0,0 +1 @@ +./setuptools-0.6c11-py2.6.egg diff --git a/test/lib/python2.6/site.py b/test/lib/python2.6/site.py new file mode 100644 index 000000000..a49cfc349 --- /dev/null +++ b/test/lib/python2.6/site.py @@ -0,0 +1,713 @@ +"""Append module search paths for third-party packages to sys.path. + +**************************************************************** +* This module is automatically imported during initialization. * +**************************************************************** + +In earlier versions of Python (up to 1.5a3), scripts or modules that +needed to use site-specific modules would place ``import site'' +somewhere near the top of their code. Because of the automatic +import, this is no longer necessary (but code that does it still +works). + +This will append site-specific paths to the module search path. On +Unix, it starts with sys.prefix and sys.exec_prefix (if different) and +appends lib/python<version>/site-packages as well as lib/site-python. +It also supports the Debian convention of +lib/python<version>/dist-packages. On other platforms (mainly Mac and +Windows), it uses just sys.prefix (and sys.exec_prefix, if different, +but this is unlikely). The resulting directories, if they exist, are +appended to sys.path, and also inspected for path configuration files. + +FOR DEBIAN, this sys.path is augmented with directories in /usr/local. +Local addons go into /usr/local/lib/python<version>/site-packages +(resp. /usr/local/lib/site-python), Debian addons install into +/usr/{lib,share}/python<version>/dist-packages. + +A path configuration file is a file whose name has the form +<package>.pth; its contents are additional directories (one per line) +to be added to sys.path. Non-existing directories (or +non-directories) are never added to sys.path; no directory is added to +sys.path more than once. Blank lines and lines beginning with +'#' are skipped. Lines starting with 'import' are executed. + +For example, suppose sys.prefix and sys.exec_prefix are set to +/usr/local and there is a directory /usr/local/lib/python2.X/site-packages +with three subdirectories, foo, bar and spam, and two path +configuration files, foo.pth and bar.pth. Assume foo.pth contains the +following: + + # foo package configuration + foo + bar + bletch + +and bar.pth contains: + + # bar package configuration + bar + +Then the following directories are added to sys.path, in this order: + + /usr/local/lib/python2.X/site-packages/bar + /usr/local/lib/python2.X/site-packages/foo + +Note that bletch is omitted because it doesn't exist; bar precedes foo +because bar.pth comes alphabetically before foo.pth; and spam is +omitted because it is not mentioned in either path configuration file. + +After these path manipulations, an attempt is made to import a module +named sitecustomize, which can perform arbitrary additional +site-specific customizations. If this import fails with an +ImportError exception, it is silently ignored. + +""" + +import sys +import os +import __builtin__ +try: + set +except NameError: + from sets import Set as set + +# Prefixes for site-packages; add additional prefixes like /usr/local here +PREFIXES = [sys.prefix, sys.exec_prefix] +# Enable per user site-packages directory +# set it to False to disable the feature or True to force the feature +ENABLE_USER_SITE = None +# for distutils.commands.install +USER_SITE = None +USER_BASE = None + +_is_pypy = hasattr(sys, 'pypy_version_info') +_is_jython = sys.platform[:4] == 'java' +if _is_jython: + ModuleType = type(os) + +def makepath(*paths): + dir = os.path.join(*paths) + if _is_jython and (dir == '__classpath__' or + dir.startswith('__pyclasspath__')): + return dir, dir + dir = os.path.abspath(dir) + return dir, os.path.normcase(dir) + +def abs__file__(): + """Set all module' __file__ attribute to an absolute path""" + for m in sys.modules.values(): + if ((_is_jython and not isinstance(m, ModuleType)) or + hasattr(m, '__loader__')): + # only modules need the abspath in Jython. and don't mess + # with a PEP 302-supplied __file__ + continue + f = getattr(m, '__file__', None) + if f is None: + continue + m.__file__ = os.path.abspath(f) + +def removeduppaths(): + """ Remove duplicate entries from sys.path along with making them + absolute""" + # This ensures that the initial path provided by the interpreter contains + # only absolute pathnames, even if we're running from the build directory. + L = [] + known_paths = set() + for dir in sys.path: + # Filter out duplicate paths (on case-insensitive file systems also + # if they only differ in case); turn relative paths into absolute + # paths. + dir, dircase = makepath(dir) + if not dircase in known_paths: + L.append(dir) + known_paths.add(dircase) + sys.path[:] = L + return known_paths + +# XXX This should not be part of site.py, since it is needed even when +# using the -S option for Python. See http://www.python.org/sf/586680 +def addbuilddir(): + """Append ./build/lib.<platform> in case we're running in the build dir + (especially for Guido :-)""" + from distutils.util import get_platform + s = "build/lib.%s-%.3s" % (get_platform(), sys.version) + if hasattr(sys, 'gettotalrefcount'): + s += '-pydebug' + s = os.path.join(os.path.dirname(sys.path[-1]), s) + sys.path.append(s) + +def _init_pathinfo(): + """Return a set containing all existing directory entries from sys.path""" + d = set() + for dir in sys.path: + try: + if os.path.isdir(dir): + dir, dircase = makepath(dir) + d.add(dircase) + except TypeError: + continue + return d + +def addpackage(sitedir, name, known_paths): + """Add a new path to known_paths by combining sitedir and 'name' or execute + sitedir if it starts with 'import'""" + if known_paths is None: + _init_pathinfo() + reset = 1 + else: + reset = 0 + fullname = os.path.join(sitedir, name) + try: + f = open(fullname, "rU") + except IOError: + return + try: + for line in f: + if line.startswith("#"): + continue + if line.startswith("import"): + exec line + continue + line = line.rstrip() + dir, dircase = makepath(sitedir, line) + if not dircase in known_paths and os.path.exists(dir): + sys.path.append(dir) + known_paths.add(dircase) + finally: + f.close() + if reset: + known_paths = None + return known_paths + +def addsitedir(sitedir, known_paths=None): + """Add 'sitedir' argument to sys.path if missing and handle .pth files in + 'sitedir'""" + if known_paths is None: + known_paths = _init_pathinfo() + reset = 1 + else: + reset = 0 + sitedir, sitedircase = makepath(sitedir) + if not sitedircase in known_paths: + sys.path.append(sitedir) # Add path component + try: + names = os.listdir(sitedir) + except os.error: + return + names.sort() + for name in names: + if name.endswith(os.extsep + "pth"): + addpackage(sitedir, name, known_paths) + if reset: + known_paths = None + return known_paths + +def addsitepackages(known_paths, sys_prefix=sys.prefix, exec_prefix=sys.exec_prefix): + """Add site-packages (and possibly site-python) to sys.path""" + prefixes = [os.path.join(sys_prefix, "local"), sys_prefix] + if exec_prefix != sys_prefix: + prefixes.append(os.path.join(exec_prefix, "local")) + + for prefix in prefixes: + if prefix: + if sys.platform in ('os2emx', 'riscos') or _is_jython: + sitedirs = [os.path.join(prefix, "Lib", "site-packages")] + elif _is_pypy: + sitedirs = [os.path.join(prefix, 'site-packages')] + elif sys.platform == 'darwin' and prefix == sys_prefix: + + if prefix.startswith("/System/Library/Frameworks/"): # Apple's Python + + sitedirs = [os.path.join("/Library/Python", sys.version[:3], "site-packages"), + os.path.join(prefix, "Extras", "lib", "python")] + + else: # any other Python distros on OSX work this way + sitedirs = [os.path.join(prefix, "lib", + "python" + sys.version[:3], "site-packages")] + + elif os.sep == '/': + sitedirs = [os.path.join(prefix, + "lib", + "python" + sys.version[:3], + "site-packages"), + os.path.join(prefix, "lib", "site-python"), + os.path.join(prefix, "python" + sys.version[:3], "lib-dynload")] + lib64_dir = os.path.join(prefix, "lib64", "python" + sys.version[:3], "site-packages") + if (os.path.exists(lib64_dir) and + os.path.realpath(lib64_dir) not in [os.path.realpath(p) for p in sitedirs]): + sitedirs.append(lib64_dir) + try: + # sys.getobjects only available in --with-pydebug build + sys.getobjects + sitedirs.insert(0, os.path.join(sitedirs[0], 'debug')) + except AttributeError: + pass + # Debian-specific dist-packages directories: + sitedirs.append(os.path.join(prefix, "lib", + "python" + sys.version[:3], + "dist-packages")) + sitedirs.append(os.path.join(prefix, "local/lib", + "python" + sys.version[:3], + "dist-packages")) + sitedirs.append(os.path.join(prefix, "lib", "dist-python")) + else: + sitedirs = [prefix, os.path.join(prefix, "lib", "site-packages")] + if sys.platform == 'darwin': + # for framework builds *only* we add the standard Apple + # locations. Currently only per-user, but /Library and + # /Network/Library could be added too + if 'Python.framework' in prefix: + home = os.environ.get('HOME') + if home: + sitedirs.append( + os.path.join(home, + 'Library', + 'Python', + sys.version[:3], + 'site-packages')) + for sitedir in sitedirs: + if os.path.isdir(sitedir): + addsitedir(sitedir, known_paths) + return None + +def check_enableusersite(): + """Check if user site directory is safe for inclusion + + The function tests for the command line flag (including environment var), + process uid/gid equal to effective uid/gid. + + None: Disabled for security reasons + False: Disabled by user (command line option) + True: Safe and enabled + """ + if hasattr(sys, 'flags') and getattr(sys.flags, 'no_user_site', False): + return False + + if hasattr(os, "getuid") and hasattr(os, "geteuid"): + # check process uid == effective uid + if os.geteuid() != os.getuid(): + return None + if hasattr(os, "getgid") and hasattr(os, "getegid"): + # check process gid == effective gid + if os.getegid() != os.getgid(): + return None + + return True + +def addusersitepackages(known_paths): + """Add a per user site-package to sys.path + + Each user has its own python directory with site-packages in the + home directory. + + USER_BASE is the root directory for all Python versions + + USER_SITE is the user specific site-packages directory + + USER_SITE/.. can be used for data. + """ + global USER_BASE, USER_SITE, ENABLE_USER_SITE + env_base = os.environ.get("PYTHONUSERBASE", None) + + def joinuser(*args): + return os.path.expanduser(os.path.join(*args)) + + #if sys.platform in ('os2emx', 'riscos'): + # # Don't know what to put here + # USER_BASE = '' + # USER_SITE = '' + if os.name == "nt": + base = os.environ.get("APPDATA") or "~" + if env_base: + USER_BASE = env_base + else: + USER_BASE = joinuser(base, "Python") + USER_SITE = os.path.join(USER_BASE, + "Python" + sys.version[0] + sys.version[2], + "site-packages") + else: + if env_base: + USER_BASE = env_base + else: + USER_BASE = joinuser("~", ".local") + USER_SITE = os.path.join(USER_BASE, "lib", + "python" + sys.version[:3], + "site-packages") + + if ENABLE_USER_SITE and os.path.isdir(USER_SITE): + addsitedir(USER_SITE, known_paths) + if ENABLE_USER_SITE: + for dist_libdir in ("lib", "local/lib"): + user_site = os.path.join(USER_BASE, dist_libdir, + "python" + sys.version[:3], + "dist-packages") + if os.path.isdir(user_site): + addsitedir(user_site, known_paths) + return known_paths + + + +def setBEGINLIBPATH(): + """The OS/2 EMX port has optional extension modules that do double duty + as DLLs (and must use the .DLL file extension) for other extensions. + The library search path needs to be amended so these will be found + during module import. Use BEGINLIBPATH so that these are at the start + of the library search path. + + """ + dllpath = os.path.join(sys.prefix, "Lib", "lib-dynload") + libpath = os.environ['BEGINLIBPATH'].split(';') + if libpath[-1]: + libpath.append(dllpath) + else: + libpath[-1] = dllpath + os.environ['BEGINLIBPATH'] = ';'.join(libpath) + + +def setquit(): + """Define new built-ins 'quit' and 'exit'. + These are simply strings that display a hint on how to exit. + + """ + if os.sep == ':': + eof = 'Cmd-Q' + elif os.sep == '\\': + eof = 'Ctrl-Z plus Return' + else: + eof = 'Ctrl-D (i.e. EOF)' + + class Quitter(object): + def __init__(self, name): + self.name = name + def __repr__(self): + return 'Use %s() or %s to exit' % (self.name, eof) + def __call__(self, code=None): + # Shells like IDLE catch the SystemExit, but listen when their + # stdin wrapper is closed. + try: + sys.stdin.close() + except: + pass + raise SystemExit(code) + __builtin__.quit = Quitter('quit') + __builtin__.exit = Quitter('exit') + + +class _Printer(object): + """interactive prompt objects for printing the license text, a list of + contributors and the copyright notice.""" + + MAXLINES = 23 + + def __init__(self, name, data, files=(), dirs=()): + self.__name = name + self.__data = data + self.__files = files + self.__dirs = dirs + self.__lines = None + + def __setup(self): + if self.__lines: + return + data = None + for dir in self.__dirs: + for filename in self.__files: + filename = os.path.join(dir, filename) + try: + fp = file(filename, "rU") + data = fp.read() + fp.close() + break + except IOError: + pass + if data: + break + if not data: + data = self.__data + self.__lines = data.split('\n') + self.__linecnt = len(self.__lines) + + def __repr__(self): + self.__setup() + if len(self.__lines) <= self.MAXLINES: + return "\n".join(self.__lines) + else: + return "Type %s() to see the full %s text" % ((self.__name,)*2) + + def __call__(self): + self.__setup() + prompt = 'Hit Return for more, or q (and Return) to quit: ' + lineno = 0 + while 1: + try: + for i in range(lineno, lineno + self.MAXLINES): + print self.__lines[i] + except IndexError: + break + else: + lineno += self.MAXLINES + key = None + while key is None: + key = raw_input(prompt) + if key not in ('', 'q'): + key = None + if key == 'q': + break + +def setcopyright(): + """Set 'copyright' and 'credits' in __builtin__""" + __builtin__.copyright = _Printer("copyright", sys.copyright) + if _is_jython: + __builtin__.credits = _Printer( + "credits", + "Jython is maintained by the Jython developers (www.jython.org).") + elif _is_pypy: + __builtin__.credits = _Printer( + "credits", + "PyPy is maintained by the PyPy developers: http://codespeak.net/pypy") + else: + __builtin__.credits = _Printer("credits", """\ + Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands + for supporting Python development. See www.python.org for more information.""") + here = os.path.dirname(os.__file__) + __builtin__.license = _Printer( + "license", "See http://www.python.org/%.3s/license.html" % sys.version, + ["LICENSE.txt", "LICENSE"], + [os.path.join(here, os.pardir), here, os.curdir]) + + +class _Helper(object): + """Define the built-in 'help'. + This is a wrapper around pydoc.help (with a twist). + + """ + + def __repr__(self): + return "Type help() for interactive help, " \ + "or help(object) for help about object." + def __call__(self, *args, **kwds): + import pydoc + return pydoc.help(*args, **kwds) + +def sethelper(): + __builtin__.help = _Helper() + +def aliasmbcs(): + """On Windows, some default encodings are not provided by Python, + while they are always available as "mbcs" in each locale. Make + them usable by aliasing to "mbcs" in such a case.""" + if sys.platform == 'win32': + import locale, codecs + enc = locale.getdefaultlocale()[1] + if enc.startswith('cp'): # "cp***" ? + try: + codecs.lookup(enc) + except LookupError: + import encodings + encodings._cache[enc] = encodings._unknown + encodings.aliases.aliases[enc] = 'mbcs' + +def setencoding(): + """Set the string encoding used by the Unicode implementation. The + default is 'ascii', but if you're willing to experiment, you can + change this.""" + encoding = "ascii" # Default value set by _PyUnicode_Init() + if 0: + # Enable to support locale aware default string encodings. + import locale + loc = locale.getdefaultlocale() + if loc[1]: + encoding = loc[1] + if 0: + # Enable to switch off string to Unicode coercion and implicit + # Unicode to string conversion. + encoding = "undefined" + if encoding != "ascii": + # On Non-Unicode builds this will raise an AttributeError... + sys.setdefaultencoding(encoding) # Needs Python Unicode build ! + + +def execsitecustomize(): + """Run custom site specific code, if available.""" + try: + import sitecustomize + except ImportError: + pass + +def virtual_install_main_packages(): + f = open(os.path.join(os.path.dirname(__file__), 'orig-prefix.txt')) + sys.real_prefix = f.read().strip() + f.close() + pos = 2 + if sys.path[0] == '': + pos += 1 + if sys.platform == 'win32': + paths = [os.path.join(sys.real_prefix, 'Lib'), os.path.join(sys.real_prefix, 'DLLs')] + elif _is_jython: + paths = [os.path.join(sys.real_prefix, 'Lib')] + elif _is_pypy: + cpyver = '%d.%d.%d' % sys.version_info[:3] + paths = [os.path.join(sys.real_prefix, 'lib_pypy'), + os.path.join(sys.real_prefix, 'lib-python', 'modified-%s' % cpyver), + os.path.join(sys.real_prefix, 'lib-python', cpyver)] + else: + paths = [os.path.join(sys.real_prefix, 'lib', 'python'+sys.version[:3])] + lib64_path = os.path.join(sys.real_prefix, 'lib64', 'python'+sys.version[:3]) + if os.path.exists(lib64_path): + paths.append(lib64_path) + # This is hardcoded in the Python executable, but relative to sys.prefix: + plat_path = os.path.join(sys.real_prefix, 'lib', 'python'+sys.version[:3], + 'plat-%s' % sys.platform) + if os.path.exists(plat_path): + paths.append(plat_path) + # This is hardcoded in the Python executable, but + # relative to sys.prefix, so we have to fix up: + for path in list(paths): + tk_dir = os.path.join(path, 'lib-tk') + if os.path.exists(tk_dir): + paths.append(tk_dir) + + # These are hardcoded in the Apple's Python executable, + # but relative to sys.prefix, so we have to fix them up: + if sys.platform == 'darwin': + hardcoded_paths = [os.path.join(sys.real_prefix, 'lib', 'python'+sys.version[:3], module) + for module in ('plat-darwin', 'plat-mac', 'plat-mac/lib-scriptpackages')] + + for path in hardcoded_paths: + if os.path.exists(path): + paths.append(path) + + sys.path.extend(paths) + +def force_global_eggs_after_local_site_packages(): + """ + Force easy_installed eggs in the global environment to get placed + in sys.path after all packages inside the virtualenv. This + maintains the "least surprise" result that packages in the + virtualenv always mask global packages, never the other way + around. + + """ + egginsert = getattr(sys, '__egginsert', 0) + for i, path in enumerate(sys.path): + if i > egginsert and path.startswith(sys.prefix): + egginsert = i + sys.__egginsert = egginsert + 1 + +def virtual_addsitepackages(known_paths): + force_global_eggs_after_local_site_packages() + return addsitepackages(known_paths, sys_prefix=sys.real_prefix) + +def fixclasspath(): + """Adjust the special classpath sys.path entries for Jython. These + entries should follow the base virtualenv lib directories. + """ + paths = [] + classpaths = [] + for path in sys.path: + if path == '__classpath__' or path.startswith('__pyclasspath__'): + classpaths.append(path) + else: + paths.append(path) + sys.path = paths + sys.path.extend(classpaths) + +def execusercustomize(): + """Run custom user specific code, if available.""" + try: + import usercustomize + except ImportError: + pass + + +def main(): + global ENABLE_USER_SITE + virtual_install_main_packages() + abs__file__() + paths_in_sys = removeduppaths() + if (os.name == "posix" and sys.path and + os.path.basename(sys.path[-1]) == "Modules"): + addbuilddir() + if _is_jython: + fixclasspath() + GLOBAL_SITE_PACKAGES = not os.path.exists(os.path.join(os.path.dirname(__file__), 'no-global-site-packages.txt')) + if not GLOBAL_SITE_PACKAGES: + ENABLE_USER_SITE = False + if ENABLE_USER_SITE is None: + ENABLE_USER_SITE = check_enableusersite() + paths_in_sys = addsitepackages(paths_in_sys) + paths_in_sys = addusersitepackages(paths_in_sys) + if GLOBAL_SITE_PACKAGES: + paths_in_sys = virtual_addsitepackages(paths_in_sys) + if sys.platform == 'os2emx': + setBEGINLIBPATH() + setquit() + setcopyright() + sethelper() + aliasmbcs() + setencoding() + execsitecustomize() + if ENABLE_USER_SITE: + execusercustomize() + # Remove sys.setdefaultencoding() so that users cannot change the + # encoding after initialization. The test for presence is needed when + # this module is run as a script, because this code is executed twice. + if hasattr(sys, "setdefaultencoding"): + del sys.setdefaultencoding + +main() + +def _script(): + help = """\ + %s [--user-base] [--user-site] + + Without arguments print some useful information + With arguments print the value of USER_BASE and/or USER_SITE separated + by '%s'. + + Exit codes with --user-base or --user-site: + 0 - user site directory is enabled + 1 - user site directory is disabled by user + 2 - uses site directory is disabled by super user + or for security reasons + >2 - unknown error + """ + args = sys.argv[1:] + if not args: + print "sys.path = [" + for dir in sys.path: + print " %r," % (dir,) + print "]" + def exists(path): + if os.path.isdir(path): + return "exists" + else: + return "doesn't exist" + print "USER_BASE: %r (%s)" % (USER_BASE, exists(USER_BASE)) + print "USER_SITE: %r (%s)" % (USER_SITE, exists(USER_BASE)) + print "ENABLE_USER_SITE: %r" % ENABLE_USER_SITE + sys.exit(0) + + buffer = [] + if '--user-base' in args: + buffer.append(USER_BASE) + if '--user-site' in args: + buffer.append(USER_SITE) + + if buffer: + print os.pathsep.join(buffer) + if ENABLE_USER_SITE: + sys.exit(0) + elif ENABLE_USER_SITE is False: + sys.exit(1) + elif ENABLE_USER_SITE is None: + sys.exit(2) + else: + sys.exit(3) + else: + import textwrap + print textwrap.dedent(help % (sys.argv[0], os.pathsep)) + sys.exit(10) + +if __name__ == '__main__': + _script() diff --git a/test/lib/python2.6/sre.py b/test/lib/python2.6/sre.py new file mode 120000 index 000000000..3ade4e80e --- /dev/null +++ b/test/lib/python2.6/sre.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/sre.py \ No newline at end of file diff --git a/test/lib/python2.6/sre_compile.py b/test/lib/python2.6/sre_compile.py new file mode 120000 index 000000000..fc13a37d5 --- /dev/null +++ b/test/lib/python2.6/sre_compile.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/sre_compile.py \ No newline at end of file diff --git a/test/lib/python2.6/sre_constants.py b/test/lib/python2.6/sre_constants.py new file mode 120000 index 000000000..98b0d5543 --- /dev/null +++ b/test/lib/python2.6/sre_constants.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/sre_constants.py \ No newline at end of file diff --git a/test/lib/python2.6/sre_parse.py b/test/lib/python2.6/sre_parse.py new file mode 120000 index 000000000..76b24c687 --- /dev/null +++ b/test/lib/python2.6/sre_parse.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/sre_parse.py \ No newline at end of file diff --git a/test/lib/python2.6/stat.py b/test/lib/python2.6/stat.py new file mode 120000 index 000000000..68d61e662 --- /dev/null +++ b/test/lib/python2.6/stat.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/stat.py \ No newline at end of file diff --git a/test/lib/python2.6/types.py b/test/lib/python2.6/types.py new file mode 120000 index 000000000..72b00ba35 --- /dev/null +++ b/test/lib/python2.6/types.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/types.py \ No newline at end of file diff --git a/test/lib/python2.6/warnings.py b/test/lib/python2.6/warnings.py new file mode 120000 index 000000000..1af4e2833 --- /dev/null +++ b/test/lib/python2.6/warnings.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/warnings.py \ No newline at end of file -- cgit From ec7ab22b8b08820f7ffa3b05e40b09a925f1ef95 Mon Sep 17 00:00:00 2001 From: Anne Gentle <anne@openstack.org> Date: Mon, 21 Feb 2011 14:35:30 -0600 Subject: Removing pesky DS_Store files too. Begone. --- .bzrignore | 1 + doc/build/.DS_Store | Bin 6148 -> 0 bytes doc/build/html/.DS_Store | Bin 6148 -> 0 bytes 3 files changed, 1 insertion(+) delete mode 100644 doc/build/.DS_Store delete mode 100644 doc/build/html/.DS_Store diff --git a/.bzrignore b/.bzrignore index b271561a3..d22b62629 100644 --- a/.bzrignore +++ b/.bzrignore @@ -13,3 +13,4 @@ CA/serial* CA/newcerts/*.pem CA/private/cakey.pem nova/vcsversion.py +*.DS_Store diff --git a/doc/build/.DS_Store b/doc/build/.DS_Store deleted file mode 100644 index 5008ddfcf..000000000 Binary files a/doc/build/.DS_Store and /dev/null differ diff --git a/doc/build/html/.DS_Store b/doc/build/html/.DS_Store deleted file mode 100644 index 5008ddfcf..000000000 Binary files a/doc/build/html/.DS_Store and /dev/null differ -- cgit From 02e196192ea1f8be22c31828266b177d14d123cd Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Mon, 21 Feb 2011 12:41:15 -0800 Subject: make sure that ec2 response times are xs:dateTime parsable --- nova/api/ec2/apirequest.py | 8 +++++++- nova/tests/test_api.py | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py index 00b527d62..2b1acba5a 100644 --- a/nova/api/ec2/apirequest.py +++ b/nova/api/ec2/apirequest.py @@ -46,6 +46,11 @@ def _underscore_to_xmlcase(str): return res[:1].lower() + res[1:] +def _database_to_isoformat(datetimeobj): + """Return a xs:dateTime parsable string from datatime""" + return datetimeobj.strftime("%Y-%m-%dT%H:%M:%SZ") + + def _try_convert(value): """Return a non-string if possible""" if value == 'None': @@ -173,7 +178,8 @@ class APIRequest(object): elif isinstance(data, bool): data_el.appendChild(xml.createTextNode(str(data).lower())) elif isinstance(data, datetime.datetime): - data_el.appendChild(xml.createTextNode(data.isoformat())) + data_el.appendChild( + xml.createTextNode(_database_to_isoformat(data))) elif data != None: data_el.appendChild(xml.createTextNode(str(data))) diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index fa27825cd..d5c54a1c3 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -20,6 +20,7 @@ import boto from boto.ec2 import regioninfo +import datetime import httplib import random import StringIO @@ -127,6 +128,28 @@ class ApiEc2TestCase(test.TestCase): self.ec2.new_http_connection(host, is_secure).AndReturn(self.http) return self.http + def test_return_valid_isoformat(self): + """ + Ensure that the ec2 api returns datetime in xs:dateTime + (which apparently isn't datetime.isoformat()) + NOTE(ken-pepple): https://bugs.launchpad.net/nova/+bug/721297 + """ + conv = apirequest._database_to_isoformat + # sqlite database representation with microseconds + time_to_convert = datetime.datetime.strptime( + "2011-02-21 20:14:10.634276", + "%Y-%m-%d %H:%M:%S.%f") + self.assertEqual( + conv(time_to_convert), + '2011-02-21T20:14:10Z') + # mysqlite database representation + time_to_convert = datetime.datetime.strptime( + "2011-02-21 19:56:18", + "%Y-%m-%d %H:%M:%S") + self.assertEqual( + conv(time_to_convert), + '2011-02-21T19:56:18Z') + def test_xmlns_version_matches_request_version(self): self.expect_http(api_version='2010-10-30') self.mox.ReplayAll() -- cgit From 29644fe5a9cf47ae33af31b848c0edc4567f3c09 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 21 Feb 2011 13:46:41 -0800 Subject: switch to explicit call to logging.setup() --- bin/nova-ajax-console-proxy | 2 +- bin/nova-api | 2 +- bin/nova-combined | 1 + bin/nova-compute | 5 ++++ bin/nova-console | 4 ++++ bin/nova-dhcpbridge | 3 ++- bin/nova-direct-api | 2 ++ bin/nova-import-canonical-imagestore | 2 ++ bin/nova-instancemonitor | 3 --- bin/nova-manage | 2 ++ bin/nova-network | 4 ++++ bin/nova-scheduler | 4 ++++ bin/nova-volume | 4 ++++ nova/log.py | 44 ++++++++++++++++++++---------------- nova/service.py | 2 -- nova/twistd.py | 1 + nova/utils.py | 2 +- run_tests.py | 2 ++ 18 files changed, 61 insertions(+), 28 deletions(-) diff --git a/bin/nova-ajax-console-proxy b/bin/nova-ajax-console-proxy index 2bc407658..392b328b1 100755 --- a/bin/nova-ajax-console-proxy +++ b/bin/nova-ajax-console-proxy @@ -25,7 +25,6 @@ from eventlet.green import urllib2 import exceptions import gettext -import logging import os import sys import time @@ -130,6 +129,7 @@ class AjaxConsoleProxy(object): if __name__ == '__main__': utils.default_flagfile() FLAGS(sys.argv) + logging.setup() server = wsgi.Server() acp = AjaxConsoleProxy() acp.register_listeners() diff --git a/bin/nova-api b/bin/nova-api index 5937f7744..61a4c7402 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -40,7 +40,6 @@ from nova import version from nova import wsgi LOG = logging.getLogger('nova.api') -LOG.setLevel(logging.DEBUG) FLAGS = flags.FLAGS @@ -80,6 +79,7 @@ def run_app(paste_config_file): if __name__ == '__main__': FLAGS(sys.argv) + logging.setup() LOG.audit(_("Starting nova-api node (version %s)"), version.version_string_with_vcs()) conf = wsgi.paste_config_file('nova-api.conf') diff --git a/bin/nova-combined b/bin/nova-combined index 5911d9016..6ae8400d1 100755 --- a/bin/nova-combined +++ b/bin/nova-combined @@ -49,6 +49,7 @@ FLAGS = flags.FLAGS if __name__ == '__main__': utils.default_flagfile() FLAGS(sys.argv) + logging.setup() compute = service.Service.create(binary='nova-compute') network = service.Service.create(binary='nova-network') diff --git a/bin/nova-compute b/bin/nova-compute index d2d352da2..e412598c2 100755 --- a/bin/nova-compute +++ b/bin/nova-compute @@ -36,10 +36,15 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): gettext.install('nova', unicode=1) +from nova import flags +from nova import log as logging from nova import service from nova import utils if __name__ == '__main__': utils.default_flagfile() + flags.FLAGS(sys.argv) + logging.setup() + service.serve() service.wait() diff --git a/bin/nova-console b/bin/nova-console index 802cc80b6..40608b995 100755 --- a/bin/nova-console +++ b/bin/nova-console @@ -35,10 +35,14 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): gettext.install('nova', unicode=1) +from nova import flags +from nova import log as logging from nova import service from nova import utils if __name__ == '__main__': utils.default_flagfile() + flags.FLAGS(sys.argv) + logging.setup() service.serve() service.wait() diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge index e0e6af826..eda2dc072 100755 --- a/bin/nova-dhcpbridge +++ b/bin/nova-dhcpbridge @@ -102,6 +102,7 @@ def main(): flagfile = os.environ.get('FLAGFILE', FLAGS.dhcpbridge_flagfile) utils.default_flagfile(flagfile) argv = FLAGS(sys.argv) + logging.setup() interface = os.environ.get('DNSMASQ_INTERFACE', 'br0') if int(os.environ.get('TESTING', '0')): FLAGS.fake_rabbit = True @@ -112,7 +113,7 @@ def main(): FLAGS.num_networks = 5 path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', - 'nova.sqlite')) + 'tests.sqlite')) FLAGS.sql_connection = 'sqlite:///%s' % path action = argv[1] if action in ['add', 'del', 'old']: diff --git a/bin/nova-direct-api b/bin/nova-direct-api index 173b39bdb..6c63bd26b 100755 --- a/bin/nova-direct-api +++ b/bin/nova-direct-api @@ -35,6 +35,7 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): gettext.install('nova', unicode=1) from nova import flags +from nova import log as logging from nova import utils from nova import wsgi from nova.api import direct @@ -48,6 +49,7 @@ flags.DEFINE_string('direct_host', '0.0.0.0', 'Direct API host') if __name__ == '__main__': utils.default_flagfile() FLAGS(sys.argv) + logging.setup() direct.register_service('compute', compute_api.API()) direct.register_service('reflect', direct.Reflection()) diff --git a/bin/nova-import-canonical-imagestore b/bin/nova-import-canonical-imagestore index 036b41e48..404ae37f4 100755 --- a/bin/nova-import-canonical-imagestore +++ b/bin/nova-import-canonical-imagestore @@ -41,6 +41,7 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): gettext.install('nova', unicode=1) from nova import flags +from nova import log as logging from nova import utils from nova.objectstore import image @@ -92,6 +93,7 @@ def main(): """Main entry point.""" utils.default_flagfile() argv = FLAGS(sys.argv) + logging.setup() images = get_images() if len(argv) == 2: diff --git a/bin/nova-instancemonitor b/bin/nova-instancemonitor index 7dca02014..24cc9fd23 100755 --- a/bin/nova-instancemonitor +++ b/bin/nova-instancemonitor @@ -41,9 +41,6 @@ from nova import utils from nova import twistd from nova.compute import monitor -# TODO(todd): shouldn't this be done with flags? And what about verbose? -logging.getLogger('boto').setLevel(logging.WARN) - LOG = logging.getLogger('nova.instancemonitor') diff --git a/bin/nova-manage b/bin/nova-manage index 861798717..5189de0e1 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -77,6 +77,7 @@ from nova import crypto from nova import db from nova import exception from nova import flags +from nova import log as logging from nova import quota from nova import rpc from nova import utils @@ -707,6 +708,7 @@ def main(): """Parse options and call the appropriate class/method.""" utils.default_flagfile() argv = FLAGS(sys.argv) + logging.setup() script_name = argv.pop(0) if len(argv) < 1: diff --git a/bin/nova-network b/bin/nova-network index 0143846a7..101761ef7 100755 --- a/bin/nova-network +++ b/bin/nova-network @@ -36,10 +36,14 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): gettext.install('nova', unicode=1) +from nova import flags +from nova import log as logging from nova import service from nova import utils if __name__ == '__main__': utils.default_flagfile() + flags.FLAGS(sys.argv) + logging.setup() service.serve() service.wait() diff --git a/bin/nova-scheduler b/bin/nova-scheduler index f4c0eaed6..0c205a80f 100755 --- a/bin/nova-scheduler +++ b/bin/nova-scheduler @@ -36,10 +36,14 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): gettext.install('nova', unicode=1) +from nova import flags +from nova import log as logging from nova import service from nova import utils if __name__ == '__main__': utils.default_flagfile() + flags.FLAGS(sys.argv) + logging.setup() service.serve() service.wait() diff --git a/bin/nova-volume b/bin/nova-volume index ad3ddc405..8dcdbc500 100755 --- a/bin/nova-volume +++ b/bin/nova-volume @@ -36,10 +36,14 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): gettext.install('nova', unicode=1) +from nova import flags +from nova import log as logging from nova import service from nova import utils if __name__ == '__main__': utils.default_flagfile() + flags.FLAGS(sys.argv) + logging.setup() service.serve() service.wait() diff --git a/nova/log.py b/nova/log.py index 3a48c97ff..10c14d74b 100644 --- a/nova/log.py +++ b/nova/log.py @@ -65,6 +65,7 @@ flags.DEFINE_string('logging_exception_prefix', flags.DEFINE_list('default_log_levels', ['amqplib=WARN', 'sqlalchemy=WARN', + 'boto=WARN', 'eventlet.wsgi.server=WARN'], 'list of logger=LEVEL pairs') @@ -263,26 +264,8 @@ class NovaRootLogger(NovaLogger): self.setLevel(INFO) -if not isinstance(logging.root, NovaRootLogger): - logging._acquireLock() - for handler in logging.root.handlers: - logging.root.removeHandler(handler) - logging.root = NovaRootLogger("nova") - for logger in NovaLogger.manager.loggerDict.itervalues(): - logger.root = logging.root - NovaLogger.root = logging.root - NovaLogger.manager.root = logging.root - NovaLogger.manager.loggerDict["nova"] = logging.root - logging._releaseLock() -root = logging.root - - def handle_exception(type, value, tb): - root.critical(str(value), exc_info=(type, value, tb)) - - -sys.excepthook = handle_exception -logging.setLoggerClass(NovaLogger) + logging.root.critical(str(value), exc_info=(type, value, tb)) def reset(): @@ -292,6 +275,29 @@ def reset(): logger.setup_from_flags() +def setup(): + """Setup nova logging.""" + if not isinstance(logging.root, NovaRootLogger): + logging._acquireLock() + for handler in logging.root.handlers: + logging.root.removeHandler(handler) + logging.root = NovaRootLogger("nova") + NovaLogger.root = logging.root + NovaLogger.manager.root = logging.root + for logger in NovaLogger.manager.loggerDict.itervalues(): + logger.root = logging.root + if isinstance(logger, logging.Logger): + NovaLogger.manager._fixupParents(logger) + NovaLogger.manager.loggerDict["nova"] = logging.root + logging._releaseLock() + sys.excepthook = handle_exception + reset() + + +root = logging.root +logging.setLoggerClass(NovaLogger) + + def audit(msg, *args, **kwargs): """Shortcut for logging to root log with sevrity 'AUDIT'.""" logging.root.log(AUDIT, msg, *args, **kwargs) diff --git a/nova/service.py b/nova/service.py index 02e86f6b3..ddb4d7791 100644 --- a/nova/service.py +++ b/nova/service.py @@ -214,8 +214,6 @@ class Service(object): def serve(*services): - FLAGS(sys.argv) - if not services: services = [Service.create()] diff --git a/nova/twistd.py b/nova/twistd.py index 0e4db022c..c07ed991f 100644 --- a/nova/twistd.py +++ b/nova/twistd.py @@ -148,6 +148,7 @@ def WrapTwistedOptions(wrapped): options.insert(0, '') args = FLAGS(options) + logging.setup() argv = args[1:] # ignore subcommands diff --git a/nova/utils.py b/nova/utils.py index c2fd5f2ee..2a3acf042 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -55,7 +55,7 @@ def import_class(import_str): __import__(mod_str) return getattr(sys.modules[mod_str], class_str) except (ImportError, ValueError, AttributeError), exc: - LOG.info(_('Inner Exception: %s'), exc) + LOG.debug(_('Inner Exception: %s'), exc) raise exception.NotFound(_('Class %s cannot be found') % class_str) diff --git a/run_tests.py b/run_tests.py index 274dc4a97..82345433a 100644 --- a/run_tests.py +++ b/run_tests.py @@ -26,6 +26,7 @@ from nose import config from nose import result from nose import core +from nova import log as logging class NovaTestResult(result.TextTestResult): def __init__(self, *args, **kw): @@ -60,6 +61,7 @@ class NovaTestRunner(core.TextTestRunner): if __name__ == '__main__': if os.path.exists("tests.sqlite"): os.unlink("tests.sqlite") + logging.setup() c = config.Config(stream=sys.stdout, env=os.environ, verbosity=3, -- cgit From dbb071c8424871b6985c6b470d9eff522cdda660 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 21 Feb 2011 13:59:46 -0800 Subject: fix pep8 and remove extra reference to reset --- bin/nova-compute | 1 - nova/flags.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/bin/nova-compute b/bin/nova-compute index e412598c2..95fa393b1 100755 --- a/bin/nova-compute +++ b/bin/nova-compute @@ -45,6 +45,5 @@ if __name__ == '__main__': utils.default_flagfile() flags.FLAGS(sys.argv) logging.setup() - service.serve() service.wait() diff --git a/nova/flags.py b/nova/flags.py index e2f7960ec..f64a62da9 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -29,6 +29,7 @@ import sys import gflags + class FlagValues(gflags.FlagValues): """Extension of gflags.FlagValues that allows undefined and runtime flags. @@ -89,8 +90,6 @@ class FlagValues(gflags.FlagValues): self.__dict__['__stored_argv'] = original_argv self.__dict__['__was_already_parsed'] = True self.ClearDirty() - from nova import log as logging - logging.reset() return args def Reset(self): -- cgit From fc0ea52d9379649d28de88d4fa1628e455533842 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Mon, 21 Feb 2011 23:08:26 +0100 Subject: Add a bunch of docs for the new iptables hotness. --- nova/network/linux_net.py | 72 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index d7a3075cb..3b6ec9338 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -69,6 +69,11 @@ binary_name = os.path.basename(inspect.stack()[-1][1]) class IptablesRule(object): + """An iptables rule + + You shouldn't need to use this class directly, it's only used by + IptablesManager + """ def __init__(self, chain, rule, wrap=True): self.chain = chain self.rule = rule @@ -93,14 +98,33 @@ class IptablesRule(object): class IptablesTable(object): + """An iptables table""" + def __init__(self): self.rules = [] self.chains = set() def add_chain(self, name): + """Adds a named chain to the table + + The chain name is wrapped to be unique for the component creating + it, so different components of Nova can safely create identically + named chains without interfering with one another. + + At the moment, its wrapped name is <binary name>-<chain name>, + so if nova-compute creates a chain named "OUTPUT", it'll actually + end up named "nova-compute-OUTPUT". + """ self.chains.add(name) def remove_chain(self, name): + """Remove named chain + + This removal "cascades". All rule in the chain are removed, as are + all rules in other chains that jump to it. + + If the chain is not found, this is merely logged. + """ if name not in self.chains: LOG.debug(_("Attempted to remove chain %s which doesn't exist"), name) @@ -112,6 +136,15 @@ class IptablesTable(object): self.rules = filter(lambda r: jump_snippet not in r.rule, self.rules) def add_rule(self, chain, rule, wrap=True): + """Add a rule to the table + + This is just like what you'd feed to iptables, just without + the "-A <chain name>" bit at the start. + + However, if you need to jump to one of your wrapped chains, + prepend its name with a '$' which will ensure the wrapping + is applied correctly. + """ if wrap and chain not in self.chains: raise ValueError(_("Unknown chain: %r") % chain) @@ -125,7 +158,13 @@ class IptablesTable(object): return '%s-%s' % (binary_name, s[1:]) return s - def remove_rule(self, *args, **kwargs): + def remove_rule(self, chain, rule, wrap=True): + """Remove a rule from a chain + + Note: The rule must be exactly identical to the one that was added. + You cannot switch arguments around like you can with the iptables + CLI tool. + """ try: self.rules.remove(IptablesRule(*args, **kwargs)) except ValueError: @@ -135,6 +174,22 @@ class IptablesTable(object): class IptablesManager(object): + """Wrapper for iptables + + See IptablesTable for some usage docs + + A number of chains are set up to begin with. + + For ipv4, the filter table has a INPUT, OUTPUT, FORWARD, and local chains + already set up, while the NAT chain has PREROUTING, OUTPUT, POSTROUTING, + and SNATTING. Except for "local" and "SNATTING" these are all set up so + that they are applied at the beginning of their non-wrapped counterparts. + "SNATTING" is jumped to from nat/POSTROUTING and "local" is jumped to from + filter/OUTPUT and filter/FORWARD. + + For ipv6, the filter table has INPUT, OUTPUT, FORWARD, and local. "local" + has the same semantics as for ipv4. + """ def __init__(self, execute=None): if not execute: if FLAGS.fake_network: @@ -190,6 +245,16 @@ class IptablesManager(object): self.semaphore = semaphore.Semaphore() def apply(self): + """Apply the current in-memory set of iptables rules + + This will blow away any rules left over from previous runs of the + same component of Nova, and replace them with our current set of + rules. This happens atomically, thanks to iptables-restore. + + We wrap the call in a semaphore lock, so that we don't race with + ourselves. In the event of a race with another component running + an iptables-* command at the same time, we retry up to 5 times. + """ with self.semaphore: s = [('iptables', self.ipv4)] if FLAGS.use_ipv6: @@ -200,14 +265,13 @@ class IptablesManager(object): current_table, _ = self.execute('sudo %s-save -t %s' % (cmd, table), attempts=5) current_lines = current_table.split('\n') - new_filter = self.modify_rules(current_lines, + new_filter = self._modify_rules(current_lines, tables[table]) self.execute('sudo %s-restore' % (cmd,), process_input='\n'.join(new_filter), attempts=5) - def modify_rules(self, current_lines, table, binary=None): - + def _modify_rules(self, current_lines, table, binary=None): chains = table.chains rules = table.rules -- cgit From e5d030863eae7f997867350916adf0c721625d26 Mon Sep 17 00:00:00 2001 From: termie <github@anarkystic.com> Date: Mon, 21 Feb 2011 14:55:06 -0800 Subject: add a test for rpc consumer isolation --- nova/tests/test_test.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 nova/tests/test_test.py diff --git a/nova/tests/test_test.py b/nova/tests/test_test.py new file mode 100644 index 000000000..c1d96a148 --- /dev/null +++ b/nova/tests/test_test.py @@ -0,0 +1,37 @@ +# 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. + +"""Tests for the testing base code.""" + +from nova import rpc +from nova import test + + +class IsolationTestCase(test.TestCase): + """Ensure that things are cleaned up after failed tests. + + These tests don't really do much here, but if isolation fails a bunch + of other tests should fail. + + """ + def test_rpc_consumer_isolation(self): + connection = rpc.Connection.instance(new=True) + consumer = rpc.TopicConsumer(connection, topic='compute') + consumer.register_callback( + lambda x, y: self.fail('I should never be called')) + consumer.attach_to_eventlet() -- cgit From ab73d72d33369d47012437c022a0679fa4ca3b38 Mon Sep 17 00:00:00 2001 From: termie <github@anarkystic.com> Date: Mon, 21 Feb 2011 14:55:06 -0800 Subject: add a start_service method to our test baseclass --- nova/service.py | 7 +++ nova/test.py | 53 +++++++++++++++++------ nova/tests/test_scheduler.py | 100 +++++++------------------------------------ nova/tests/test_test.py | 3 ++ 4 files changed, 67 insertions(+), 96 deletions(-) diff --git a/nova/service.py b/nova/service.py index 59648adf2..45286cf94 100644 --- a/nova/service.py +++ b/nova/service.py @@ -181,6 +181,13 @@ class Service(object): pass self.timers = [] + def wait(self): + for x in self.timers: + try: + x.wait() + except Exception: + pass + def periodic_tasks(self): """Tasks to be run at a periodic interval""" self.manager.periodic_tasks(context.get_admin_context()) diff --git a/nova/test.py b/nova/test.py index a12cf9d32..9bff401a1 100644 --- a/nova/test.py +++ b/nova/test.py @@ -23,6 +23,7 @@ and some black magic for inline callbacks. """ import datetime +import uuid import unittest import mox @@ -33,6 +34,7 @@ from nova import db from nova import fakerabbit from nova import flags from nova import rpc +from nova import service from nova.network import manager as network_manager from nova.tests import fake_flags @@ -80,6 +82,7 @@ class TestCase(unittest.TestCase): self.stubs = stubout.StubOutForTesting() self.flag_overrides = {} self.injected = [] + self._services = [] self._monkey_patch_attach() self._original_flags = FLAGS.FlagValuesDict() @@ -91,25 +94,42 @@ class TestCase(unittest.TestCase): self.stubs.UnsetAll() self.stubs.SmartUnsetAll() self.mox.VerifyAll() - # NOTE(vish): Clean up any ips associated during the test. - ctxt = context.get_admin_context() - db.fixed_ip_disassociate_all_by_timeout(ctxt, FLAGS.host, - self.start) - db.network_disassociate_all(ctxt) + super(TestCase, self).tearDown() + finally: + try: + # Clean up any ips associated during the test. + ctxt = context.get_admin_context() + db.fixed_ip_disassociate_all_by_timeout(ctxt, FLAGS.host, + self.start) + db.network_disassociate_all(ctxt) + + db.security_group_destroy_all(ctxt) + except Exception: + pass + + # Clean out fake_rabbit's queue if we used it + if FLAGS.fake_rabbit: + fakerabbit.reset_all() + + # Reset any overriden flags + self.reset_flags() + + # Reset our monkey-patches rpc.Consumer.attach_to_eventlet = self.originalAttach + + # Stop any timers for x in self.injected: try: x.stop() except AssertionError: pass - if FLAGS.fake_rabbit: - fakerabbit.reset_all() - - db.security_group_destroy_all(ctxt) - super(TestCase, self).tearDown() - finally: - self.reset_flags() + # Kill any services + for x in self._services: + try: + x.kill() + except Exception: + pass def flags(self, **kw): """Override flag variables for a test""" @@ -127,6 +147,15 @@ class TestCase(unittest.TestCase): for k, v in self._original_flags.iteritems(): setattr(FLAGS, k, v) + def start_service(self, name=None, host=None, **kwargs): + host = host and host or uuid.uuid4().hex + kwargs.setdefault('host', host) + kwargs.setdefault('binary', 'nova-%s' % name) + svc = service.Service.create(**kwargs) + svc.start() + self._services.append(svc) + return svc + def _monkey_patch_attach(self): self.originalAttach = rpc.Consumer.attach_to_eventlet diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py index 9d458244b..250170072 100644 --- a/nova/tests/test_scheduler.py +++ b/nova/tests/test_scheduler.py @@ -176,18 +176,8 @@ class SimpleDriverTestCase(test.TestCase): def test_doesnt_report_disabled_hosts_as_up(self): """Ensures driver doesn't find hosts before they are enabled""" - # NOTE(vish): constructing service without create method - # because we are going to use it without queue - compute1 = service.Service('host1', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute1.start() - compute2 = service.Service('host2', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute2.start() + compute1 = self.start_service('compute', host='host1') + compute2 = self.start_service('compute', host='host2') s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute') s2 = db.service_get_by_args(self.context, 'host2', 'nova-compute') db.service_update(self.context, s1['id'], {'disabled': True}) @@ -199,18 +189,8 @@ class SimpleDriverTestCase(test.TestCase): def test_reports_enabled_hosts_as_up(self): """Ensures driver can find the hosts that are up""" - # NOTE(vish): constructing service without create method - # because we are going to use it without queue - compute1 = service.Service('host1', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute1.start() - compute2 = service.Service('host2', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute2.start() + compute1 = self.start_service('compute', host='host1') + compute2 = self.start_service('compute', host='host2') hosts = self.scheduler.driver.hosts_up(self.context, 'compute') self.assertEqual(2, len(hosts)) compute1.kill() @@ -218,16 +198,8 @@ class SimpleDriverTestCase(test.TestCase): def test_least_busy_host_gets_instance(self): """Ensures the host with less cores gets the next one""" - compute1 = service.Service('host1', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute1.start() - compute2 = service.Service('host2', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute2.start() + compute1 = self.start_service('compute', host='host1') + compute2 = self.start_service('compute', host='host2') instance_id1 = self._create_instance() compute1.run_instance(self.context, instance_id1) instance_id2 = self._create_instance() @@ -241,16 +213,8 @@ class SimpleDriverTestCase(test.TestCase): def test_specific_host_gets_instance(self): """Ensures if you set availability_zone it launches on that zone""" - compute1 = service.Service('host1', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute1.start() - compute2 = service.Service('host2', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute2.start() + compute1 = self.start_service('compute', host='host1') + compute2 = self.start_service('compute', host='host2') instance_id1 = self._create_instance() compute1.run_instance(self.context, instance_id1) instance_id2 = self._create_instance(availability_zone='nova:host1') @@ -263,11 +227,7 @@ class SimpleDriverTestCase(test.TestCase): compute2.kill() def test_wont_sechedule_if_specified_host_is_down(self): - compute1 = service.Service('host1', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute1.start() + compute1 = self.start_service('compute', host='host1') s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute') now = datetime.datetime.utcnow() delta = datetime.timedelta(seconds=FLAGS.service_down_time * 2) @@ -282,11 +242,7 @@ class SimpleDriverTestCase(test.TestCase): compute1.kill() def test_will_schedule_on_disabled_host_if_specified(self): - compute1 = service.Service('host1', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute1.start() + compute1 = self.start_service('compute', host='host1') s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute') db.service_update(self.context, s1['id'], {'disabled': True}) instance_id2 = self._create_instance(availability_zone='nova:host1') @@ -298,16 +254,8 @@ class SimpleDriverTestCase(test.TestCase): def test_too_many_cores(self): """Ensures we don't go over max cores""" - compute1 = service.Service('host1', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute1.start() - compute2 = service.Service('host2', - 'nova-compute', - 'compute', - FLAGS.compute_manager) - compute2.start() + compute1 = self.start_service('compute', host='host1') + compute2 = self.start_service('compute', host='host2') instance_ids1 = [] instance_ids2 = [] for index in xrange(FLAGS.max_cores): @@ -331,16 +279,8 @@ class SimpleDriverTestCase(test.TestCase): def test_least_busy_host_gets_volume(self): """Ensures the host with less gigabytes gets the next one""" - volume1 = service.Service('host1', - 'nova-volume', - 'volume', - FLAGS.volume_manager) - volume1.start() - volume2 = service.Service('host2', - 'nova-volume', - 'volume', - FLAGS.volume_manager) - volume2.start() + volume1 = self.start_service('volume', host='host1') + volume2 = self.start_service('volume', host='host2') volume_id1 = self._create_volume() volume1.create_volume(self.context, volume_id1) volume_id2 = self._create_volume() @@ -354,16 +294,8 @@ class SimpleDriverTestCase(test.TestCase): def test_too_many_gigabytes(self): """Ensures we don't go over max gigabytes""" - volume1 = service.Service('host1', - 'nova-volume', - 'volume', - FLAGS.volume_manager) - volume1.start() - volume2 = service.Service('host2', - 'nova-volume', - 'volume', - FLAGS.volume_manager) - volume2.start() + volume1 = self.start_service('volume', host='host1') + volume2 = self.start_service('volume', host='host2') volume_ids1 = [] volume_ids2 = [] for index in xrange(FLAGS.max_gigabytes): diff --git a/nova/tests/test_test.py b/nova/tests/test_test.py index c1d96a148..e237674e6 100644 --- a/nova/tests/test_test.py +++ b/nova/tests/test_test.py @@ -29,6 +29,9 @@ class IsolationTestCase(test.TestCase): of other tests should fail. """ + def test_service_isolation(self): + self.start_service('compute') + def test_rpc_consumer_isolation(self): connection = rpc.Connection.instance(new=True) consumer = rpc.TopicConsumer(connection, topic='compute') -- cgit From 9003241814ab67817ea910943e932d7b2e542eb6 Mon Sep 17 00:00:00 2001 From: termie <github@anarkystic.com> Date: Mon, 21 Feb 2011 14:55:06 -0800 Subject: move test_cloud to use start_service, too --- nova/tests/test_cloud.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 445cc6e8b..a174ea75d 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -65,10 +65,8 @@ class CloudTestCase(test.TestCase): self.cloud = cloud.CloudController() # set up services - self.compute = service.Service.create(binary='nova-compute') - self.compute.start() - self.network = service.Service.create(binary='nova-network') - self.network.start() + self.compute = self.start_service('compute') + self.network = self.start_service('network') self.manager = manager.AuthManager() self.user = self.manager.create_user('admin', 'admin', 'admin', True) -- cgit From 83e4dcb7184169d4d35769c2d56b21e66c908e75 Mon Sep 17 00:00:00 2001 From: termie <github@anarkystic.com> Date: Mon, 21 Feb 2011 14:55:06 -0800 Subject: remove keyword argument, per review --- nova/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/test.py b/nova/test.py index 9bff401a1..4602f0313 100644 --- a/nova/test.py +++ b/nova/test.py @@ -147,7 +147,7 @@ class TestCase(unittest.TestCase): for k, v in self._original_flags.iteritems(): setattr(FLAGS, k, v) - def start_service(self, name=None, host=None, **kwargs): + def start_service(self, name, host=None, **kwargs): host = host and host or uuid.uuid4().hex kwargs.setdefault('host', host) kwargs.setdefault('binary', 'nova-%s' % name) -- cgit From 4b2a45aa5dc91b24aea53f748906d8a69e40f7c8 Mon Sep 17 00:00:00 2001 From: termie <github@anarkystic.com> Date: Mon, 21 Feb 2011 15:42:16 -0800 Subject: modify tests to use specific hosts rather than default --- nova/tests/test_cloud.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index a174ea75d..1824d24bc 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -100,7 +100,7 @@ class CloudTestCase(test.TestCase): address = "10.10.10.10" db.floating_ip_create(self.context, {'address': address, - 'host': FLAGS.host}) + 'host': self.network.host}) self.cloud.allocate_address(self.context) self.cloud.describe_addresses(self.context) self.cloud.release_address(self.context, @@ -113,9 +113,9 @@ class CloudTestCase(test.TestCase): address = "10.10.10.10" db.floating_ip_create(self.context, {'address': address, - 'host': FLAGS.host}) + 'host': self.network.host}) self.cloud.allocate_address(self.context) - inst = db.instance_create(self.context, {'host': FLAGS.host}) + inst = db.instance_create(self.context, {'host': self.compute.host}) fixed = self.network.allocate_fixed_ip(self.context, inst['id']) ec2_id = cloud.id_to_ec2_id(inst['id']) self.cloud.associate_address(self.context, -- cgit From f797d6c6464f8ee2816d56ee771ad718418def64 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Mon, 21 Feb 2011 15:52:41 -0800 Subject: Renamed db_update to model_update, and lots more documentation --- nova/volume/driver.py | 26 ++++++++++++++++++++++---- nova/volume/manager.py | 12 ++++++------ nova/volume/san.py | 18 +++++++++--------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/nova/volume/driver.py b/nova/volume/driver.py index f172e2fdc..687bc99d0 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -91,7 +91,8 @@ class VolumeDriver(object): % FLAGS.volume_group) def create_volume(self, volume): - """Creates a logical volume.""" + """Creates a logical volume. Can optionally return a Dictionary of + changes to the volume object to be persisted.""" if int(volume['size']) == 0: sizestr = '100M' else: @@ -126,7 +127,8 @@ class VolumeDriver(object): raise NotImplementedError() def create_export(self, context, volume): - """Exports the volume.""" + """Exports the volume. Can optionally return a Dictionary of changes + to the volume object to be persisted.""" raise NotImplementedError() def remove_export(self, context, volume): @@ -225,7 +227,14 @@ class FakeAOEDriver(AOEDriver): class ISCSIDriver(VolumeDriver): - """Executes commands relating to ISCSI volumes.""" + """Executes commands relating to ISCSI volumes. We make use of model + provider properties as follows: + provider_location - if present, contains the iSCSI target information + in the same format as an ietadm discovery + i.e. '<target iqn>,<target portal> <target name>' + provider_auth - if present, contains a space-separated triple: + '<auth method> <auth username> <auth password>'. CHAP is the only + auth_method in use at the moment.""" def ensure_export(self, context, volume): """Synchronously recreates an export for a logical volume.""" @@ -313,7 +322,16 @@ class ISCSIDriver(VolumeDriver): def _get_iscsi_properties(self, volume): """Gets iscsi configuration, ideally from saved information in the - volume entity, but falling back to discovery if need be.""" + volume entity, but falling back to discovery if need be. The + properties are: + target_discovered - boolean indicating whether discovery was used, + target_iqn - the IQN of the iSCSI target, + target_portal - the portal of the iSCSI target, + and auth_method, auth_username and auth_password + - the authentication details. Right now, either + auth_method is not present meaning no authentication, or + auth_method == 'CHAP' meaning use CHAP with the specified + credentials.""" properties = {} diff --git a/nova/volume/manager.py b/nova/volume/manager.py index 7193ece14..3e8bc16b3 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -107,14 +107,14 @@ class VolumeManager(manager.Manager): vol_size = volume_ref['size'] LOG.debug(_("volume %(vol_name)s: creating lv of" " size %(vol_size)sG") % locals()) - db_update = self.driver.create_volume(volume_ref) - if db_update: - self.db.volume_update(context, volume_ref['id'], db_update) + model_update = self.driver.create_volume(volume_ref) + if model_update: + self.db.volume_update(context, volume_ref['id'], model_update) LOG.debug(_("volume %s: creating export"), volume_ref['name']) - db_update = self.driver.create_export(context, volume_ref) - if db_update: - self.db.volume_update(context, volume_ref['id'], db_update) + model_update = self.driver.create_export(context, volume_ref) + if model_update: + self.db.volume_update(context, volume_ref['id'], model_update) except Exception: self.db.volume_update(context, volume_ref['id'], {'status': 'error'}) diff --git a/nova/volume/san.py b/nova/volume/san.py index 911ad096f..09192bc9f 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -485,12 +485,12 @@ class HpSanISCSIDriver(SanISCSIDriver): cluster_vip = self._cliq_get_cluster_vip(cluster_name) iscsi_portal = cluster_vip + ":3260," + cluster_interface - db_update = {} - db_update['provider_location'] = ("%s %s" % - (iscsi_portal, - iscsi_iqn)) + model_update = {} + model_update['provider_location'] = ("%s %s" % + (iscsi_portal, + iscsi_iqn)) - return db_update + return model_update def delete_volume(self, volume): """Deletes a volume.""" @@ -517,7 +517,7 @@ class HpSanISCSIDriver(SanISCSIDriver): is_shared = 'permission.authGroup' in volume_info - db_update = {} + model_update = {} should_export = False @@ -551,10 +551,10 @@ class HpSanISCSIDriver(SanISCSIDriver): self._cliq_run_xml("assignVolumeChap", cliq_args) - db_update['provider_auth'] = ("CHAP %s %s" % - (chap_username, chap_password)) + model_update['provider_auth'] = ("CHAP %s %s" % + (chap_username, chap_password)) - return db_update + return model_update def remove_export(self, context, volume): """Removes an export for a logical volume.""" -- cgit From 90007cd9085909a1e1ac59732a0b371dd79c2557 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 21 Feb 2011 15:55:50 -0800 Subject: pretty colors for logs and a few optimizations --- bin/nova-dhcpbridge | 2 + run_tests.py | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 202 insertions(+), 2 deletions(-) diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge index eda2dc072..04a1771f0 100755 --- a/bin/nova-dhcpbridge +++ b/bin/nova-dhcpbridge @@ -113,6 +113,8 @@ def main(): FLAGS.num_networks = 5 path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', + 'nova', + 'tests', 'tests.sqlite')) FLAGS.sql_connection = 'sqlite:///%s' % path action = argv[1] diff --git a/run_tests.py b/run_tests.py index 82345433a..43508394f 100644 --- a/run_tests.py +++ b/run_tests.py @@ -17,6 +17,29 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Colorizer Code is borrowed from Twisted: +# Copyright (c) 2001-2010 Twisted Matrix Laboratories. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + import gettext import os import unittest @@ -28,14 +51,186 @@ from nose import core from nova import log as logging +class _AnsiColorizer(object): + """ + A colorizer is an object that loosely wraps around a stream, allowing + callers to write text to the stream in a particular color. + + Colorizer classes must implement C{supported()} and C{write(text, color)}. + """ + _colors = dict(black=30, red=31, green=32, yellow=33, + blue=34, magenta=35, cyan=36, white=37) + + def __init__(self, stream): + self.stream = stream + + def supported(cls, stream=sys.stdout): + """ + A class method that returns True if the current platform supports + coloring terminal output using this method. Returns False otherwise. + """ + if not stream.isatty(): + return False # auto color only on TTYs + try: + import curses + except ImportError: + return False + else: + try: + try: + return curses.tigetnum("colors") > 2 + except curses.error: + curses.setupterm(fd=stream.fileno()) + return curses.tigetnum("colors") > 2 + except: + raise + # guess false in case of error + return False + supported = classmethod(supported) + + def write(self, text, color): + """ + Write the given text to the stream in the given color. + + @param text: Text to be written to the stream. + + @param color: A string label for a color. e.g. 'red', 'white'. + """ + color = self._colors[color] + self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text)) + + +class _Win32Colorizer(object): + """ + See _AnsiColorizer docstring. + """ + def __init__(self, stream): + from win32console import GetStdHandle, STD_ERROR_HANDLE, \ + FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \ + FOREGROUND_INTENSITY + red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN, + FOREGROUND_BLUE, FOREGROUND_INTENSITY) + self.stream = stream + self.screenBuffer = GetStdHandle(STD_ERROR_HANDLE) + self._colors = { + 'normal': red | green | blue, + 'red': red | bold, + 'green': green | bold, + 'blue': blue | bold, + 'yellow': red | green | bold, + 'magenta': red | blue | bold, + 'cyan': green | blue | bold, + 'white': red | green | blue | bold + } + + def supported(cls, stream=sys.stdout): + try: + import win32console + screenBuffer = win32console.GetStdHandle( + win32console.STD_ERROR_HANDLE) + except ImportError: + return False + import pywintypes + try: + screenBuffer.SetConsoleTextAttribute( + win32console.FOREGROUND_RED | + win32console.FOREGROUND_GREEN | + win32console.FOREGROUND_BLUE) + except pywintypes.error: + return False + else: + return True + supported = classmethod(supported) + + def write(self, text, color): + color = self._colors[color] + self.screenBuffer.SetConsoleTextAttribute(color) + self.stream.write(text) + self.screenBuffer.SetConsoleTextAttribute(self._colors['normal']) + + +class _NullColorizer(object): + """ + See _AnsiColorizer docstring. + """ + def __init__(self, stream): + self.stream = stream + + def supported(cls, stream=sys.stdout): + return True + supported = classmethod(supported) + + def write(self, text, color): + self.stream.write(text) + class NovaTestResult(result.TextTestResult): def __init__(self, *args, **kw): result.TextTestResult.__init__(self, *args, **kw) self._last_case = None + self.colorizer = None + for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]: + # NOTE(vish): nose does funky stuff with stdout, so use stderr + # to setup the colorizer + if colorizer.supported(sys.stderr): + self.colorizer = colorizer(self.stream) + break def getDescription(self, test): return str(test) + def addSuccess(self, test): + if self.showAll: + self.colorizer.write("OK", 'green') + self.stream.writeln() + elif self.dots: + self.stream.write('.') + self.stream.flush() + + def addFailure(self, test): + if self.showAll: + self.colorizer.write("FAIL", 'red') + self.stream.writeln() + elif self.dots: + self.stream.write('F') + self.stream.flush() + + def addError(self, test, err): + """Overrides normal addError to add support for + errorClasses. If the exception is a registered class, the + error will be added to the list for that class, not errors. + """ + stream = getattr(self, 'stream', None) + ec, ev, tb = err + try: + exc_info = self._exc_info_to_string(err, test) + except TypeError: + # 2.3 compat + exc_info = self._exc_info_to_string(err) + for cls, (storage, label, isfail) in self.errorClasses.items(): + if result.isclass(ec) and issubclass(ec, cls): + if isfail: + test.passed = False + storage.append((test, exc_info)) + # Might get patched into a streamless result + if stream is not None: + if self.showAll: + message = [label] + detail = result._exception_detail(err[1]) + if detail: + message.append(detail) + stream.writeln(": ".join(message)) + elif self.dots: + stream.write(label[:1]) + return + self.errors.append((test, exc_info)) + test.passed = False + if stream is not None: + if self.showAll: + self.colorizer.write("ERROR", 'red') + self.stream.writeln() + elif self.dots: + stream.write('E') + def startTest(self, test): unittest.TestResult.startTest(self, test) current_case = test.test.__class__.__name__ @@ -59,12 +254,15 @@ class NovaTestRunner(core.TextTestRunner): if __name__ == '__main__': - if os.path.exists("tests.sqlite"): - os.unlink("tests.sqlite") logging.setup() + testdir = os.path.abspath(os.path.join("nova","tests")) + testdb = os.path.join(testdir, "tests.sqlite") + if os.path.exists(testdb): + os.unlink(testdb) c = config.Config(stream=sys.stdout, env=os.environ, verbosity=3, + workingDir=testdir, plugins=core.DefaultPluginManager()) runner = NovaTestRunner(stream=c.stream, -- cgit From 305ef6bf5f8f8926fdaa8db5f75a0680fbd8a2be Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Mon, 21 Feb 2011 16:02:38 -0800 Subject: Fixed my confusion in documenting the syntax of iSCSI discovery --- nova/volume/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 687bc99d0..22c2c2fc3 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -231,7 +231,7 @@ class ISCSIDriver(VolumeDriver): provider properties as follows: provider_location - if present, contains the iSCSI target information in the same format as an ietadm discovery - i.e. '<target iqn>,<target portal> <target name>' + i.e. '<target ip/port>,<target portal> <target IQN>' provider_auth - if present, contains a space-separated triple: '<auth method> <auth username> <auth password>'. CHAP is the only auth_method in use at the moment.""" -- cgit From 71f7119910f16cb99c10f43a07ccb1e7c0ca473f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 21 Feb 2011 16:05:28 -0800 Subject: remove changes to test db --- bin/nova-dhcpbridge | 2 +- nova/tests/fake_flags.py | 2 +- run_tests.py | 2 -- run_tests.sh | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge index eda2dc072..35b837ca9 100755 --- a/bin/nova-dhcpbridge +++ b/bin/nova-dhcpbridge @@ -113,7 +113,7 @@ def main(): FLAGS.num_networks = 5 path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', - 'tests.sqlite')) + 'nova.sqlite')) FLAGS.sql_connection = 'sqlite:///%s' % path action = argv[1] if action in ['add', 'del', 'old']: diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index 575fefff6..cfa65c137 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -39,6 +39,6 @@ FLAGS.num_shelves = 2 FLAGS.blades_per_shelf = 4 FLAGS.iscsi_num_targets = 8 FLAGS.verbose = True -FLAGS.sql_connection = 'sqlite:///tests.sqlite' +FLAGS.sql_connection = 'sqlite:///nova.sqlite' FLAGS.use_ipv6 = True FLAGS.logfile = 'tests.log' diff --git a/run_tests.py b/run_tests.py index 82345433a..cb957da42 100644 --- a/run_tests.py +++ b/run_tests.py @@ -59,8 +59,6 @@ class NovaTestRunner(core.TextTestRunner): if __name__ == '__main__': - if os.path.exists("tests.sqlite"): - os.unlink("tests.sqlite") logging.setup() c = config.Config(stream=sys.stdout, env=os.environ, diff --git a/run_tests.sh b/run_tests.sh index 4e21fe945..70212cc6a 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -40,7 +40,7 @@ done function run_tests { # Just run the test suites in current environment ${wrapper} rm -f nova.sqlite - ${wrapper} $NOSETESTS 2> run_tests.err.log + ${wrapper} $NOSETESTS } NOSETESTS="python run_tests.py $noseargs" -- cgit From 11c57867ec18bd61dcc6bde0dc4b459318d54e70 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 21 Feb 2011 16:19:48 -0800 Subject: fixed newline and moved import fake_flags into run_tests where it makes more sense --- nova/test.py | 3 --- run_tests.py | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/nova/test.py b/nova/test.py index 8022396cd..bff43b6c7 100644 --- a/nova/test.py +++ b/nova/test.py @@ -36,9 +36,6 @@ from nova import log as logging from nova import rpc from nova.network import manager as network_manager -from nova.tests import fake_flags -logging.reset() - FLAGS = flags.FLAGS flags.DEFINE_bool('flush_db', True, diff --git a/run_tests.py b/run_tests.py index cb957da42..6d96454b9 100644 --- a/run_tests.py +++ b/run_tests.py @@ -27,6 +27,8 @@ from nose import result from nose import core from nova import log as logging +from nova.tests import fake_flags + class NovaTestResult(result.TextTestResult): def __init__(self, *args, **kw): -- cgit From 0f402b72cbf80d1adde503eb532a578944fa0c79 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 21 Feb 2011 16:22:09 -0800 Subject: update based on prereq branch --- nova/tests/fake_flags.py | 2 +- run_tests.sh | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index cfa65c137..575fefff6 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -39,6 +39,6 @@ FLAGS.num_shelves = 2 FLAGS.blades_per_shelf = 4 FLAGS.iscsi_num_targets = 8 FLAGS.verbose = True -FLAGS.sql_connection = 'sqlite:///nova.sqlite' +FLAGS.sql_connection = 'sqlite:///tests.sqlite' FLAGS.use_ipv6 = True FLAGS.logfile = 'tests.log' diff --git a/run_tests.sh b/run_tests.sh index 70212cc6a..e8433bc06 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -39,7 +39,6 @@ done function run_tests { # Just run the test suites in current environment - ${wrapper} rm -f nova.sqlite ${wrapper} $NOSETESTS } -- cgit From 6e6c3fcfe97d60b6262bf90d423a77fa250bd383 Mon Sep 17 00:00:00 2001 From: Christian Berendt <berendt@b1-systems.de> Date: Tue, 22 Feb 2011 08:22:42 +0100 Subject: added disabled services to the list of displayed services in bin/nova-manage brontes:~ # nova-manage service list ares nova-scheduler enabled :-) 2011-02-22 07:21:29 ares nova-network enabled :-) 2011-02-22 07:21:29 ares nova-volume enabled XXX 2011-02-16 19:04:29 brontes nova-volume enabled XXX 2011-02-12 18:31:43 brontes nova-network enabled :-) 2011-02-22 07:21:29 ares nova-compute disabled :-) 2011-02-22 07:21:25 brontes nova-compute disabled :-) 2011-02-22 07:21:24 --- bin/nova-manage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-manage b/bin/nova-manage index 6d67252b8..af8432441 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -552,7 +552,7 @@ class ServiceCommands(object): args: [host] [service]""" ctxt = context.get_admin_context() now = datetime.datetime.utcnow() - services = db.service_get_all(ctxt) + services = db.service_get_all(ctxt) + db.service_get_all(ctxt, True) if host: services = [s for s in services if s['host'] == host] if service: -- cgit From 3125d978fec27608064dd3dd8d3696f2219fbf12 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 21 Feb 2011 23:26:03 -0800 Subject: use a different flag for listen port for apis --- bin/nova-api | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/nova-api b/bin/nova-api index 11176a021..cb9b41725 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -44,6 +44,8 @@ LOG = logging.getLogger('nova.api') LOG.setLevel(logging.DEBUG) FLAGS = flags.FLAGS +flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen') +flags.DEFINE_integer('osapi_listen_port', 8774, 'port for os api to listen') API_ENDPOINTS = ['ec2', 'osapi'] @@ -60,10 +62,10 @@ def run_app(paste_config_file): wsgi.paste_config_to_flags(config, { "verbose": FLAGS.verbose, "%s_host" % api: config.get('host', '0.0.0.0'), - "%s_port" % api: getattr(FLAGS, "%s_port" % api)}) + "%s_listen_port" % api: getattr(FLAGS, "%s_listen_port" % api)}) LOG.info(_("Running %s API"), api) app = wsgi.load_paste_app(paste_config_file, api) - apps.append((app, getattr(FLAGS, "%s_port" % api), + apps.append((app, getattr(FLAGS, "%s_listen_port" % api), getattr(FLAGS, "%s_host" % api))) if len(apps) == 0: LOG.error(_("No known API applications configured in %s."), -- cgit From c53bb1718a9b5900d09637d0ee966dadbf073900 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Tue, 22 Feb 2011 09:00:29 +0100 Subject: Address some review comments. --- nova/network/linux_net.py | 16 ++++++++-------- nova/tests/test_virt.py | 1 - 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 3b6ec9338..42af73e74 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -54,8 +54,6 @@ flags.DEFINE_string('dhcpbridge', _bin_file('nova-dhcpbridge'), 'location of nova-dhcpbridge') flags.DEFINE_string('routing_source_ip', '$my_ip', 'Public IP of network host') -flags.DEFINE_bool('use_nova_chains', False, - 'use the nova_ routing chains instead of default') flags.DEFINE_string('input_chain', 'INPUT', 'chain to add nova_input to') @@ -266,7 +264,7 @@ class IptablesManager(object): (cmd, table), attempts=5) current_lines = current_table.split('\n') new_filter = self._modify_rules(current_lines, - tables[table]) + tables[table]) self.execute('sudo %s-restore' % (cmd,), process_input='\n'.join(new_filter), attempts=5) @@ -276,15 +274,17 @@ class IptablesManager(object): rules = table.rules # Remove any trace of our rules - new_filter = filter(lambda l: binary_name not in l, current_lines) + new_filter = filter(lambda line: binary_name not in line, + current_lines) seen_chains = False - for rules_index in range(len(new_filter)): + rules_index = 0 + for rules_index, rule in enumerate(new_filter): if not seen_chains: - if new_filter[rules_index].startswith(':'): + if rule.startswith(':'): seen_chains = True - elif seen_chains == 1: - if not new_filter[rules_index].startswith(':'): + else: + if not rule.startswith(':'): break new_filter[rules_index:rules_index] = [str(rule) for rule in rules] diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 11201788c..c2c7c8337 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -354,7 +354,6 @@ class IptablesFirewallTestCase(test.TestCase): instance_chain = None for rule in self.out_rules: - print rule # This is pretty crude, but it'll do for now if '-d 10.11.12.13 -j' in rule: instance_chain = rule.split(' ')[-1] -- cgit From 70d526dc44299b6bd54a5757d013ca3109887747 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Tue, 22 Feb 2011 09:28:02 +0100 Subject: Add a new chain, floating-ip-snat, at the top of SNATTING, so that SNATting for floating ips gets applied before the default SNAT rule. --- nova/network/linux_net.py | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 42af73e74..6b0735b5c 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -201,26 +201,13 @@ class IptablesManager(object): 'nat': IptablesTable()} self.ipv6 = {'filter': IptablesTable()} - self.ipv4['nat'].add_chain('SNATTING') - self.ipv4['nat'].add_rule('POSTROUTING', - '-j %s-SNATTING' % (binary_name,), - wrap=False) - self.ipv4['filter'].add_chain('local') - self.ipv4['filter'].add_rule('FORWARD', - '-j %s-local' % (binary_name,), - wrap=False) - self.ipv4['filter'].add_rule('OUTPUT', - '-j %s-local' % (binary_name,), - wrap=False) + self.ipv4['filter'].add_rule('FORWARD', '-j $local', wrap=False) + self.ipv4['filter'].add_rule('OUTPUT', '-j $local', wrap=False) self.ipv6['filter'].add_chain('local') - self.ipv6['filter'].add_rule('FORWARD', - '-j %s-local' % (binary_name,), - wrap=False) - self.ipv6['filter'].add_rule('OUTPUT', - '-j %s-local' % (binary_name,), - wrap=False) + self.ipv6['filter'].add_rule('FORWARD', '-j $local', wrap=False) + self.ipv6['filter'].add_rule('OUTPUT', '-j $local', wrap=False) # Wrap the builtin chains builtin_chains = {4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], @@ -236,12 +223,22 @@ class IptablesManager(object): for table, chains in builtin_chains[ip_version].iteritems(): for chain in chains: tables[table].add_chain(chain) - tables[table].add_rule(chain, - '-j %s-%s' % (binary_name, chain), + tables[table].add_rule(chain, '-j $%s' % (chain,), wrap=False) + # We add a SNATTING chain after our (wrapped) POSTROUTING chain + # so that rules added there will be applied after whatever we have in + # (the wrapped) POSTROUTING. + self.ipv4['nat'].add_chain('SNATTING') + self.ipv4['nat'].add_rule('POSTROUTING', '-j $SNATTING', wrap=False) + + self.ipv4['nat'].add_chain('floating-ip-snat') + self.ipv4['nat'].add_rule('SNATTING', '-j $floating-ip-snat') + self.semaphore = semaphore.Semaphore() + iptables_manager.apply() + def apply(self): """Apply the current in-memory set of iptables rules @@ -318,11 +315,6 @@ def init_host(): (FLAGS.fixed_range, FLAGS.routing_source_ip)) - iptables_manager.ipv4['nat'].add_rule("POSTROUTING", - "-s %s -j SNAT --to-source %s" % \ - (FLAGS.fixed_range, - FLAGS.routing_source_ip)) - iptables_manager.ipv4['nat'].add_rule("POSTROUTING", "-s %s -d %s -j ACCEPT" % \ (FLAGS.fixed_range, FLAGS.dmz_cidr)) @@ -377,7 +369,8 @@ def remove_floating_forward(floating_ip, fixed_ip): def floating_forward_rules(floating_ip, fixed_ip): return [("PREROUTING", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)), ("OUTPUT", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)), - ("SNATTING", "-d %s -j SNAT --to %s" % (fixed_ip, floating_ip))] + ("floating-ip-snat", + "-s %s -j SNAT --to %s" % (fixed_ip, floating_ip))] def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): -- cgit From bf37fb0ab5503a077a3d9e4109990d252e27cb15 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Tue, 22 Feb 2011 11:29:58 +0100 Subject: Add a bunch of tests for everything. Add a 'head' kwarg to add_rule that lets the rule bubble to the top. This is needed for nova-filter-top to end up at the top. --- nova/network/linux_net.py | 120 ++++++++++++++++++++++++++++++------------ nova/tests/test_network.py | 128 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 192 insertions(+), 56 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 6b0735b5c..2ccb5969e 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -72,19 +72,22 @@ class IptablesRule(object): You shouldn't need to use this class directly, it's only used by IptablesManager """ - def __init__(self, chain, rule, wrap=True): + def __init__(self, chain, rule, wrap=True, head=False): self.chain = chain self.rule = rule self.wrap = wrap + self.head = head def __eq__(self, other): return ((self.chain == other.chain) and (self.rule == other.rule) and + (self.head == other.head) and (self.wrap == other.wrap)) def __ne__(self, other): return ((self.chain != other.chain) or (self.rule != other.rule) or + (self.head != other.head) or (self.wrap != other.wrap)) def __str__(self): @@ -101,8 +104,9 @@ class IptablesTable(object): def __init__(self): self.rules = [] self.chains = set() + self.unwrapped_chains = set() - def add_chain(self, name): + def add_chain(self, name, wrap=True): """Adds a named chain to the table The chain name is wrapped to be unique for the component creating @@ -113,9 +117,12 @@ class IptablesTable(object): so if nova-compute creates a chain named "OUTPUT", it'll actually end up named "nova-compute-OUTPUT". """ - self.chains.add(name) + if wrap: + self.chains.add(name) + else: + self.unwrapped_chains.add(name) - def remove_chain(self, name): + def remove_chain(self, name, wrap=True): """Remove named chain This removal "cascades". All rule in the chain are removed, as are @@ -123,17 +130,27 @@ class IptablesTable(object): If the chain is not found, this is merely logged. """ - if name not in self.chains: + if wrap: + chain_set = self.chains + else: + chain_set = self.unwrapped_chains + + if name not in chain_set: LOG.debug(_("Attempted to remove chain %s which doesn't exist"), name) return - self.chains.remove(name) + + chain_set.remove(name) self.rules = filter(lambda r: r.chain != name, self.rules) - jump_snippet = '-j %s-%s' % (binary_name, name) + if wrap: + jump_snippet = '-j %s-%s' % (binary_name, name) + else: + jump_snippet = '-j %s' % (name,) + self.rules = filter(lambda r: jump_snippet not in r.rule, self.rules) - def add_rule(self, chain, rule, wrap=True): + def add_rule(self, chain, rule, wrap=True, head=False): """Add a rule to the table This is just like what you'd feed to iptables, just without @@ -149,14 +166,14 @@ class IptablesTable(object): if '$' in rule: rule = ' '.join(map(self._wrap_target_chain, rule.split(' '))) - self.rules.append(IptablesRule(chain, rule, wrap)) + self.rules.append(IptablesRule(chain, rule, wrap, head)) def _wrap_target_chain(self, s): if s.startswith('$'): return '%s-%s' % (binary_name, s[1:]) return s - def remove_rule(self, chain, rule, wrap=True): + def remove_rule(self, chain, rule, wrap=True, head=False): """Remove a rule from a chain Note: The rule must be exactly identical to the one that was added. @@ -164,11 +181,12 @@ class IptablesTable(object): CLI tool. """ try: - self.rules.remove(IptablesRule(*args, **kwargs)) + self.rules.remove(IptablesRule(chain, rule, wrap, head)) except ValueError: LOG.debug(_("Tried to remove rule that wasn't there:" - " %(args)r %(kwargs)r"), {'args': args, - 'kwargs': kwargs}) + " %(chain)r %(rule)r %(wrap)r %(head)r"), + {'chain': chain, 'rule': rule, + 'head': head, 'wrap': wrap}) class IptablesManager(object): @@ -178,15 +196,19 @@ class IptablesManager(object): A number of chains are set up to begin with. - For ipv4, the filter table has a INPUT, OUTPUT, FORWARD, and local chains - already set up, while the NAT chain has PREROUTING, OUTPUT, POSTROUTING, - and SNATTING. Except for "local" and "SNATTING" these are all set up so - that they are applied at the beginning of their non-wrapped counterparts. - "SNATTING" is jumped to from nat/POSTROUTING and "local" is jumped to from - filter/OUTPUT and filter/FORWARD. + First, nova-filter-top. It's added at the top of FORWARD and OUTPUT. Its + name is not wrapped, so it's shared between the various nova workers. It's + intended for rules that need to live at the top of the FORWARD and OUTPUT + chains. It's in both the ipv4 and ipv6 set of tables. - For ipv6, the filter table has INPUT, OUTPUT, FORWARD, and local. "local" - has the same semantics as for ipv4. + For ipv4 and ipv6, the builtin INPUT, OUTPUT, and FORWARD filter chains are + wrapped, meaning that the "real" INPUT chain has a rule that jumps to the + wrapped INPUT chain, etc. Additionally, there's a wrapped chain named + "local" which is jumped to from nova-filter-top. + + For ipv4, the builtin PREROUTING, OUTPUT, and POSTROUTING nat chains are + wrapped in the same was as the builtin filter chains. Additionally, there's + a SNATTING chain that is applied after the POSTROUTING chain. """ def __init__(self, execute=None): if not execute: @@ -201,13 +223,19 @@ class IptablesManager(object): 'nat': IptablesTable()} self.ipv6 = {'filter': IptablesTable()} - self.ipv4['filter'].add_chain('local') - self.ipv4['filter'].add_rule('FORWARD', '-j $local', wrap=False) - self.ipv4['filter'].add_rule('OUTPUT', '-j $local', wrap=False) + # Add a nova-filter-top chain. It's intended to be shared + # among the various nova components. It sits at the very top + # of FORWARD and OUTPUT. + for tables in [self.ipv4, self.ipv6]: + tables['filter'].add_chain('nova-filter-top', wrap=False) + tables['filter'].add_rule('FORWARD', '-j nova-filter-top', + wrap=False, head=True) + tables['filter'].add_rule('OUTPUT', '-j nova-filter-top', + wrap=False, head=True) - self.ipv6['filter'].add_chain('local') - self.ipv6['filter'].add_rule('FORWARD', '-j $local', wrap=False) - self.ipv6['filter'].add_rule('OUTPUT', '-j $local', wrap=False) + tables['filter'].add_chain('local') + tables['filter'].add_rule('nova-filter-top', '-j $local', + wrap=False) # Wrap the builtin chains builtin_chains = {4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], @@ -226,18 +254,27 @@ class IptablesManager(object): tables[table].add_rule(chain, '-j $%s' % (chain,), wrap=False) - # We add a SNATTING chain after our (wrapped) POSTROUTING chain - # so that rules added there will be applied after whatever we have in - # (the wrapped) POSTROUTING. + # Add a nova-postrouting-bottom chain. It's intended to be shared + # among the various nova components. We set it as the last chain + # of POSTROUTING chain. + self.ipv4['nat'].add_chain('nova-postrouting-bottom', wrap=False) + self.ipv4['nat'].add_rule('POSTROUTING', '-j nova-postrouting-bottom', + wrap=False) + + + # We add a SNATTING chain to the shared nova-postrouting-bottom chain + # so that it's applied last. self.ipv4['nat'].add_chain('SNATTING') - self.ipv4['nat'].add_rule('POSTROUTING', '-j $SNATTING', wrap=False) + self.ipv4['nat'].add_rule('nova-postrouting-bottom', '-j $SNATTING', wrap=False) + # And then we add a floating-ip-snat chain and jump to first thing in the SNATTING + # chain. self.ipv4['nat'].add_chain('floating-ip-snat') self.ipv4['nat'].add_rule('SNATTING', '-j $floating-ip-snat') self.semaphore = semaphore.Semaphore() - iptables_manager.apply() + self.apply() def apply(self): """Apply the current in-memory set of iptables rules @@ -245,7 +282,7 @@ class IptablesManager(object): This will blow away any rules left over from previous runs of the same component of Nova, and replace them with our current set of rules. This happens atomically, thanks to iptables-restore. - + We wrap the call in a semaphore lock, so that we don't race with ourselves. In the event of a race with another component running an iptables-* command at the same time, we retry up to 5 times. @@ -267,6 +304,7 @@ class IptablesManager(object): attempts=5) def _modify_rules(self, current_lines, table, binary=None): + unwrapped_chains = table.unwrapped_chains chains = table.chains rules = table.rules @@ -284,11 +322,25 @@ class IptablesManager(object): if not rule.startswith(':'): break - new_filter[rules_index:rules_index] = [str(rule) for rule in rules] + new_filter[rules_index:rules_index] = [str(rule) for rule in rules + if rule.head or + str(rule) not in new_filter] + new_filter[rules_index:rules_index] = [':%s - [0:0]' % \ + (name,) \ + for name in unwrapped_chains] new_filter[rules_index:rules_index] = [':%s-%s - [0:0]' % \ (binary_name, name,) \ for name in chains] + seen_lines = set() + def _weed_out_duplicates(line): + if line in seen_lines: + return False + else: + seen_lines.add(line) + return True + + new_filter = filter(_weed_out_duplicates, new_filter) return new_filter diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index f1d4fe133..afd38272d 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -36,12 +36,22 @@ LOG = logging.getLogger('nova.tests.network') class IptablesManagerTestCase(test.TestCase): - sample_filter = """# Completed on Fri Feb 18 15:17:05 2011 -# Generated by iptables-save v1.4.10 on Fri Feb 18 15:17:05 2011 + sample_filter = """# Generated by iptables-save on Fri Feb 18 15:17:05 2011 *filter :INPUT ACCEPT [2223527:305688874] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [2172501:140856656] +:nova-compute-FORWARD - [0:0] +:nova-compute-INPUT - [0:0] +:nova-compute-local - [0:0] +:nova-compute-OUTPUT - [0:0] +:nova-filter-top - [0:0] +-A FORWARD -j nova-filter-top +-A OUTPUT -j nova-filter-top +-A nova-filter-top -j nova-compute-local +-A INPUT -j nova-compute-INPUT +-A OUTPUT -j nova-compute-OUTPUT +-A FORWARD -j nova-compute-FORWARD -A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT -A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT -A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT @@ -53,42 +63,116 @@ class IptablesManagerTestCase(test.TestCase): COMMIT # Completed on Fri Feb 18 15:17:05 2011""" + sample_nat = """# Generated by iptables-save on Fri Feb 18 15:17:05 2011 +*nat +:PREROUTING ACCEPT [3936:762355] +:INPUT ACCEPT [2447:225266] +:OUTPUT ACCEPT [63491:4191863] +:POSTROUTING ACCEPT [63112:4108641] +:nova-compute-OUTPUT - [0:0] +:nova-compute-floating-ip-snat - [0:0] +:nova-compute-SNATTING - [0:0] +:nova-compute-PREROUTING - [0:0] +:nova-compute-POSTROUTING - [0:0] +:nova-postrouting-bottom - [0:0] +-A PREROUTING -j nova-compute-PREROUTING +-A OUTPUT -j nova-compute-OUTPUT +-A POSTROUTING -j nova-compute-POSTROUTING +-A POSTROUTING -j nova-postrouting-bottom +-A nova-postrouting-bottom -j nova-compute-SNATTING +-A nova-compute-SNATTING -j nova-compute-floating-ip-snat +COMMIT +# Completed on Fri Feb 18 15:17:05 2011 +""" + def setUp(self): super(IptablesManagerTestCase, self).setUp() self.manager = linux_net.IptablesManager() - def test_rules_are_wrapped(self): + + def test_filter_rules_are_wrapped(self): current_lines = self.sample_filter.split('\n') table = self.manager.ipv4['filter'] table.add_rule('FORWARD', '-s 1.2.3.4/5 -j DROP') - new_lines = self.manager.modify_rules(current_lines, table) + new_lines = self.manager._modify_rules(current_lines, table) self.assertTrue('-A run_tests.py-FORWARD ' '-s 1.2.3.4/5 -j DROP' in new_lines) table.remove_rule('FORWARD', '-s 1.2.3.4/5 -j DROP') - new_lines = self.manager.modify_rules(current_lines, table) + new_lines = self.manager._modify_rules(current_lines, table) self.assertTrue('-A run_tests.py-FORWARD ' '-s 1.2.3.4/5 -j DROP' not in new_lines) - def test_wrapper_rules_in_place(self): - current_lines = self.sample_filter.split('\n') + def test_nat_rules(self): + current_lines = self.sample_nat.split('\n') + new_lines = self.manager._modify_rules(current_lines, + self.manager.ipv4['nat']) + + for line in [':nova-compute-OUTPUT - [0:0]', + ':nova-compute-floating-ip-snat - [0:0]', + ':nova-compute-SNATTING - [0:0]', + ':nova-compute-PREROUTING - [0:0]', + ':nova-compute-POSTROUTING - [0:0]']: + self.assertTrue(line in new_lines, "One of nova-compute's chains " + "went missing.") + + seen_lines = set() + for line in new_lines: + self.assertTrue(line not in seen_lines, + "Duplicate line: %s" % line) + seen_lines.add(line) - # TODO(soren): Add stuff for ipv6 - check_matrix = {4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD'], - 'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']}, - 6: {'filter': ['INPUT', 'OUTPUT', 'FORWARD']}} - - for ip_version in check_matrix: - ip = getattr(self.manager, 'ipv%d' % ip_version) - for table_name in ip: - table = ip[table_name] - new_lines = self.manager.modify_rules(current_lines, table) - for chain in check_matrix[ip_version][table_name]: - self.assertTrue(':run_tests.py-%s - [0:0]' % \ - (chain,) in new_lines) - self.assertTrue('-A %s -j run_tests.py-%s' % \ - (chain, chain) in new_lines) + last_postrouting_line = '' + + for line in new_lines: + if line.startswith('-A POSTROUTING'): + last_postrouting_line = line + + self.assertTrue('-j nova-postrouting-bottom' in last_postrouting_line, + "Last POSTROUTING rule does not jump to " + "nova-postouting-bottom: %s" % last_postrouting_line) + + for chain in ['POSTROUTING', 'PREROUTING', 'OUTPUT']: + self.assertTrue('-A %s -j run_tests.py-%s' \ + % (chain, chain) in new_lines, + "Built-in chain %s not wrapped" % (chain,)) + + + def test_filter_rules(self): + current_lines = self.sample_filter.split('\n') + new_lines = self.manager._modify_rules(current_lines, + self.manager.ipv4['filter']) + + for line in [':nova-compute-FORWARD - [0:0]', + ':nova-compute-INPUT - [0:0]', + ':nova-compute-local - [0:0]', + ':nova-compute-OUTPUT - [0:0]']: + self.assertTrue(line in new_lines, "One of nova-compute's chains" + " went missing.") + + seen_lines = set() + for line in new_lines: + self.assertTrue(line not in seen_lines, + "Duplicate line: %s" % line) + seen_lines.add(line) + + for chain in ['FORWARD', 'OUTPUT']: + for line in new_lines: + if line.startswith('-A %s' % chain): + self.assertTrue('-j nova-filter-top' in line, + "First %s rule does not " + "jump to nova-filter-top" % chain) + break + + self.assertTrue('-A nova-filter-top ' + '-j run_tests.py-local' in new_lines, + "nova-filter-top does not jump to wrapped local chain") + + for chain in ['INPUT', 'OUTPUT', 'FORWARD']: + self.assertTrue('-A %s -j run_tests.py-%s' \ + % (chain, chain) in new_lines, + "Built-in chain %s not wrapped" % (chain,)) class NetworkTestCase(test.TestCase): -- cgit From 54f2362d09393ad6ccdfee5689d4f547c69b3f42 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Tue, 22 Feb 2011 11:47:46 +0100 Subject: Remove leftover from debugging. --- nova/network/linux_net.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 2ccb5969e..5f480f633 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -274,8 +274,6 @@ class IptablesManager(object): self.semaphore = semaphore.Semaphore() - self.apply() - def apply(self): """Apply the current in-memory set of iptables rules -- cgit From b5e6601f76d64a96d6c7de5e9acdf5a8cf0fe8e9 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Tue, 22 Feb 2011 12:21:29 +0100 Subject: PEP8 adjustments. --- nova/network/linux_net.py | 9 +++++---- nova/tests/test_network.py | 10 ++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 5f480f633..7c4c16810 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -261,14 +261,14 @@ class IptablesManager(object): self.ipv4['nat'].add_rule('POSTROUTING', '-j nova-postrouting-bottom', wrap=False) - # We add a SNATTING chain to the shared nova-postrouting-bottom chain # so that it's applied last. self.ipv4['nat'].add_chain('SNATTING') - self.ipv4['nat'].add_rule('nova-postrouting-bottom', '-j $SNATTING', wrap=False) + self.ipv4['nat'].add_rule('nova-postrouting-bottom', '-j $SNATTING', + wrap=False) - # And then we add a floating-ip-snat chain and jump to first thing in the SNATTING - # chain. + # And then we add a floating-ip-snat chain and jump to first thing in + # the SNATTING chain. self.ipv4['nat'].add_chain('floating-ip-snat') self.ipv4['nat'].add_rule('SNATTING', '-j $floating-ip-snat') @@ -331,6 +331,7 @@ class IptablesManager(object): for name in chains] seen_lines = set() + def _weed_out_duplicates(line): if line in seen_lines: return False diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index afd38272d..2bdf3709e 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -89,7 +89,6 @@ COMMIT super(IptablesManagerTestCase, self).setUp() self.manager = linux_net.IptablesManager() - def test_filter_rules_are_wrapped(self): current_lines = self.sample_filter.split('\n') @@ -114,8 +113,8 @@ COMMIT ':nova-compute-SNATTING - [0:0]', ':nova-compute-PREROUTING - [0:0]', ':nova-compute-POSTROUTING - [0:0]']: - self.assertTrue(line in new_lines, "One of nova-compute's chains " - "went missing.") + self.assertTrue(line in new_lines, "One of nova-compute's chains " + "went missing.") seen_lines = set() for line in new_lines: @@ -138,7 +137,6 @@ COMMIT % (chain, chain) in new_lines, "Built-in chain %s not wrapped" % (chain,)) - def test_filter_rules(self): current_lines = self.sample_filter.split('\n') new_lines = self.manager._modify_rules(current_lines, @@ -148,8 +146,8 @@ COMMIT ':nova-compute-INPUT - [0:0]', ':nova-compute-local - [0:0]', ':nova-compute-OUTPUT - [0:0]']: - self.assertTrue(line in new_lines, "One of nova-compute's chains" - " went missing.") + self.assertTrue(line in new_lines, "One of nova-compute's chains" + " went missing.") seen_lines = set() for line in new_lines: -- cgit From 92a693b04feddad2420a835a12f53520c5529d8f Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Tue, 22 Feb 2011 12:42:38 +0100 Subject: floating-ip-snat was too long. Use floating-snat instead. --- nova/network/linux_net.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 7c4c16810..5e89a1df6 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -267,10 +267,10 @@ class IptablesManager(object): self.ipv4['nat'].add_rule('nova-postrouting-bottom', '-j $SNATTING', wrap=False) - # And then we add a floating-ip-snat chain and jump to first thing in + # And then we add a floating-snat chain and jump to first thing in # the SNATTING chain. - self.ipv4['nat'].add_chain('floating-ip-snat') - self.ipv4['nat'].add_rule('SNATTING', '-j $floating-ip-snat') + self.ipv4['nat'].add_chain('floating-snat') + self.ipv4['nat'].add_rule('SNATTING', '-j $floating-snat') self.semaphore = semaphore.Semaphore() @@ -420,7 +420,7 @@ def remove_floating_forward(floating_ip, fixed_ip): def floating_forward_rules(floating_ip, fixed_ip): return [("PREROUTING", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)), ("OUTPUT", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)), - ("floating-ip-snat", + ("floating-snat", "-s %s -j SNAT --to %s" % (fixed_ip, floating_ip))] -- cgit From 443f01ef7d977ba6ff86508b908f66733bdd989e Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Tue, 22 Feb 2011 14:32:20 +0100 Subject: Account for the fact that iptables-save outputs rules with a space at the end. Reverse the rule deduplication so that the last one takes precedence. --- nova/network/linux_net.py | 45 ++++++++++++++++++++++++++++++--------------- nova/tests/test_network.py | 42 ++++++++++++++++++++++-------------------- 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 5e89a1df6..849181641 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -72,22 +72,22 @@ class IptablesRule(object): You shouldn't need to use this class directly, it's only used by IptablesManager """ - def __init__(self, chain, rule, wrap=True, head=False): + def __init__(self, chain, rule, wrap=True, top=False): self.chain = chain self.rule = rule self.wrap = wrap - self.head = head + self.top = top def __eq__(self, other): return ((self.chain == other.chain) and (self.rule == other.rule) and - (self.head == other.head) and + (self.top == other.top) and (self.wrap == other.wrap)) def __ne__(self, other): return ((self.chain != other.chain) or (self.rule != other.rule) or - (self.head != other.head) or + (self.top != other.top) or (self.wrap != other.wrap)) def __str__(self): @@ -150,7 +150,7 @@ class IptablesTable(object): self.rules = filter(lambda r: jump_snippet not in r.rule, self.rules) - def add_rule(self, chain, rule, wrap=True, head=False): + def add_rule(self, chain, rule, wrap=True, top=False): """Add a rule to the table This is just like what you'd feed to iptables, just without @@ -166,14 +166,14 @@ class IptablesTable(object): if '$' in rule: rule = ' '.join(map(self._wrap_target_chain, rule.split(' '))) - self.rules.append(IptablesRule(chain, rule, wrap, head)) + self.rules.append(IptablesRule(chain, rule, wrap, top)) def _wrap_target_chain(self, s): if s.startswith('$'): return '%s-%s' % (binary_name, s[1:]) return s - def remove_rule(self, chain, rule, wrap=True, head=False): + def remove_rule(self, chain, rule, wrap=True, top=False): """Remove a rule from a chain Note: The rule must be exactly identical to the one that was added. @@ -181,12 +181,12 @@ class IptablesTable(object): CLI tool. """ try: - self.rules.remove(IptablesRule(chain, rule, wrap, head)) + self.rules.remove(IptablesRule(chain, rule, wrap, top)) except ValueError: LOG.debug(_("Tried to remove rule that wasn't there:" - " %(chain)r %(rule)r %(wrap)r %(head)r"), + " %(chain)r %(rule)r %(wrap)r %(top)r"), {'chain': chain, 'rule': rule, - 'head': head, 'wrap': wrap}) + 'top': top, 'wrap': wrap}) class IptablesManager(object): @@ -229,9 +229,9 @@ class IptablesManager(object): for tables in [self.ipv4, self.ipv6]: tables['filter'].add_chain('nova-filter-top', wrap=False) tables['filter'].add_rule('FORWARD', '-j nova-filter-top', - wrap=False, head=True) + wrap=False, top=True) tables['filter'].add_rule('OUTPUT', '-j nova-filter-top', - wrap=False, head=True) + wrap=False, top=True) tables['filter'].add_chain('local') tables['filter'].add_rule('nova-filter-top', '-j $local', @@ -320,9 +320,19 @@ class IptablesManager(object): if not rule.startswith(':'): break - new_filter[rules_index:rules_index] = [str(rule) for rule in rules - if rule.head or - str(rule) not in new_filter] + our_rules = [] + for rule in rules: + rule_str = str(rule) + if rule.top: + # rule.top == True means we want this rule to be at the top. + # Further down, we weed out duplicates from the bottom of the + # list, so here we remove the dupes ahead of time. + new_filter = filter(lambda s: s.strip() != rule_str.strip(), + new_filter) + our_rules += [rule_str] + + new_filter[rules_index:rules_index] = our_rules + new_filter[rules_index:rules_index] = [':%s - [0:0]' % \ (name,) \ for name in unwrapped_chains] @@ -333,13 +343,18 @@ class IptablesManager(object): seen_lines = set() def _weed_out_duplicates(line): + line = line.strip() if line in seen_lines: return False else: seen_lines.add(line) return True + # We filter duplicates, letting the *last* occurrence take + # precendence. + new_filter.reverse() new_filter = filter(_weed_out_duplicates, new_filter) + new_filter.reverse() return new_filter diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 2bdf3709e..b1d70e323 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -46,20 +46,20 @@ class IptablesManagerTestCase(test.TestCase): :nova-compute-local - [0:0] :nova-compute-OUTPUT - [0:0] :nova-filter-top - [0:0] --A FORWARD -j nova-filter-top --A OUTPUT -j nova-filter-top --A nova-filter-top -j nova-compute-local --A INPUT -j nova-compute-INPUT --A OUTPUT -j nova-compute-OUTPUT --A FORWARD -j nova-compute-FORWARD --A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT --A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT --A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT --A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT --A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT --A FORWARD -i virbr0 -o virbr0 -j ACCEPT --A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable --A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable +-A FORWARD -j nova-filter-top +-A OUTPUT -j nova-filter-top +-A nova-filter-top -j nova-compute-local +-A INPUT -j nova-compute-INPUT +-A OUTPUT -j nova-compute-OUTPUT +-A FORWARD -j nova-compute-FORWARD +-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT +-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT +-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT +-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT +-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT +-A FORWARD -i virbr0 -o virbr0 -j ACCEPT +-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable +-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable COMMIT # Completed on Fri Feb 18 15:17:05 2011""" @@ -75,12 +75,12 @@ COMMIT :nova-compute-PREROUTING - [0:0] :nova-compute-POSTROUTING - [0:0] :nova-postrouting-bottom - [0:0] --A PREROUTING -j nova-compute-PREROUTING --A OUTPUT -j nova-compute-OUTPUT --A POSTROUTING -j nova-compute-POSTROUTING --A POSTROUTING -j nova-postrouting-bottom --A nova-postrouting-bottom -j nova-compute-SNATTING --A nova-compute-SNATTING -j nova-compute-floating-ip-snat +-A PREROUTING -j nova-compute-PREROUTING +-A OUTPUT -j nova-compute-OUTPUT +-A POSTROUTING -j nova-compute-POSTROUTING +-A POSTROUTING -j nova-postrouting-bottom +-A nova-postrouting-bottom -j nova-compute-SNATTING +-A nova-compute-SNATTING -j nova-compute-floating-ip-snat COMMIT # Completed on Fri Feb 18 15:17:05 2011 """ @@ -118,6 +118,7 @@ COMMIT seen_lines = set() for line in new_lines: + line = line.strip() self.assertTrue(line not in seen_lines, "Duplicate line: %s" % line) seen_lines.add(line) @@ -151,6 +152,7 @@ COMMIT seen_lines = set() for line in new_lines: + line = line.strip() self.assertTrue(line not in seen_lines, "Duplicate line: %s" % line) seen_lines.add(line) -- cgit From 7eee81ee6480a36b179ae26be88ebad9415c4b62 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Tue, 22 Feb 2011 14:40:00 +0100 Subject: PEP8 again --- nova/tests/test_network.py | 103 +++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index b1d70e323..d3a23abf4 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -36,61 +36,62 @@ LOG = logging.getLogger('nova.tests.network') class IptablesManagerTestCase(test.TestCase): - sample_filter = """# Generated by iptables-save on Fri Feb 18 15:17:05 2011 -*filter -:INPUT ACCEPT [2223527:305688874] -:FORWARD ACCEPT [0:0] -:OUTPUT ACCEPT [2172501:140856656] -:nova-compute-FORWARD - [0:0] -:nova-compute-INPUT - [0:0] -:nova-compute-local - [0:0] -:nova-compute-OUTPUT - [0:0] -:nova-filter-top - [0:0] --A FORWARD -j nova-filter-top --A OUTPUT -j nova-filter-top --A nova-filter-top -j nova-compute-local --A INPUT -j nova-compute-INPUT --A OUTPUT -j nova-compute-OUTPUT --A FORWARD -j nova-compute-FORWARD --A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT --A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT --A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT --A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT --A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT --A FORWARD -i virbr0 -o virbr0 -j ACCEPT --A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable --A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable -COMMIT -# Completed on Fri Feb 18 15:17:05 2011""" - - sample_nat = """# Generated by iptables-save on Fri Feb 18 15:17:05 2011 -*nat -:PREROUTING ACCEPT [3936:762355] -:INPUT ACCEPT [2447:225266] -:OUTPUT ACCEPT [63491:4191863] -:POSTROUTING ACCEPT [63112:4108641] -:nova-compute-OUTPUT - [0:0] -:nova-compute-floating-ip-snat - [0:0] -:nova-compute-SNATTING - [0:0] -:nova-compute-PREROUTING - [0:0] -:nova-compute-POSTROUTING - [0:0] -:nova-postrouting-bottom - [0:0] --A PREROUTING -j nova-compute-PREROUTING --A OUTPUT -j nova-compute-OUTPUT --A POSTROUTING -j nova-compute-POSTROUTING --A POSTROUTING -j nova-postrouting-bottom --A nova-postrouting-bottom -j nova-compute-SNATTING --A nova-compute-SNATTING -j nova-compute-floating-ip-snat -COMMIT -# Completed on Fri Feb 18 15:17:05 2011 -""" + sample_filter = ['#Generated by iptables-save on Fri Feb 18 15:17:05 2011', + '*filter', + ':INPUT ACCEPT [2223527:305688874]', + ':FORWARD ACCEPT [0:0]', + ':OUTPUT ACCEPT [2172501:140856656]', + ':nova-compute-FORWARD - [0:0]', + ':nova-compute-INPUT - [0:0]', + ':nova-compute-local - [0:0]', + ':nova-compute-OUTPUT - [0:0]', + ':nova-filter-top - [0:0]', + '-A FORWARD -j nova-filter-top ', + '-A OUTPUT -j nova-filter-top ', + '-A nova-filter-top -j nova-compute-local ', + '-A INPUT -j nova-compute-INPUT ', + '-A OUTPUT -j nova-compute-OUTPUT ', + '-A FORWARD -j nova-compute-FORWARD ', + '-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT ', + '-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT ', + '-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT ', + '-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT ', + '-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT ', + '-A FORWARD -i virbr0 -o virbr0 -j ACCEPT ', + '-A FORWARD -o virbr0 -j REJECT --reject-with ' + 'icmp-port-unreachable ', + '-A FORWARD -i virbr0 -j REJECT --reject-with ' + 'icmp-port-unreachable ', + 'COMMIT', + '# Completed on Fri Feb 18 15:17:05 2011'] + + sample_nat = ['# Generated by iptables-save on Fri Feb 18 15:17:05 2011', + '*nat', + ':PREROUTING ACCEPT [3936:762355]', + ':INPUT ACCEPT [2447:225266]', + ':OUTPUT ACCEPT [63491:4191863]', + ':POSTROUTING ACCEPT [63112:4108641]', + ':nova-compute-OUTPUT - [0:0]', + ':nova-compute-floating-ip-snat - [0:0]', + ':nova-compute-SNATTING - [0:0]', + ':nova-compute-PREROUTING - [0:0]', + ':nova-compute-POSTROUTING - [0:0]', + ':nova-postrouting-bottom - [0:0]', + '-A PREROUTING -j nova-compute-PREROUTING ', + '-A OUTPUT -j nova-compute-OUTPUT ', + '-A POSTROUTING -j nova-compute-POSTROUTING ', + '-A POSTROUTING -j nova-postrouting-bottom ', + '-A nova-postrouting-bottom -j nova-compute-SNATTING ', + '-A nova-compute-SNATTING -j nova-compute-floating-ip-snat ', + 'COMMIT', + '# Completed on Fri Feb 18 15:17:05 2011'] def setUp(self): super(IptablesManagerTestCase, self).setUp() self.manager = linux_net.IptablesManager() def test_filter_rules_are_wrapped(self): - current_lines = self.sample_filter.split('\n') + current_lines = self.sample_filter table = self.manager.ipv4['filter'] table.add_rule('FORWARD', '-s 1.2.3.4/5 -j DROP') @@ -104,7 +105,7 @@ COMMIT '-s 1.2.3.4/5 -j DROP' not in new_lines) def test_nat_rules(self): - current_lines = self.sample_nat.split('\n') + current_lines = self.sample_nat new_lines = self.manager._modify_rules(current_lines, self.manager.ipv4['nat']) @@ -139,7 +140,7 @@ COMMIT "Built-in chain %s not wrapped" % (chain,)) def test_filter_rules(self): - current_lines = self.sample_filter.split('\n') + current_lines = self.sample_filter new_lines = self.manager._modify_rules(current_lines, self.manager.ipv4['filter']) -- cgit From 9e2942931b5381d3ba0e8cc4f9846160b003f45b Mon Sep 17 00:00:00 2001 From: Thierry Carrez <thierry@openstack.org> Date: Tue, 22 Feb 2011 17:18:04 +0100 Subject: Get rid of nova-combined, see rationale on ML --- bin/nova-combined | 83 ------------------------------------------------------- 1 file changed, 83 deletions(-) delete mode 100755 bin/nova-combined diff --git a/bin/nova-combined b/bin/nova-combined deleted file mode 100755 index 22f0d5cb7..000000000 --- a/bin/nova-combined +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python -# 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. - -"""Combined starter script for Nova services.""" - -import eventlet -eventlet.monkey_patch() - -import gettext -import os -import sys - -# If ../nova/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): - sys.path.insert(0, possible_topdir) - -gettext.install('nova', unicode=1) - -from nova import flags -from nova import log as logging -from nova import service -from nova import utils -from nova import wsgi - - -FLAGS = flags.FLAGS - -API_ENDPOINTS = ['ec2', 'osapi'] - -for api in API_ENDPOINTS: - flags.DEFINE_string("%s_listen" % api, "0.0.0.0", - "IP address to listen to for API %s" % api) - flags.DEFINE_integer("%s_listen_port" % api, - getattr(FLAGS, "%s_port" % api), - "Port to listen to for API %s" % api) - -if __name__ == '__main__': - utils.default_flagfile() - FLAGS(sys.argv) - logging.setup() - - compute = service.Service.create(binary='nova-compute') - network = service.Service.create(binary='nova-network') - volume = service.Service.create(binary='nova-volume') - scheduler = service.Service.create(binary='nova-scheduler') - #objectstore = service.Service.create(binary='nova-objectstore') - - service.serve(compute, network, volume, scheduler) - - apps = [] - paste_config_file = wsgi.paste_config_file('nova-api.conf') - for api in API_ENDPOINTS: - config = wsgi.load_paste_configuration(paste_config_file, api) - if config is None: - continue - app = wsgi.load_paste_app(paste_config_file, api) - apps.append((app, getattr(FLAGS, "%s_listen_port" % api), - getattr(FLAGS, "%s_listen" % api))) - if len(apps) > 0: - server = wsgi.Server() - for app in apps: - server.start(*app) - server.wait() -- cgit From 912e762c9baf3cb17a24bc0d9feba4b26892dbbc Mon Sep 17 00:00:00 2001 From: Thierry Carrez <thierry@openstack.org> Date: Tue, 22 Feb 2011 17:37:12 +0100 Subject: Also remove nova-combined from setup.py --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 4ab8f386b..3b48990ac 100644 --- a/setup.py +++ b/setup.py @@ -98,7 +98,6 @@ DistUtilsExtra.auto.setup(name='nova', test_suite='nose.collector', scripts=['bin/nova-ajax-console-proxy', 'bin/nova-api', - 'bin/nova-combined', 'bin/nova-compute', 'bin/nova-console', 'bin/nova-dhcpbridge', -- cgit From 8b0e8b155eab313e0caece48eee609d12df5e5d4 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Tue, 22 Feb 2011 20:45:36 +0100 Subject: Rename "SNATTING" chain to "snat". --- nova/network/linux_net.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 849181641..34337901a 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -208,7 +208,7 @@ class IptablesManager(object): For ipv4, the builtin PREROUTING, OUTPUT, and POSTROUTING nat chains are wrapped in the same was as the builtin filter chains. Additionally, there's - a SNATTING chain that is applied after the POSTROUTING chain. + a snat chain that is applied after the POSTROUTING chain. """ def __init__(self, execute=None): if not execute: @@ -261,16 +261,16 @@ class IptablesManager(object): self.ipv4['nat'].add_rule('POSTROUTING', '-j nova-postrouting-bottom', wrap=False) - # We add a SNATTING chain to the shared nova-postrouting-bottom chain + # We add a snat chain to the shared nova-postrouting-bottom chain # so that it's applied last. - self.ipv4['nat'].add_chain('SNATTING') - self.ipv4['nat'].add_rule('nova-postrouting-bottom', '-j $SNATTING', + self.ipv4['nat'].add_chain('snat') + self.ipv4['nat'].add_rule('nova-postrouting-bottom', '-j $snat', wrap=False) # And then we add a floating-snat chain and jump to first thing in - # the SNATTING chain. + # the snat chain. self.ipv4['nat'].add_chain('floating-snat') - self.ipv4['nat'].add_rule('SNATTING', '-j $floating-snat') + self.ipv4['nat'].add_rule('snat', '-j $floating-snat') self.semaphore = semaphore.Semaphore() @@ -376,7 +376,7 @@ def init_host(): # NOTE(devcamcar): Cloud public SNAT entries and the default # SNAT rule for outbound traffic. - iptables_manager.ipv4['nat'].add_rule("SNATTING", + iptables_manager.ipv4['nat'].add_rule("snat", "-s %s -j SNAT --to-source %s" % \ (FLAGS.fixed_range, FLAGS.routing_source_ip)) -- cgit From f4289df0e58080d6d9fa381915bbd0d29f3b9751 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 22 Feb 2011 15:05:48 -0800 Subject: We're not using prefix matching on AMQP, so fakerabbit shouldn't be doing it! --- nova/fakerabbit.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/fakerabbit.py b/nova/fakerabbit.py index dd82a9366..a7dee8caf 100644 --- a/nova/fakerabbit.py +++ b/nova/fakerabbit.py @@ -48,7 +48,6 @@ class Exchange(object): nm = self.name LOG.debug(_('(%(nm)s) publish (key: %(routing_key)s)' ' %(message)s') % locals()) - routing_key = routing_key.split('.')[0] if routing_key in self._routes: for f in self._routes[routing_key]: LOG.debug(_('Publishing to route %s'), f) -- cgit From e8a3f461319c1ee20a18f3a375af5e1e958af05e Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 22 Feb 2011 15:50:42 -0800 Subject: Missing import for nova.exceptions (!) --- nova/network/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/network/api.py b/nova/network/api.py index bf43acb51..4ee1148cb 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -21,6 +21,7 @@ Handles all requests relating to instances (guest vms). """ from nova import db +from nova import exception from nova import flags from nova import log as logging from nova import quota -- cgit From ab6b11b0399655ccdd9619be00470eda464cf2a7 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 22 Feb 2011 16:09:41 -0800 Subject: Don't blindly concatenate queue name if second portiion is None --- nova/db/sqlalchemy/api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 2697fac73..009ed1f06 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1209,7 +1209,9 @@ def project_get_network_v6(context, project_id): def queue_get_for(_context, topic, physical_node_id): # FIXME(ja): this should be servername? - return "%s.%s" % (topic, physical_node_id) + if physical_node_id: + return "%s.%s" % (topic, physical_node_id) + return topic ################### -- cgit From 5b2ec209d07d7df45f9b7ca6eebfcbc9443de94e Mon Sep 17 00:00:00 2001 From: termie <github@anarkystic.com> Date: Tue, 22 Feb 2011 17:10:34 -0800 Subject: don't make a syslog handler if we didn't ask for one --- nova/log.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/log.py b/nova/log.py index 10c14d74b..591d26c63 100644 --- a/nova/log.py +++ b/nova/log.py @@ -236,16 +236,17 @@ class NovaRootLogger(NovaLogger): def __init__(self, name, level=NOTSET): self.logpath = None self.filelog = None - self.syslog = SysLogHandler(address='/dev/log') self.streamlog = StreamHandler() + self.syslog = None NovaLogger.__init__(self, name, level) def setup_from_flags(self): """Setup logger from flags""" global _filelog if FLAGS.use_syslog: + self.syslog = SysLogHandler(address='/dev/log') self.addHandler(self.syslog) - else: + elif self.syslog: self.removeHandler(self.syslog) logpath = _get_log_file_path() if logpath: -- cgit From 18793c2e184713d33bc93306d464cf443584ffd6 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Tue, 22 Feb 2011 17:44:07 -0800 Subject: test that shows error on filtering groups --- nova/tests/test_cloud.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 445cc6e8b..2bce64353 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -133,6 +133,20 @@ class CloudTestCase(test.TestCase): db.instance_destroy(self.context, inst['id']) db.floating_ip_destroy(self.context, address) + def test_describe_security_groups(self): + """Makes sure describe_security_groups works and filters results.""" + sec = db.security_group_create(self.context, {'name': 'test'}) + result = self.cloud.describe_security_groups(self.context) + # NOTE(vish): should have the default group as well + self.assertEqual(len(result['securityGroupInfo']), 2) + result = self.cloud.describe_security_groups(self.context, + group_name=[sec['name']]) + self.assertEqual(len(result['securityGroupInfo']), 1) + self.assertEqual( + cloud.ec2_id_to_id(result['securityGroupInfo'][0]['name']), + sec['name']) + db.security_group_destroy(self.context, sec['id']) + def test_describe_volumes(self): """Makes sure describe_volumes works and filters results.""" vol1 = db.volume_create(self.context, {}) @@ -286,19 +300,6 @@ class CloudTestCase(test.TestCase): LOG.debug(_("Terminating instance %s"), instance_id) rv = self.compute.terminate_instance(instance_id) - def test_describe_instances(self): - """Makes sure describe_instances works.""" - instance1 = db.instance_create(self.context, {'host': 'host2'}) - comp1 = db.service_create(self.context, {'host': 'host2', - 'availability_zone': 'zone1', - 'topic': "compute"}) - result = self.cloud.describe_instances(self.context) - self.assertEqual(result['reservationSet'][0] - ['instancesSet'][0] - ['placement']['availabilityZone'], 'zone1') - db.instance_destroy(self.context, instance1['id']) - db.service_destroy(self.context, comp1['id']) - def test_instance_update_state(self): # TODO(termie): what is this code even testing? def instance(num): -- cgit From b6254db80ca9841361775a92b85f88db7251f857 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 22 Feb 2011 17:45:38 -0800 Subject: Refactoring nova-api to be a service, so that we can reuse it in tests --- bin/nova-api | 50 ++++---------------------------- nova/apiservice.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 45 deletions(-) create mode 100644 nova/apiservice.py diff --git a/bin/nova-api b/bin/nova-api index d5efb4687..99417e6c6 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -36,57 +36,17 @@ gettext.install('nova', unicode=1) from nova import flags from nova import log as logging -from nova import version +from nova import apiservice +from nova import utils from nova import wsgi -LOG = logging.getLogger('nova.api') - FLAGS = flags.FLAGS -flags.DEFINE_string('ec2_listen', "0.0.0.0", - 'IP address for EC2 API to listen') -flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen') -flags.DEFINE_string('osapi_listen', "0.0.0.0", - 'IP address for OpenStack API to listen') -flags.DEFINE_integer('osapi_listen_port', 8774, 'port for os api to listen') - -API_ENDPOINTS = ['ec2', 'osapi'] - - -def run_app(paste_config_file): - LOG.debug(_("Using paste.deploy config at: %s"), paste_config_file) - apps = [] - for api in API_ENDPOINTS: - config = wsgi.load_paste_configuration(paste_config_file, api) - if config is None: - LOG.debug(_("No paste configuration for app: %s"), api) - continue - LOG.debug(_("App Config: %(api)s\n%(config)r") % locals()) - LOG.info(_("Running %s API"), api) - app = wsgi.load_paste_app(paste_config_file, api) - apps.append((app, getattr(FLAGS, "%s_listen_port" % api), - getattr(FLAGS, "%s_listen" % api))) - if len(apps) == 0: - LOG.error(_("No known API applications configured in %s."), - paste_config_file) - return - - server = wsgi.Server() - for app in apps: - server.start(*app) - server.wait() - if __name__ == '__main__': FLAGS(sys.argv) logging.setup() - LOG.audit(_("Starting nova-api node (version %s)"), - version.version_string_with_vcs()) - LOG.debug(_("Full set of FLAGS:")) - for flag in FLAGS: - flag_get = FLAGS.get(flag, None) - LOG.debug("%(flag)s : %(flag_get)s" % locals()) conf = wsgi.paste_config_file('nova-api.conf') - if conf: - run_app(conf) - else: + if not conf: LOG.error(_("No paste configuration found for: %s"), 'nova-api.conf') + else: + apiservice.serve(conf) diff --git a/nova/apiservice.py b/nova/apiservice.py new file mode 100644 index 000000000..7b453e19f --- /dev/null +++ b/nova/apiservice.py @@ -0,0 +1,85 @@ +# 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. + +""" +Wrapper for API service, makes it look more like the non-WSGI services +""" + +from nova import flags +from nova import log as logging +from nova import version +from nova import wsgi + + +LOG = logging.getLogger('nova.api') + +FLAGS = flags.FLAGS +flags.DEFINE_string('ec2_listen', "0.0.0.0", + 'IP address for EC2 API to listen') +flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen') +flags.DEFINE_string('osapi_listen', "0.0.0.0", + 'IP address for OpenStack API to listen') +flags.DEFINE_integer('osapi_listen_port', 8774, 'port for os api to listen') + +API_ENDPOINTS = ['ec2', 'osapi'] + + +def _run_app(paste_config_file): + LOG.debug(_("Using paste.deploy config at: %s"), paste_config_file) + apps = [] + for api in API_ENDPOINTS: + config = wsgi.load_paste_configuration(paste_config_file, api) + if config is None: + LOG.debug(_("No paste configuration for app: %s"), api) + continue + LOG.debug(_("App Config: %(api)s\n%(config)r") % locals()) + LOG.info(_("Running %s API"), api) + app = wsgi.load_paste_app(paste_config_file, api) + apps.append((app, getattr(FLAGS, "%s_listen_port" % api), + getattr(FLAGS, "%s_listen" % api))) + if len(apps) == 0: + LOG.error(_("No known API applications configured in %s."), + paste_config_file) + return + + server = wsgi.Server() + for app in apps: + server.start(*app) + server.wait() + + +class ApiService(object): + """Base class for workers that run on hosts.""" + + def __init__(self, conf): + self.conf = conf + + def start(self): + _run_app(self.conf) + + +def serve(conf): + LOG.audit(_("Starting nova-api node (version %s)"), + version.version_string_with_vcs()) + LOG.debug(_("Full set of FLAGS:")) + for flag in FLAGS: + flag_get = FLAGS.get(flag, None) + LOG.debug("%(flag)s : %(flag_get)s" % locals()) + + service = ApiService(conf) + service.start() -- cgit From 2610a522d26351686612058a6da0300bce731112 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Tue, 22 Feb 2011 17:49:38 -0800 Subject: fix test --- nova/tests/test_cloud.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 2bce64353..afdbb80a9 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -135,7 +135,9 @@ class CloudTestCase(test.TestCase): def test_describe_security_groups(self): """Makes sure describe_security_groups works and filters results.""" - sec = db.security_group_create(self.context, {'name': 'test'}) + sec = db.security_group_create(self.context, + {'project_id': self.context.project_id, + 'name': 'test'}) result = self.cloud.describe_security_groups(self.context) # NOTE(vish): should have the default group as well self.assertEqual(len(result['securityGroupInfo']), 2) @@ -143,7 +145,7 @@ class CloudTestCase(test.TestCase): group_name=[sec['name']]) self.assertEqual(len(result['securityGroupInfo']), 1) self.assertEqual( - cloud.ec2_id_to_id(result['securityGroupInfo'][0]['name']), + result['securityGroupInfo'][0]['groupName'], sec['name']) db.security_group_destroy(self.context, sec['id']) -- cgit From 79f6c437b486262bab3faacb59197a5cae30b2bd Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 22 Feb 2011 17:50:26 -0800 Subject: Added create static method to ApiService --- nova/apiservice.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/nova/apiservice.py b/nova/apiservice.py index 7b453e19f..1914b9e59 100644 --- a/nova/apiservice.py +++ b/nova/apiservice.py @@ -72,6 +72,11 @@ class ApiService(object): def start(self): _run_app(self.conf) + @staticmethod + def create(): + conf = wsgi.paste_config_file('nova-api.conf') + return serve(conf) + def serve(conf): LOG.audit(_("Starting nova-api node (version %s)"), @@ -83,3 +88,5 @@ def serve(conf): service = ApiService(conf) service.start() + + return service -- cgit From e37e7b91a9fb5664ad50c1ff38e95f1a2d655c06 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 22 Feb 2011 17:58:01 -0800 Subject: Support service-like wait behaviour for API service --- bin/nova-api | 3 ++- nova/apiservice.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/bin/nova-api b/bin/nova-api index 99417e6c6..ccb7701ae 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -49,4 +49,5 @@ if __name__ == '__main__': if not conf: LOG.error(_("No paste configuration found for: %s"), 'nova-api.conf') else: - apiservice.serve(conf) + service = apiservice.serve(conf) + service.wait() diff --git a/nova/apiservice.py b/nova/apiservice.py index 1914b9e59..14239f196 100644 --- a/nova/apiservice.py +++ b/nova/apiservice.py @@ -60,7 +60,7 @@ def _run_app(paste_config_file): server = wsgi.Server() for app in apps: server.start(*app) - server.wait() + return server class ApiService(object): @@ -68,9 +68,13 @@ class ApiService(object): def __init__(self, conf): self.conf = conf + self.wsgi_app = None def start(self): - _run_app(self.conf) + self.wsgi_app = _run_app(self.conf) + + def wait(self): + self.wsgi_app.wait() @staticmethod def create(): -- cgit From 828e3ea3f29f57767a4e25ad40b275c886cb7968 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Tue, 22 Feb 2011 18:02:25 -0800 Subject: fix and optimize security group filtering --- nova/api/ec2/cloud.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 882cdcfc9..fc9c13d91 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -318,14 +318,19 @@ class CloudController(object): def describe_security_groups(self, context, group_name=None, **kwargs): self.compute_api.ensure_default_security_group(context) - if context.is_admin: + if group_name: + groups = [] + for name in group_name: + group = db.security_group_get_by_name(context, + context.project_id, + name) + groups.append(group) + elif context.is_admin: groups = db.security_group_get_all(context) else: groups = db.security_group_get_by_project(context, context.project_id) groups = [self._format_security_group(context, g) for g in groups] - if not group_name is None: - groups = [g for g in groups if g.name in group_name] return {'securityGroupInfo': list(sorted(groups, -- cgit From 2fd33bdd50b933dc14fea065c823f5a73324129b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Tue, 22 Feb 2011 18:04:08 -0800 Subject: separate out smoketests and add updated nova.sh --- contrib/nova.sh | 12 +- smoketests/base.py | 12 + smoketests/public_network_smoketests.py | 11 +- smoketests/sysadmin_smoketests.py | 293 +++++++++++++++++++++++ smoketests/user_smoketests.py | 397 -------------------------------- 5 files changed, 321 insertions(+), 404 deletions(-) create mode 100644 smoketests/sysadmin_smoketests.py delete mode 100644 smoketests/user_smoketests.py diff --git a/contrib/nova.sh b/contrib/nova.sh index 9259035ca..1187f2728 100755 --- a/contrib/nova.sh +++ b/contrib/nova.sh @@ -66,7 +66,7 @@ if [ "$CMD" == "install" ]; then sudo apt-get install -y user-mode-linux kvm libvirt-bin sudo apt-get install -y screen euca2ools vlan curl rabbitmq-server sudo apt-get install -y lvm2 iscsitarget open-iscsi - sudo apt-get install -y socat + sudo apt-get install -y socat unzip echo "ISCSITARGET_ENABLE=true" | sudo tee /etc/default/iscsitarget sudo /etc/init.d/iscsitarget restart sudo modprobe kvm @@ -111,8 +111,7 @@ if [ "$CMD" == "run" ]; then --nodaemon --dhcpbridge_flagfile=$NOVA_DIR/bin/nova.conf --network_manager=nova.network.manager.$NET_MAN ---cc_host=$HOST_IP ---routing_source_ip=$HOST_IP +--my_ip=$HOST_IP --sql_connection=$SQL_CONN --auth_driver=nova.auth.$AUTH --libvirt_type=$LIBVIRT_TYPE @@ -151,7 +150,6 @@ NOVA_CONF_EOF mkdir -p $NOVA_DIR/instances rm -rf $NOVA_DIR/networks mkdir -p $NOVA_DIR/networks - $NOVA_DIR/tools/clean-vlans if [ ! -d "$NOVA_DIR/images" ]; then ln -s $DIR/images $NOVA_DIR/images fi @@ -169,10 +167,14 @@ NOVA_CONF_EOF # create a project called 'admin' with project manager of 'admin' $NOVA_DIR/bin/nova-manage project create admin admin # export environment variables for project 'admin' and user 'admin' - $NOVA_DIR/bin/nova-manage project environment admin admin $NOVA_DIR/novarc + $NOVA_DIR/bin/nova-manage project zipfile admin admin $NOVA_DIR/nova.zip + unzip -o $NOVA_DIR/nova.zip -d $NOVA_DIR/ # create a small network $NOVA_DIR/bin/nova-manage network create 10.0.0.0/8 1 32 + # create some floating ips + $NOVA_DIR/bin/nova-manage floating create `hostname` 10.6.0.0/27 + # nova api crashes if we start it with a regular screen command, # so send the start command by forcing text into the window. screen_it api "$NOVA_DIR/bin/nova-api" diff --git a/smoketests/base.py b/smoketests/base.py index afc618074..204b4a1eb 100644 --- a/smoketests/base.py +++ b/smoketests/base.py @@ -28,7 +28,9 @@ from boto.ec2.regioninfo import RegionInfo from smoketests import flags +SUITE_NAMES = '[image, instance, volume]' FLAGS = flags.FLAGS +flags.DEFINE_string('suite', None, 'Specific test suite to run ' + SUITE_NAMES) boto_v6 = None @@ -173,6 +175,16 @@ class SmokeTestCase(unittest.TestCase): return True +TEST_DATA = {} + + +class UserSmokeTestCase(SmokeTestCase): + def setUp(self): + global TEST_DATA + self.conn = self.connection_for_env() + self.data = TEST_DATA + + def run_tests(suites): argv = FLAGS(sys.argv) if FLAGS.use_ipv6: diff --git a/smoketests/public_network_smoketests.py b/smoketests/public_network_smoketests.py index bfc2b20ba..5a4c67642 100644 --- a/smoketests/public_network_smoketests.py +++ b/smoketests/public_network_smoketests.py @@ -24,9 +24,16 @@ import sys import time import unittest +# If ../nova/__init__.py exists, add ../ to Python search path, so that +# it will override what happens to be installed in /usr/(local/)lib/python... +possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), + os.pardir, + os.pardir)) +if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): + sys.path.insert(0, possible_topdir) + from smoketests import flags from smoketests import base -from smoketests import user_smoketests #Note that this test should run from #public network (outside of private network segments) @@ -42,7 +49,7 @@ TEST_KEY2 = '%s_key2' % TEST_PREFIX TEST_DATA = {} -class InstanceTestsFromPublic(user_smoketests.UserSmokeTestCase): +class InstanceTestsFromPublic(base.UserSmokeTestCase): def test_001_can_create_keypair(self): key = self.create_key_pair(self.conn, TEST_KEY) self.assertEqual(key.name, TEST_KEY) diff --git a/smoketests/sysadmin_smoketests.py b/smoketests/sysadmin_smoketests.py new file mode 100644 index 000000000..e3b84d3d3 --- /dev/null +++ b/smoketests/sysadmin_smoketests.py @@ -0,0 +1,293 @@ +# 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. + +import commands +import os +import random +import sys +import time +import unittest + +# If ../nova/__init__.py exists, add ../ to Python search path, so that +# it will override what happens to be installed in /usr/(local/)lib/python... +possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), + os.pardir, + os.pardir)) +if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): + sys.path.insert(0, possible_topdir) + +from smoketests import flags +from smoketests import base + + + +FLAGS = flags.FLAGS +flags.DEFINE_string('bundle_kernel', 'openwrt-x86-vmlinuz', + 'Local kernel file to use for bundling tests') +flags.DEFINE_string('bundle_image', 'openwrt-x86-ext2.image', + 'Local image file to use for bundling tests') + +TEST_PREFIX = 'test%s' % int(random.random() * 1000000) +TEST_BUCKET = '%s_bucket' % TEST_PREFIX +TEST_KEY = '%s_key' % TEST_PREFIX +TEST_GROUP = '%s_group' % TEST_PREFIX +class ImageTests(base.UserSmokeTestCase): + def test_001_can_bundle_image(self): + self.assertTrue(self.bundle_image(FLAGS.bundle_image)) + + def test_002_can_upload_image(self): + self.assertTrue(self.upload_image(TEST_BUCKET, FLAGS.bundle_image)) + + def test_003_can_register_image(self): + image_id = self.conn.register_image('%s/%s.manifest.xml' % + (TEST_BUCKET, FLAGS.bundle_image)) + self.assert_(image_id is not None) + self.data['image_id'] = image_id + + def test_004_can_bundle_kernel(self): + self.assertTrue(self.bundle_image(FLAGS.bundle_kernel, kernel=True)) + + def test_005_can_upload_kernel(self): + self.assertTrue(self.upload_image(TEST_BUCKET, FLAGS.bundle_kernel)) + + def test_006_can_register_kernel(self): + kernel_id = self.conn.register_image('%s/%s.manifest.xml' % + (TEST_BUCKET, FLAGS.bundle_kernel)) + self.assert_(kernel_id is not None) + self.data['kernel_id'] = kernel_id + + def test_007_images_are_available_within_10_seconds(self): + for i in xrange(10): + image = self.conn.get_image(self.data['image_id']) + if image and image.state == 'available': + break + time.sleep(1) + else: + self.assert_(False) # wasn't available within 10 seconds + self.assert_(image.type == 'machine') + + for i in xrange(10): + kernel = self.conn.get_image(self.data['kernel_id']) + if kernel and kernel.state == 'available': + break + time.sleep(1) + else: + self.assert_(False) # wasn't available within 10 seconds + self.assert_(kernel.type == 'kernel') + + def test_008_can_describe_image_attribute(self): + attrs = self.conn.get_image_attribute(self.data['image_id'], + 'launchPermission') + self.assert_(attrs.name, 'launch_permission') + + def test_009_can_modify_image_launch_permission(self): + self.conn.modify_image_attribute(image_id=self.data['image_id'], + operation='add', + attribute='launchPermission', + groups='all') + image = self.conn.get_image(self.data['image_id']) + self.assertEqual(image.id, self.data['image_id']) + + def test_010_can_see_launch_permission(self): + attrs = self.conn.get_image_attribute(self.data['image_id'], + 'launchPermission') + self.assert_(attrs.name, 'launch_permission') + self.assert_(attrs.attrs['groups'][0], 'all') + + def test_011_user_can_deregister_kernel(self): + self.assertTrue(self.conn.deregister_image(self.data['kernel_id'])) + + def test_012_can_deregister_image(self): + self.assertTrue(self.conn.deregister_image(self.data['image_id'])) + + def test_013_can_delete_bundle(self): + self.assertTrue(self.delete_bundle_bucket(TEST_BUCKET)) + + +class InstanceTests(base.UserSmokeTestCase): + def test_001_can_create_keypair(self): + key = self.create_key_pair(self.conn, TEST_KEY) + self.assertEqual(key.name, TEST_KEY) + + def test_002_can_create_instance_with_keypair(self): + reservation = self.conn.run_instances(FLAGS.test_image, + key_name=TEST_KEY, + instance_type='m1.tiny') + self.assertEqual(len(reservation.instances), 1) + self.data['instance'] = reservation.instances[0] + + def test_003_instance_runs_within_60_seconds(self): + instance = self.data['instance'] + # allow 60 seconds to exit pending with IP + if not self.wait_for_running(self.data['instance']): + self.fail('instance failed to start') + self.data['instance'].update() + ip = self.data['instance'].private_dns_name + self.failIf(ip == '0.0.0.0') + if FLAGS.use_ipv6: + ipv6 = self.data['instance'].dns_name_v6 + self.failIf(ipv6 is None) + + def test_004_can_ping_private_ip(self): + if not self.wait_for_ping(self.data['instance'].private_dns_name): + self.fail('could not ping instance') + + if FLAGS.use_ipv6: + if not self.wait_for_ping(self.data['instance'].ip_v6, "ping6"): + self.fail('could not ping instance v6') + + def test_005_can_ssh_to_private_ip(self): + if not self.wait_for_ssh(self.data['instance'].private_dns_name, + TEST_KEY): + self.fail('could not ssh to instance') + + if FLAGS.use_ipv6: + if not self.wait_for_ssh(self.data['instance'].ip_v6, + TEST_KEY): + self.fail('could not ssh to instance v6') + + def test_999_tearDown(self): + self.delete_key_pair(self.conn, TEST_KEY) + self.conn.terminate_instances([self.data['instance'].id]) + + +class VolumeTests(base.UserSmokeTestCase): + def setUp(self): + super(VolumeTests, self).setUp() + self.device = '/dev/vdb' + + def test_000_setUp(self): + self.create_key_pair(self.conn, TEST_KEY) + reservation = self.conn.run_instances(FLAGS.test_image, + instance_type='m1.tiny', + key_name=TEST_KEY) + self.data['instance'] = reservation.instances[0] + if not self.wait_for_running(self.data['instance']): + self.fail('instance failed to start') + self.data['instance'].update() + if not self.wait_for_ping(self.data['instance'].private_dns_name): + self.fail('could not ping instance') + if not self.wait_for_ssh(self.data['instance'].private_dns_name, + TEST_KEY): + self.fail('could not ssh to instance') + + def test_001_can_create_volume(self): + volume = self.conn.create_volume(1, 'nova') + self.assertEqual(volume.size, 1) + self.data['volume'] = volume + # Give network time to find volume. + time.sleep(10) + + def test_002_can_attach_volume(self): + volume = self.data['volume'] + + for x in xrange(10): + volume.update() + if volume.status.startswith('available'): + break + time.sleep(1) + else: + self.fail('cannot attach volume with state %s' % volume.status) + + volume.attach(self.data['instance'].id, self.device) + + # wait + for x in xrange(10): + volume.update() + if volume.status.startswith('in-use'): + break + time.sleep(1) + else: + self.fail('volume never got to in use') + + self.assertTrue(volume.status.startswith('in-use')) + + # Give instance time to recognize volume. + time.sleep(10) + + def test_003_can_mount_volume(self): + ip = self.data['instance'].private_dns_name + conn = self.connect_ssh(ip, TEST_KEY) + # NOTE(vish): this will create an dev for images that don't have + # udev rules + stdin, stdout, stderr = conn.exec_command( + 'grep %s /proc/partitions | ' + '`awk \'{print "mknod /dev/"\\$4" b "\\$1" "\\$2}\'`' + % self.device.rpartition('/')[2]) + exec_list = [] + exec_list.append('mkdir -p /mnt/vol') + exec_list.append('/sbin/mke2fs %s' % self.device) + exec_list.append('mount %s /mnt/vol' % self.device) + exec_list.append('echo success') + stdin, stdout, stderr = conn.exec_command(' && '.join(exec_list)) + out = stdout.read() + conn.close() + if not out.strip().endswith('success'): + self.fail('Unable to mount: %s %s' % (out, stderr.read())) + + def test_004_can_write_to_volume(self): + ip = self.data['instance'].private_dns_name + conn = self.connect_ssh(ip, TEST_KEY) + # FIXME(devcamcar): This doesn't fail if the volume hasn't been mounted + stdin, stdout, stderr = conn.exec_command( + 'echo hello > /mnt/vol/test.txt') + err = stderr.read() + conn.close() + if len(err) > 0: + self.fail('Unable to write to mount: %s' % (err)) + + def test_005_volume_is_correct_size(self): + ip = self.data['instance'].private_dns_name + conn = self.connect_ssh(ip, TEST_KEY) + stdin, stdout, stderr = conn.exec_command( + "df -h | grep %s | awk {'print $2'}" % self.device) + out = stdout.read() + conn.close() + if not out.strip() == '1007.9M': + self.fail('Volume is not the right size: %s %s' % + (out, stderr.read())) + + def test_006_me_can_umount_volume(self): + ip = self.data['instance'].private_dns_name + conn = self.connect_ssh(ip, TEST_KEY) + stdin, stdout, stderr = conn.exec_command('umount /mnt/vol') + err = stderr.read() + conn.close() + if len(err) > 0: + self.fail('Unable to unmount: %s' % (err)) + + def test_007_me_can_detach_volume(self): + result = self.conn.detach_volume(volume_id=self.data['volume'].id) + self.assertTrue(result) + time.sleep(5) + + def test_008_me_can_delete_volume(self): + result = self.conn.delete_volume(self.data['volume'].id) + self.assertTrue(result) + + def test_999_tearDown(self): + self.conn.terminate_instances([self.data['instance'].id]) + self.conn.delete_key_pair(TEST_KEY) + + +if __name__ == "__main__": + suites = {'image': unittest.makeSuite(ImageTests), + 'instance': unittest.makeSuite(InstanceTests), + 'volume': unittest.makeSuite(VolumeTests) + } + sys.exit(base.run_tests(suites)) diff --git a/smoketests/user_smoketests.py b/smoketests/user_smoketests.py deleted file mode 100644 index 26f6344f7..000000000 --- a/smoketests/user_smoketests.py +++ /dev/null @@ -1,397 +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. - -import commands -import os -import random -import sys -import time -import unittest - -# If ../nova/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): - sys.path.insert(0, possible_topdir) - -from smoketests import flags -from smoketests import base - - -SUITE_NAMES = '[image, instance, volume]' - -FLAGS = flags.FLAGS -flags.DEFINE_string('suite', None, 'Specific test suite to run ' + SUITE_NAMES) -flags.DEFINE_string('bundle_kernel', 'openwrt-x86-vmlinuz', - 'Local kernel file to use for bundling tests') -flags.DEFINE_string('bundle_image', 'openwrt-x86-ext2.image', - 'Local image file to use for bundling tests') - -TEST_PREFIX = 'test%s' % int(random.random() * 1000000) -TEST_BUCKET = '%s_bucket' % TEST_PREFIX -TEST_KEY = '%s_key' % TEST_PREFIX -TEST_GROUP = '%s_group' % TEST_PREFIX -TEST_DATA = {} - - -class UserSmokeTestCase(base.SmokeTestCase): - def setUp(self): - global TEST_DATA - self.conn = self.connection_for_env() - self.data = TEST_DATA - - -class ImageTests(UserSmokeTestCase): - def test_001_can_bundle_image(self): - self.assertTrue(self.bundle_image(FLAGS.bundle_image)) - - def test_002_can_upload_image(self): - self.assertTrue(self.upload_image(TEST_BUCKET, FLAGS.bundle_image)) - - def test_003_can_register_image(self): - image_id = self.conn.register_image('%s/%s.manifest.xml' % - (TEST_BUCKET, FLAGS.bundle_image)) - self.assert_(image_id is not None) - self.data['image_id'] = image_id - - def test_004_can_bundle_kernel(self): - self.assertTrue(self.bundle_image(FLAGS.bundle_kernel, kernel=True)) - - def test_005_can_upload_kernel(self): - self.assertTrue(self.upload_image(TEST_BUCKET, FLAGS.bundle_kernel)) - - def test_006_can_register_kernel(self): - kernel_id = self.conn.register_image('%s/%s.manifest.xml' % - (TEST_BUCKET, FLAGS.bundle_kernel)) - self.assert_(kernel_id is not None) - self.data['kernel_id'] = kernel_id - - def test_007_images_are_available_within_10_seconds(self): - for i in xrange(10): - image = self.conn.get_image(self.data['image_id']) - if image and image.state == 'available': - break - time.sleep(1) - else: - self.assert_(False) # wasn't available within 10 seconds - self.assert_(image.type == 'machine') - - for i in xrange(10): - kernel = self.conn.get_image(self.data['kernel_id']) - if kernel and kernel.state == 'available': - break - time.sleep(1) - else: - self.assert_(False) # wasn't available within 10 seconds - self.assert_(kernel.type == 'kernel') - - def test_008_can_describe_image_attribute(self): - attrs = self.conn.get_image_attribute(self.data['image_id'], - 'launchPermission') - self.assert_(attrs.name, 'launch_permission') - - def test_009_can_modify_image_launch_permission(self): - self.conn.modify_image_attribute(image_id=self.data['image_id'], - operation='add', - attribute='launchPermission', - groups='all') - image = self.conn.get_image(self.data['image_id']) - self.assertEqual(image.id, self.data['image_id']) - - def test_010_can_see_launch_permission(self): - attrs = self.conn.get_image_attribute(self.data['image_id'], - 'launchPermission') - self.assert_(attrs.name, 'launch_permission') - self.assert_(attrs.attrs['groups'][0], 'all') - - def test_011_user_can_deregister_kernel(self): - self.assertTrue(self.conn.deregister_image(self.data['kernel_id'])) - - def test_012_can_deregister_image(self): - self.assertTrue(self.conn.deregister_image(self.data['image_id'])) - - def test_013_can_delete_bundle(self): - self.assertTrue(self.delete_bundle_bucket(TEST_BUCKET)) - - -class InstanceTests(UserSmokeTestCase): - def test_001_can_create_keypair(self): - key = self.create_key_pair(self.conn, TEST_KEY) - self.assertEqual(key.name, TEST_KEY) - - def test_002_can_create_instance_with_keypair(self): - reservation = self.conn.run_instances(FLAGS.test_image, - key_name=TEST_KEY, - instance_type='m1.tiny') - self.assertEqual(len(reservation.instances), 1) - self.data['instance'] = reservation.instances[0] - - def test_003_instance_runs_within_60_seconds(self): - instance = self.data['instance'] - # allow 60 seconds to exit pending with IP - if not self.wait_for_running(self.data['instance']): - self.fail('instance failed to start') - self.data['instance'].update() - ip = self.data['instance'].private_dns_name - self.failIf(ip == '0.0.0.0') - if FLAGS.use_ipv6: - ipv6 = self.data['instance'].dns_name_v6 - self.failIf(ipv6 is None) - - def test_004_can_ping_private_ip(self): - if not self.wait_for_ping(self.data['instance'].private_dns_name): - self.fail('could not ping instance') - - if FLAGS.use_ipv6: - if not self.wait_for_ping(self.data['instance'].ip_v6, "ping6"): - self.fail('could not ping instance v6') - - def test_005_can_ssh_to_private_ip(self): - if not self.wait_for_ssh(self.data['instance'].private_dns_name, - TEST_KEY): - self.fail('could not ssh to instance') - - if FLAGS.use_ipv6: - if not self.wait_for_ssh(self.data['instance'].ip_v6, - TEST_KEY): - self.fail('could not ssh to instance v6') - - def test_006_can_allocate_elastic_ip(self): - result = self.conn.allocate_address() - self.assertTrue(hasattr(result, 'public_ip')) - self.data['public_ip'] = result.public_ip - - def test_007_can_associate_ip_with_instance(self): - result = self.conn.associate_address(self.data['instance'].id, - self.data['public_ip']) - self.assertTrue(result) - - def test_008_can_ssh_with_public_ip(self): - if not self.wait_for_ssh(self.data['public_ip'], TEST_KEY): - self.fail('could not ssh to public ip') - - def test_009_can_disassociate_ip_from_instance(self): - result = self.conn.disassociate_address(self.data['public_ip']) - self.assertTrue(result) - - def test_010_can_deallocate_elastic_ip(self): - result = self.conn.release_address(self.data['public_ip']) - self.assertTrue(result) - - def test_999_tearDown(self): - self.delete_key_pair(self.conn, TEST_KEY) - self.conn.terminate_instances([self.data['instance'].id]) - - -class VolumeTests(UserSmokeTestCase): - def setUp(self): - super(VolumeTests, self).setUp() - self.device = '/dev/vdb' - - def test_000_setUp(self): - self.create_key_pair(self.conn, TEST_KEY) - reservation = self.conn.run_instances(FLAGS.test_image, - instance_type='m1.tiny', - key_name=TEST_KEY) - self.data['instance'] = reservation.instances[0] - if not self.wait_for_running(self.data['instance']): - self.fail('instance failed to start') - self.data['instance'].update() - if not self.wait_for_ping(self.data['instance'].private_dns_name): - self.fail('could not ping instance') - if not self.wait_for_ssh(self.data['instance'].private_dns_name, - TEST_KEY): - self.fail('could not ssh to instance') - - def test_001_can_create_volume(self): - volume = self.conn.create_volume(1, 'nova') - self.assertEqual(volume.size, 1) - self.data['volume'] = volume - # Give network time to find volume. - time.sleep(10) - - def test_002_can_attach_volume(self): - volume = self.data['volume'] - - for x in xrange(10): - volume.update() - if volume.status.startswith('available'): - break - time.sleep(1) - else: - self.fail('cannot attach volume with state %s' % volume.status) - - volume.attach(self.data['instance'].id, self.device) - - # wait - for x in xrange(10): - volume.update() - if volume.status.startswith('in-use'): - break - time.sleep(1) - else: - self.fail('volume never got to in use') - - self.assertTrue(volume.status.startswith('in-use')) - - # Give instance time to recognize volume. - time.sleep(10) - - def test_003_can_mount_volume(self): - ip = self.data['instance'].private_dns_name - conn = self.connect_ssh(ip, TEST_KEY) - # NOTE(vish): this will create an dev for images that don't have - # udev rules - stdin, stdout, stderr = conn.exec_command( - 'grep %s /proc/partitions | ' - '`awk \'{print "mknod /dev/"\\$4" b "\\$1" "\\$2}\'`' - % self.device.rpartition('/')[2]) - exec_list = [] - exec_list.append('mkdir -p /mnt/vol') - exec_list.append('/sbin/mke2fs %s' % self.device) - exec_list.append('mount %s /mnt/vol' % self.device) - exec_list.append('echo success') - stdin, stdout, stderr = conn.exec_command(' && '.join(exec_list)) - out = stdout.read() - conn.close() - if not out.strip().endswith('success'): - self.fail('Unable to mount: %s %s' % (out, stderr.read())) - - def test_004_can_write_to_volume(self): - ip = self.data['instance'].private_dns_name - conn = self.connect_ssh(ip, TEST_KEY) - # FIXME(devcamcar): This doesn't fail if the volume hasn't been mounted - stdin, stdout, stderr = conn.exec_command( - 'echo hello > /mnt/vol/test.txt') - err = stderr.read() - conn.close() - if len(err) > 0: - self.fail('Unable to write to mount: %s' % (err)) - - def test_005_volume_is_correct_size(self): - ip = self.data['instance'].private_dns_name - conn = self.connect_ssh(ip, TEST_KEY) - stdin, stdout, stderr = conn.exec_command( - "df -h | grep %s | awk {'print $2'}" % self.device) - out = stdout.read() - conn.close() - if not out.strip() == '1007.9M': - self.fail('Volume is not the right size: %s %s' % - (out, stderr.read())) - - def test_006_me_can_umount_volume(self): - ip = self.data['instance'].private_dns_name - conn = self.connect_ssh(ip, TEST_KEY) - stdin, stdout, stderr = conn.exec_command('umount /mnt/vol') - err = stderr.read() - conn.close() - if len(err) > 0: - self.fail('Unable to unmount: %s' % (err)) - - def test_007_me_can_detach_volume(self): - result = self.conn.detach_volume(volume_id=self.data['volume'].id) - self.assertTrue(result) - time.sleep(5) - - def test_008_me_can_delete_volume(self): - result = self.conn.delete_volume(self.data['volume'].id) - self.assertTrue(result) - - def test_999_tearDown(self): - self.conn.terminate_instances([self.data['instance'].id]) - self.conn.delete_key_pair(TEST_KEY) - - -class SecurityGroupTests(UserSmokeTestCase): - - def __public_instance_is_accessible(self): - id_url = "latest/meta-data/instance-id" - options = "-s --max-time 1" - command = "curl %s %s/%s" % (options, self.data['public_ip'], id_url) - instance_id = commands.getoutput(command).strip() - if not instance_id: - return False - if instance_id != self.data['instance_id']: - raise Exception("Wrong instance id") - return True - - def test_001_can_create_security_group(self): - self.conn.create_security_group(TEST_GROUP, description='test') - - groups = self.conn.get_all_security_groups() - self.assertTrue(TEST_GROUP in [group.name for group in groups]) - - def test_002_can_launch_instance_in_security_group(self): - self.create_key_pair(self.conn, TEST_KEY) - reservation = self.conn.run_instances(FLAGS.test_image, - key_name=TEST_KEY, - security_groups=[TEST_GROUP], - instance_type='m1.tiny') - - self.data['instance_id'] = reservation.instances[0].id - - def test_003_can_authorize_security_group_ingress(self): - self.assertTrue(self.conn.authorize_security_group(TEST_GROUP, - ip_protocol='tcp', - from_port=80, - to_port=80)) - - def test_004_can_access_instance_over_public_ip(self): - result = self.conn.allocate_address() - self.assertTrue(hasattr(result, 'public_ip')) - self.data['public_ip'] = result.public_ip - - result = self.conn.associate_address(self.data['instance_id'], - self.data['public_ip']) - start_time = time.time() - while not self.__public_instance_is_accessible(): - # 1 minute to launch - if time.time() - start_time > 60: - raise Exception("Timeout") - time.sleep(1) - - def test_005_can_revoke_security_group_ingress(self): - self.assertTrue(self.conn.revoke_security_group(TEST_GROUP, - ip_protocol='tcp', - from_port=80, - to_port=80)) - start_time = time.time() - while self.__public_instance_is_accessible(): - # 1 minute to teardown - if time.time() - start_time > 60: - raise Exception("Timeout") - time.sleep(1) - - def test_999_tearDown(self): - self.conn.delete_key_pair(TEST_KEY) - self.conn.delete_security_group(TEST_GROUP) - groups = self.conn.get_all_security_groups() - self.assertFalse(TEST_GROUP in [group.name for group in groups]) - self.conn.terminate_instances([self.data['instance_id']]) - self.assertTrue(self.conn.release_address(self.data['public_ip'])) - - -if __name__ == "__main__": - suites = {'image': unittest.makeSuite(ImageTests), - 'instance': unittest.makeSuite(InstanceTests), - #'security_group': unittest.makeSuite(SecurityGroupTests), - 'volume': unittest.makeSuite(VolumeTests) - } - sys.exit(base.run_tests(suites)) -- cgit From ef37833e6f45f99b1d16143d29685974a191c387 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Tue, 22 Feb 2011 18:04:32 -0800 Subject: add netadmin smoketests --- smoketests/netadmin_smoketests.py | 194 ++++++++++++++++++++++++++++++++++++++ smoketests/proxy.sh | 22 +++++ 2 files changed, 216 insertions(+) create mode 100644 smoketests/netadmin_smoketests.py create mode 100755 smoketests/proxy.sh diff --git a/smoketests/netadmin_smoketests.py b/smoketests/netadmin_smoketests.py new file mode 100644 index 000000000..38beb8fdc --- /dev/null +++ b/smoketests/netadmin_smoketests.py @@ -0,0 +1,194 @@ +# 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. + +import commands +import os +import random +import sys +import time +import unittest + +# If ../nova/__init__.py exists, add ../ to Python search path, so that +# it will override what happens to be installed in /usr/(local/)lib/python... +possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), + os.pardir, + os.pardir)) +if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): + sys.path.insert(0, possible_topdir) + +from smoketests import flags +from smoketests import base + + +FLAGS = flags.FLAGS + +TEST_PREFIX = 'test%s' % int(random.random() * 1000000) +TEST_BUCKET = '%s_bucket' % TEST_PREFIX +TEST_KEY = '%s_key' % TEST_PREFIX +TEST_GROUP = '%s_group' % TEST_PREFIX + + +class AddressTests(base.UserSmokeTestCase): + def test_000_setUp(self): + self.create_key_pair(self.conn, TEST_KEY) + reservation = self.conn.run_instances(FLAGS.test_image, + instance_type='m1.tiny', + key_name=TEST_KEY) + self.data['instance'] = reservation.instances[0] + if not self.wait_for_running(self.data['instance']): + self.fail('instance failed to start') + self.data['instance'].update() + if not self.wait_for_ping(self.data['instance'].private_dns_name): + self.fail('could not ping instance') + if not self.wait_for_ssh(self.data['instance'].private_dns_name, + TEST_KEY): + self.fail('could not ssh to instance') + + def test_001_can_allocate_floating_ip(self): + result = self.conn.allocate_address() + self.assertTrue(hasattr(result, 'public_ip')) + self.data['public_ip'] = result.public_ip + + def test_002_can_associate_ip_with_instance(self): + result = self.conn.associate_address(self.data['instance'].id, + self.data['public_ip']) + self.assertTrue(result) + + def test_003_can_ssh_with_public_ip(self): + ssh_authorized = False + groups = self.conn.get_all_security_groups(['default']) + for rule in groups[0].rules: + if (rule.ip_protocol == 'tcp' and + rule.from_port <= 22 and rule.to_port >= 22): + ssh_authorized = True + if not ssh_authorized: + self.conn.authorize_security_group('default', + ip_protocol='tcp', + from_port=22, + to_port=22) + try: + if not self.wait_for_ssh(self.data['public_ip'], TEST_KEY): + self.fail('could not ssh to public ip') + finally: + if not ssh_authorized: + self.conn.revoke_security_group('default', + ip_protocol='tcp', + from_port=22, + to_port=22) + + def test_004_can_disassociate_ip_from_instance(self): + result = self.conn.disassociate_address(self.data['public_ip']) + self.assertTrue(result) + + def test_005_can_deallocate_floating_ip(self): + result = self.conn.release_address(self.data['public_ip']) + self.assertTrue(result) + + def test_999_tearDown(self): + self.delete_key_pair(self.conn, TEST_KEY) + self.conn.terminate_instances([self.data['instance'].id]) + + +class SecurityGroupTests(base.UserSmokeTestCase): + + def __public_instance_is_accessible(self): + id_url = "latest/meta-data/instance-id" + options = "-s --max-time 1" + command = "curl %s %s/%s" % (options, self.data['public_ip'], id_url) + instance_id = commands.getoutput(command).strip() + if not instance_id: + return False + if instance_id != self.data['instance'].id: + raise Exception("Wrong instance id") + return True + + def test_001_can_create_security_group(self): + self.conn.create_security_group(TEST_GROUP, description='test') + + groups = self.conn.get_all_security_groups() + self.assertTrue(TEST_GROUP in [group.name for group in groups]) + + def test_002_can_launch_instance_in_security_group(self): + with open("proxy.sh") as f: + user_data = f.read() + self.create_key_pair(self.conn, TEST_KEY) + reservation = self.conn.run_instances(FLAGS.test_image, + key_name=TEST_KEY, + security_groups=[TEST_GROUP], + user_data=user_data, + instance_type='m1.tiny') + + self.data['instance'] = reservation.instances[0] + if not self.wait_for_running(self.data['instance']): + self.fail('instance failed to start') + self.data['instance'].update() + if not self.wait_for_ping(self.data['instance'].private_dns_name): + self.fail('could not ping instance') + if not self.wait_for_ssh(self.data['instance'].private_dns_name, + TEST_KEY): + self.fail('could not ssh to instance') + + def test_003_can_authorize_security_group_ingress(self): + self.assertTrue(self.conn.authorize_security_group(TEST_GROUP, + ip_protocol='tcp', + from_port=80, + to_port=80)) + + def test_004_can_access_metadata_over_public_ip(self): + result = self.conn.allocate_address() + self.assertTrue(hasattr(result, 'public_ip')) + self.data['public_ip'] = result.public_ip + + result = self.conn.associate_address(self.data['instance'].id, + self.data['public_ip']) + start_time = time.time() + try: + while not self.__public_instance_is_accessible(): + # 1 minute to launch + if time.time() - start_time > 60: + raise Exception("Timeout") + time.sleep(1) + finally: + result = self.conn.disassociate_address(self.data['public_ip']) + + def test_005_can_revoke_security_group_ingress(self): + self.assertTrue(self.conn.revoke_security_group(TEST_GROUP, + ip_protocol='tcp', + from_port=80, + to_port=80)) + start_time = time.time() + while self.__public_instance_is_accessible(): + # 1 minute to teardown + if time.time() - start_time > 60: + raise Exception("Timeout") + time.sleep(1) + + def test_999_tearDown(self): + self.conn.delete_key_pair(TEST_KEY) + self.conn.delete_security_group(TEST_GROUP) + groups = self.conn.get_all_security_groups() + self.assertFalse(TEST_GROUP in [group.name for group in groups]) + self.conn.terminate_instances([self.data['instance'].id]) + self.assertTrue(self.conn.release_address(self.data['public_ip'])) + + +if __name__ == "__main__": + suites = {'address': unittest.makeSuite(AddressTests), + 'security_group': unittest.makeSuite(SecurityGroupTests) + } + sys.exit(base.run_tests(suites)) diff --git a/smoketests/proxy.sh b/smoketests/proxy.sh new file mode 100755 index 000000000..9b3f3108a --- /dev/null +++ b/smoketests/proxy.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# This is a simple shell script that uses netcat to set up a proxy to the +# metadata server on port 80 and to a google ip on port 8080. This is meant +# to be passed in by a script to an instance via user data, so that +# automatic testing of network connectivity can be performed. + +# Example usage: +# euca-run-instances -t m1.tiny -f proxy.sh ami-tty + +mkfifo backpipe1 +mkfifo backpipe2 + +# NOTE(vish): proxy metadata on port 80 +while true; do + nc -l -p 80 0<backpipe1 | nc 169.254.169.254 80 1>backpipe1 +done & + +# NOTE(vish): proxy google on port 8080 +while true; do + nc -l -p 8080 0<backpipe2 | nc 74.125.19.99 80 1>backpipe2 +done & -- cgit From 7c8c325f475926724f3243344803841e24d5cb84 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 22 Feb 2011 18:15:29 -0800 Subject: Make static create method behave more like other services --- nova/apiservice.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nova/apiservice.py b/nova/apiservice.py index 14239f196..693bc9a63 100644 --- a/nova/apiservice.py +++ b/nova/apiservice.py @@ -79,7 +79,10 @@ class ApiService(object): @staticmethod def create(): conf = wsgi.paste_config_file('nova-api.conf') - return serve(conf) + LOG.audit(_("Starting nova-api node (version %s)"), + version.version_string_with_vcs()) + service = ApiService(conf) + return service def serve(conf): -- cgit From 9f169fdef93898097e33b5e1c0318f543ced672e Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 22 Feb 2011 18:41:41 -0800 Subject: Reverted change to focus on the core bug - kernel_id and ramdisk_id are optional --- nova/api/openstack/servers.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 11a84687d..41b05cbb4 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -144,11 +144,13 @@ class Controller(wsgi.Controller): metadata stored in Glance as 'image_properties' """ def lookup(param): - properties = image.get('properties') - if properties: - return properties.get(param) - else: - return image.get(param) + _image_id = image_id + try: + return image['properties'][param] + except KeyError: + LOG.debug( + _("%(param)s property not found for image %(_image_id)s") % + locals()) image_id = str(image_id) image = self._image_service.show(req.environ['nova.context'], image_id) -- cgit From 3ef3dfc2f6c8b9cc14119793df4990432ff74ea2 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 22 Feb 2011 18:42:23 -0800 Subject: Return null if no kernel_id / ramdisk_id --- nova/api/openstack/servers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 41b05cbb4..d83bd34ab 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -151,6 +151,7 @@ class Controller(wsgi.Controller): LOG.debug( _("%(param)s property not found for image %(_image_id)s") % locals()) + return None image_id = str(image_id) image = self._image_service.show(req.environ['nova.context'], image_id) -- cgit From dd6b9c21d3ad493051f25ce632fb327ed7fc7b73 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 22 Feb 2011 18:57:04 -0800 Subject: Exit with exit code 1 if conf cannot be read --- bin/nova-api | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/nova-api b/bin/nova-api index ccb7701ae..d03be85e3 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -48,6 +48,7 @@ if __name__ == '__main__': conf = wsgi.paste_config_file('nova-api.conf') if not conf: LOG.error(_("No paste configuration found for: %s"), 'nova-api.conf') + sys.exit(1) else: service = apiservice.serve(conf) service.wait() -- cgit From 50e71cef14c3bd079fbc2d2c203b0e0f76ee869e Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 22 Feb 2011 18:59:23 -0800 Subject: Removed unused import & formatting cleanups --- bin/nova-api | 1 - nova/apiservice.py | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bin/nova-api b/bin/nova-api index d03be85e3..96c784624 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -37,7 +37,6 @@ gettext.install('nova', unicode=1) from nova import flags from nova import log as logging from nova import apiservice -from nova import utils from nova import wsgi FLAGS = flags.FLAGS diff --git a/nova/apiservice.py b/nova/apiservice.py index 693bc9a63..6340e9b9b 100644 --- a/nova/apiservice.py +++ b/nova/apiservice.py @@ -16,9 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. -""" -Wrapper for API service, makes it look more like the non-WSGI services -""" +"""Wrapper for API service, makes it look more like the non-WSGI services""" from nova import flags from nova import log as logging @@ -28,6 +26,7 @@ from nova import wsgi LOG = logging.getLogger('nova.api') + FLAGS = flags.FLAGS flags.DEFINE_string('ec2_listen', "0.0.0.0", 'IP address for EC2 API to listen') @@ -36,6 +35,7 @@ flags.DEFINE_string('osapi_listen', "0.0.0.0", 'IP address for OpenStack API to listen') flags.DEFINE_integer('osapi_listen_port', 8774, 'port for os api to listen') + API_ENDPOINTS = ['ec2', 'osapi'] -- cgit From 409ee5ff22bbd62d94a7afb1df1e6b7353c95d83 Mon Sep 17 00:00:00 2001 From: Todd Willey <todd@ansolabs.com> Date: Tue, 22 Feb 2011 23:42:49 -0500 Subject: Update the admin client to deal with VPNs and have a function host list. --- nova/adminclient.py | 63 ++++++++++++++++++++++++---- nova/api/ec2/admin.py | 111 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 152 insertions(+), 22 deletions(-) diff --git a/nova/adminclient.py b/nova/adminclient.py index c614b274c..fe2aca351 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -23,6 +23,8 @@ import base64 import boto import boto.exception import httplib +import re +import string from boto.ec2.regioninfo import RegionInfo @@ -165,19 +167,20 @@ class HostInfo(object): **Fields Include** - * Disk stats - * Running Instances - * Memory stats - * CPU stats - * Network address info - * Firewall info - * Bridge and devices - + * Hostname + * Compute service status + * Volume service status + * Instance count + * Volume count """ def __init__(self, connection=None): self.connection = connection self.hostname = None + self.compute = None + self.volume = None + self.instance_count = 0 + self.volume_count = 0 def __repr__(self): return 'Host:%s' % self.hostname @@ -188,7 +191,39 @@ class HostInfo(object): # this is needed by the sax parser, so ignore the ugly name def endElement(self, name, value, connection): - setattr(self, name, value) + fixed_name = string.lower(re.sub(r'([A-Z])', r'_\1', name)) + setattr(self, fixed_name, value) + + +class Vpn(object): + """ + Information about a Vpn, as parsed through SAX + + **Fields Include** + + * instance_id + * project_id + * public_ip + * public_port + * created_at + * internal_ip + * state + """ + + def __init__(self, connection=None): + self.connection = connection + self.instance_id = None + self.project_id = None + + def __repr__(self): + return 'Vpn:%s:%s' % (self.project_id, self.instance_id) + + def startElement(self, name, attrs, connection): + return None + + def endElement(self, name, value, connection): + fixed_name = string.lower(re.sub(r'([A-Z])', r'_\1', name)) + setattr(self, fixed_name, value) class InstanceType(object): @@ -422,6 +457,16 @@ class NovaAdminClient(object): zip = self.apiconn.get_object('GenerateX509ForUser', params, UserInfo) return zip.file + def start_vpn(self, project): + """ + Starts the vpn for a user + """ + return self.apiconn.get_object('StartVpn', {'Project': project}, Vpn) + + def get_vpns(self): + """Return a list of vpn with project name""" + return self.apiconn.get_list('DescribeVpns', {}, [('item', Vpn)]) + def get_hosts(self): return self.apiconn.get_list('DescribeHosts', {}, [('item', HostInfo)]) diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 735951082..e2a05fce1 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -21,14 +21,18 @@ Admin API controller, exposed through http via the api worker. """ import base64 +import datetime from nova import db from nova import exception +from nova import flags from nova import log as logging +from nova import utils from nova.auth import manager from nova.compute import instance_types +FLAGS = flags.FLAGS LOG = logging.getLogger('nova.api.ec2.admin') @@ -55,12 +59,25 @@ def project_dict(project): return {} -def host_dict(host): +def host_dict(host, compute_service, instances, volume_service, volumes, now): """Convert a host model object to a result dict""" - if host: - return host.state - else: - return {} + rv = {'hostanme': host, 'instance_count': len(instances), + 'volume_count': len(volumes)} + if compute_service: + latest = compute_service['updated_at'] or compute_service['created_at'] + delta = now - latest + if delta.seconds <= FLAGS.service_down_time: + rv['compute'] = 'up' + else: + rv['compute'] = 'down' + if volume_service: + latest = volume_service['updated_at'] or volume_service['created_at'] + delta = now - latest + if delta.seconds <= FLAGS.service_down_time: + rv['volume'] = 'up' + else: + rv['volume'] = 'down' + return rv def instance_dict(name, inst): @@ -71,6 +88,25 @@ def instance_dict(name, inst): 'flavor_id': inst['flavorid']} +def vpn_dict(project, vpn_instance): + rv = {'project_id': project.id, + 'public_ip': project.vpn_ip, + 'public_port': project.vpn_port} + if vpn_instance: + rv['instance_id'] = vpn_instance['ec2_id'] + rv['created_at'] = utils.isotime(vpn_instance['created_at']) + address = vpn_instance.get('fixed_ip', None) + if address: + rv['internal_ip'] = address['address'] + if utils.vpn_ping(project.vpn_ip, project.vpn_port): + rv['state'] = 'running' + else: + rv['state'] = 'down' + else: + rv['state'] = 'pending' + return rv + + class AdminController(object): """ API Controller for users, hosts, nodes, and workers. @@ -223,19 +259,68 @@ class AdminController(object): raise exception.ApiError(_('operation must be add or remove')) return True + def _vpn_for(self, context, project_id): + """Get the VPN instance for a project ID.""" + for instance in db.instance_get_all_by_project(context, project_id): + if (instance['image_id'] == FLAGS.vpn_image_id + and not instance['state_description'] in + ['shutting_down', 'shutdown']): + return instance + + def start_vpn(self, context, project): + instance = self._vpn_for(context, project) + if not instance: + # NOTE(vish) import delayed because of __init__.py + from nova.cloudpipe import pipelib + pipe = pipelib.CloudPipe() + try: + pipe.launch_vpn_instance(project) + except db.NoMoreNetworks: + raise exception.ApiError("Unable to claim IP for VPN instance" + ", ensure it isn't running, and try " + "again in a few minutes") + instance = self._vpn_for(context, project) + return {'instance_id': instance['ec2_id']} + + def describe_vpns(self, context): + vpns = [] + for project in manager.AuthManager().get_projects(): + instance = self._vpn_for(context, project.id) + vpns.append(vpn_dict(project, instance)) + return {'items': vpns} + # FIXME(vish): these host commands don't work yet, perhaps some of the # required data can be retrieved from service objects? - def describe_hosts(self, _context, **_kwargs): + def describe_hosts(self, context, **_kwargs): """Returns status info for all nodes. Includes: - * Disk Space - * Instance List - * RAM used - * CPU used - * DHCP servers running - * Iptables / bridges + * Hostname + * Compute (up, down, None) + * Instance count + * Volume (up, down, None) + * Volume Count """ - return {'hostSet': [host_dict(h) for h in db.host_get_all()]} + services = db.service_get_all(context) + now = datetime.datetime.utcnow() + hosts = [] + rv = [] + for host in [service['host'] for service in services]: + if not host in hosts: + hosts.append(host) + for host in hosts: + compute = [s for s in services if s['host'] == host \ + and s['binary'] == 'nova-compute'] + if compute: + compute = compute[0] + instances = db.instance_get_all_by_host(context, host) + volume = [s for s in services if s['host'] == host \ + and s['binary'] == 'nova-volume'] + if volume: + volume = volume[0] + volumes = db.volume_get_all_by_host(context, host) + rv.append(host_dict(host, compute, instances, volume, volumes, + now)) + return {'hosts': rv} def describe_host(self, _context, name, **_kwargs): """Returns status info for single node.""" -- cgit From 943b863bef09a4e2b3de36c26a3fabbcc6093411 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Tue, 22 Feb 2011 23:21:01 -0800 Subject: Lots of test fixing --- nova/api/ec2/cloud.py | 5 ++- nova/compute/api.py | 2 +- nova/db/sqlalchemy/api.py | 3 +- nova/tests/api/openstack/test_servers.py | 2 +- nova/tests/test_cloud.py | 75 ++++++++------------------------ nova/tests/test_network.py | 3 ++ nova/tests/test_scheduler.py | 3 ++ nova/tests/test_virt.py | 3 ++ nova/virt/fake.py | 4 +- 9 files changed, 36 insertions(+), 64 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 882cdcfc9..99b6d5cb6 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -529,8 +529,9 @@ class CloudController(object): def get_ajax_console(self, context, instance_id, **kwargs): ec2_id = instance_id[0] - internal_id = ec2_id_to_id(ec2_id) - return self.compute_api.get_ajax_console(context, internal_id) + instance_id = ec2_id_to_id(ec2_id) + return self.compute_api.get_ajax_console(context, + instance_id=instance_id) def describe_volumes(self, context, volume_id=None, **kwargs): if volume_id: diff --git a/nova/compute/api.py b/nova/compute/api.py index 81ea6dc53..0caadc32e 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -447,7 +447,7 @@ class API(base.Base): {'method': 'authorize_ajax_console', 'args': {'token': output['token'], 'host': output['host'], 'port': output['port']}}) - return {'url': '%s?token=%s' % (FLAGS.ajax_console_proxy_url, + return {'url': '%s/?token=%s' % (FLAGS.ajax_console_proxy_url, output['token'])} def get_console_output(self, context, instance_id): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 2697fac73..2ab402e1c 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1046,7 +1046,8 @@ def network_create_safe(context, values): @require_admin_context def network_disassociate(context, network_id): - network_update(context, network_id, {'project_id': None}) + network_update(context, network_id, {'project_id': None, + 'host': None}) @require_admin_context diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index a7be0796e..589f3d3eb 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -84,7 +84,7 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None): "vcpus": 0, "local_gb": 0, "hostname": "", - "host": "", + "host": None, "instance_type": "", "user_data": "", "reservation_id": "", diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 1824d24bc..2c6dc5973 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -66,6 +66,7 @@ class CloudTestCase(test.TestCase): # set up services self.compute = self.start_service('compute') + self.scheduter = self.start_service('scheduler') self.network = self.start_service('network') self.manager = manager.AuthManager() @@ -73,8 +74,12 @@ class CloudTestCase(test.TestCase): self.project = self.manager.create_project('proj', 'admin', 'proj') self.context = context.RequestContext(user=self.user, project=self.project) + host = self.network.get_network_host(self.context.elevated()) def tearDown(self): + network_ref = db.project_get_network(self.context, + self.project.id) + db.network_disassociate(self.context, network_ref['id']) self.manager.delete_project(self.project) self.manager.delete_user(self.user) self.compute.kill() @@ -201,27 +206,32 @@ class CloudTestCase(test.TestCase): 'instance_type': instance_type, 'max_count': max_count} rv = self.cloud.run_instances(self.context, **kwargs) + greenthread.sleep(0.3) instance_id = rv['instancesSet'][0]['instanceId'] output = self.cloud.get_console_output(context=self.context, - instance_id=[instance_id]) + instance_id=[instance_id]) self.assertEquals(b64decode(output['output']), 'FAKE CONSOLE OUTPUT') # TODO(soren): We need this until we can stop polling in the rpc code # for unit tests. greenthread.sleep(0.3) rv = self.cloud.terminate_instances(self.context, [instance_id]) + greenthread.sleep(0.3) def test_ajax_console(self): + image_id = FLAGS.default_image kwargs = {'image_id': image_id} - rv = yield self.cloud.run_instances(self.context, **kwargs) + rv = self.cloud.run_instances(self.context, **kwargs) instance_id = rv['instancesSet'][0]['instanceId'] - output = yield self.cloud.get_console_output(context=self.context, - instance_id=[instance_id]) - self.assertEquals(b64decode(output['output']), - 'http://fakeajaxconsole.com/?token=FAKETOKEN') + greenthread.sleep(0.3) + output = self.cloud.get_ajax_console(context=self.context, + instance_id=[instance_id]) + self.assertEquals(output['url'], + '%s/?token=FAKETOKEN' % FLAGS.ajax_console_proxy_url) # TODO(soren): We need this until we can stop polling in the rpc code # for unit tests. greenthread.sleep(0.3) - rv = yield self.cloud.terminate_instances(self.context, [instance_id]) + rv = self.cloud.terminate_instances(self.context, [instance_id]) + greenthread.sleep(0.3) def test_key_generation(self): result = self._create_key('test') @@ -297,57 +307,6 @@ class CloudTestCase(test.TestCase): db.instance_destroy(self.context, instance1['id']) db.service_destroy(self.context, comp1['id']) - def test_instance_update_state(self): - # TODO(termie): what is this code even testing? - def instance(num): - return { - 'reservation_id': 'r-1', - 'instance_id': 'i-%s' % num, - 'image_id': 'ami-%s' % num, - 'private_dns_name': '10.0.0.%s' % num, - 'dns_name': '10.0.0%s' % num, - 'ami_launch_index': str(num), - 'instance_type': 'fake', - 'availability_zone': 'fake', - 'key_name': None, - 'kernel_id': 'fake', - 'ramdisk_id': 'fake', - 'groups': ['default'], - 'product_codes': None, - 'state': 0x01, - 'user_data': ''} - rv = self.cloud._format_describe_instances(self.context) - logging.error(str(rv)) - self.assertEqual(len(rv['reservationSet']), 0) - - # simulate launch of 5 instances - # self.cloud.instances['pending'] = {} - #for i in xrange(5): - # inst = instance(i) - # self.cloud.instances['pending'][inst['instance_id']] = inst - - #rv = self.cloud._format_instances(self.admin) - #self.assert_(len(rv['reservationSet']) == 1) - #self.assert_(len(rv['reservationSet'][0]['instances_set']) == 5) - # report 4 nodes each having 1 of the instances - #for i in xrange(4): - # self.cloud.update_state('instances', - # {('node-%s' % i): {('i-%s' % i): - # instance(i)}}) - - # one instance should be pending still - #self.assert_(len(self.cloud.instances['pending'].keys()) == 1) - - # check that the reservations collapse - #rv = self.cloud._format_instances(self.admin) - #self.assert_(len(rv['reservationSet']) == 1) - #self.assert_(len(rv['reservationSet'][0]['instances_set']) == 5) - - # check that we can get metadata for each instance - #for i in xrange(4): - # data = self.cloud.get_metadata(instance(i)['private_dns_name']) - # self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i) - @staticmethod def _fake_set_image_description(ctxt, image_id, description): from nova.objectstore import handler diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 00f9323f3..53cfea276 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -117,6 +117,9 @@ class NetworkTestCase(test.TestCase): utils.to_global_ipv6( network_ref['cidr_v6'], instance_ref['mac_address'])) + self._deallocate_address(0, address) + db.instance_destroy(context.get_admin_context(), + instance_ref['id']) def test_public_network_association(self): """Makes sure that we can allocaate a public ip""" diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py index 250170072..8e4a4daf5 100644 --- a/nova/tests/test_scheduler.py +++ b/nova/tests/test_scheduler.py @@ -118,6 +118,7 @@ class ZoneSchedulerTestCase(test.TestCase): arg = IgnoreArg() db.service_get_all_by_topic(arg, arg).AndReturn(service_list) self.mox.StubOutWithMock(rpc, 'cast', use_mock_anything=True) + self.mox.StubOutWithMock(db, 'instance_create', use_mock_anything=True) rpc.cast(ctxt, 'compute.host1', {'method': 'run_instance', @@ -150,6 +151,7 @@ class SimpleDriverTestCase(test.TestCase): def tearDown(self): self.manager.delete_user(self.user) self.manager.delete_project(self.project) + super(SimpleDriverTestCase, self).tearDown() def _create_instance(self, **kwargs): """Create a test instance""" @@ -270,6 +272,7 @@ class SimpleDriverTestCase(test.TestCase): self.scheduler.driver.schedule_run_instance, self.context, instance_id) + db.instance_destroy(self.context, instance_id) for instance_id in instance_ids1: compute1.terminate_instance(self.context, instance_id) for instance_id in instance_ids2: diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 6e5a0114b..5b3247df9 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -204,6 +204,7 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_conn.LibvirtConnection(True) uri = conn.get_uri() self.assertEquals(uri, testuri) + db.instance_destroy(user_context, instance_ref['id']) def tearDown(self): super(LibvirtConnTestCase, self).tearDown() @@ -365,6 +366,7 @@ class IptablesFirewallTestCase(test.TestCase): '--dports 80:81 -j ACCEPT' % security_group_chain \ in self.out_rules, "TCP port 80/81 acceptance rule wasn't added") + db.instance_destroy(admin_ctxt, instance_ref['id']) class NWFilterTestCase(test.TestCase): @@ -514,3 +516,4 @@ class NWFilterTestCase(test.TestCase): self.fw.apply_instance_filter(instance) _ensure_all_called() self.teardown_security_group() + db.instance_destroy(admin_ctxt, instance_ref['id']) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 92749f38a..4346dffc1 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -319,7 +319,9 @@ class FakeConnection(object): return 'FAKE CONSOLE OUTPUT' def get_ajax_console(self, instance): - return 'http://fakeajaxconsole.com/?token=FAKETOKEN' + return {'token': 'FAKETOKEN', + 'host': 'fakeajaxconsole.com', + 'port': 6969} def get_console_pool_info(self, console_type): return {'address': '127.0.0.1', -- cgit From 015900b215805808d8cc3138b0f4deb2c0941f76 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Tue, 22 Feb 2011 23:30:52 -0800 Subject: remove unnecessary stubout --- nova/tests/test_scheduler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py index 8e4a4daf5..b6888c4d2 100644 --- a/nova/tests/test_scheduler.py +++ b/nova/tests/test_scheduler.py @@ -118,7 +118,6 @@ class ZoneSchedulerTestCase(test.TestCase): arg = IgnoreArg() db.service_get_all_by_topic(arg, arg).AndReturn(service_list) self.mox.StubOutWithMock(rpc, 'cast', use_mock_anything=True) - self.mox.StubOutWithMock(db, 'instance_create', use_mock_anything=True) rpc.cast(ctxt, 'compute.host1', {'method': 'run_instance', -- cgit From 60ed7c9c52306d08b1ad3e759e173931b0a495a8 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 00:59:15 -0800 Subject: fix failures --- run_tests.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/run_tests.py b/run_tests.py index 4084d8d80..01a1cf8bc 100644 --- a/run_tests.py +++ b/run_tests.py @@ -181,6 +181,7 @@ class NovaTestResult(result.TextTestResult): return str(test) def addSuccess(self, test): + unittest.TestResult.addSuccess(self, test) if self.showAll: self.colorizer.write("OK", 'green') self.stream.writeln() @@ -188,7 +189,8 @@ class NovaTestResult(result.TextTestResult): self.stream.write('.') self.stream.flush() - def addFailure(self, test): + def addFailure(self, test, err): + unittest.TestResult.addFailure(self, test, err) if self.showAll: self.colorizer.write("FAIL", 'red') self.stream.writeln() -- cgit From 2bec58e35ab1f2df543e50d399433f76e98210d7 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 01:13:41 -0800 Subject: move db creation into fixtures and clean db for each test --- nova/test.py | 17 ++--------------- nova/tests/__init__.py | 15 +++++++++++++++ nova/tests/api/openstack/__init__.py | 4 ++-- nova/tests/api/openstack/test_adminapi.py | 11 +++++------ nova/tests/api/openstack/test_api.py | 4 ++-- nova/tests/api/openstack/test_auth.py | 14 +++++++------- nova/tests/api/openstack/test_common.py | 5 +++-- nova/tests/api/openstack/test_faults.py | 4 ++-- nova/tests/api/openstack/test_flavors.py | 10 ++++------ nova/tests/api/openstack/test_images.py | 14 ++++++++++---- nova/tests/api/openstack/test_ratelimiting.py | 15 +++++++-------- nova/tests/api/openstack/test_servers.py | 10 ++++------ nova/tests/api/openstack/test_shared_ip_groups.py | 7 ++++--- nova/tests/api/openstack/test_zones.py | 10 ++++------ nova/tests/api/test_wsgi.py | 6 +++--- nova/tests/objectstore_unittest.py | 1 + nova/tests/test_direct.py | 1 + nova/tests/test_scheduler.py | 1 + nova/tests/test_virt.py | 3 ++- 19 files changed, 79 insertions(+), 73 deletions(-) diff --git a/nova/test.py b/nova/test.py index bff43b6c7..42accffa7 100644 --- a/nova/test.py +++ b/nova/test.py @@ -26,15 +26,14 @@ import datetime import unittest import mox +import shutil import stubout from nova import context from nova import db from nova import fakerabbit from nova import flags -from nova import log as logging from nova import rpc -from nova.network import manager as network_manager FLAGS = flags.FLAGS @@ -64,15 +63,7 @@ class TestCase(unittest.TestCase): # now that we have some required db setup for the system # to work properly. self.start = datetime.datetime.utcnow() - ctxt = context.get_admin_context() - if db.network_count(ctxt) != 5: - network_manager.VlanManager().create_networks(ctxt, - FLAGS.fixed_range, - 5, 16, - FLAGS.fixed_range_v6, - FLAGS.vlan_start, - FLAGS.vpn_start, - ) + shutil.copyfile("clean.sqlite", "tests.sqlite") # emulate some of the mox stuff, we can't use the metaclass # because it screws with our generators @@ -93,9 +84,6 @@ class TestCase(unittest.TestCase): self.mox.VerifyAll() # NOTE(vish): Clean up any ips associated during the test. ctxt = context.get_admin_context() - db.fixed_ip_disassociate_all_by_timeout(ctxt, FLAGS.host, - self.start) - db.network_disassociate_all(ctxt) rpc.Consumer.attach_to_eventlet = self.originalAttach for x in self.injected: try: @@ -106,7 +94,6 @@ class TestCase(unittest.TestCase): if FLAGS.fake_rabbit: fakerabbit.reset_all() - db.security_group_destroy_all(ctxt) super(TestCase, self).tearDown() finally: self.reset_flags() diff --git a/nova/tests/__init__.py b/nova/tests/__init__.py index 592d5bea9..5472bdaf2 100644 --- a/nova/tests/__init__.py +++ b/nova/tests/__init__.py @@ -37,5 +37,20 @@ setattr(__builtin__, '_', lambda x: x) def setup(): + import shutil + from nova import context + from nova import flags from nova.db import migration + from nova.network import manager as network_manager + from nova.tests import fake_flags + FLAGS = flags.FLAGS migration.db_sync() + ctxt = context.get_admin_context() + network_manager.VlanManager().create_networks(ctxt, + FLAGS.fixed_range, + 5, 16, + FLAGS.fixed_range_v6, + FLAGS.vlan_start, + FLAGS.vpn_start, + ) + shutil.copyfile("tests.sqlite", "clean.sqlite") diff --git a/nova/tests/api/openstack/__init__.py b/nova/tests/api/openstack/__init__.py index 77b1dd37f..e18120285 100644 --- a/nova/tests/api/openstack/__init__.py +++ b/nova/tests/api/openstack/__init__.py @@ -16,7 +16,7 @@ # under the License. import webob.dec -import unittest +from nova import test from nova import context from nova import flags @@ -33,7 +33,7 @@ def simple_wsgi(req): return "" -class RateLimitingMiddlewareTest(unittest.TestCase): +class RateLimitingMiddlewareTest(test.TestCase): def test_get_action_name(self): middleware = RateLimitingMiddleware(simple_wsgi) diff --git a/nova/tests/api/openstack/test_adminapi.py b/nova/tests/api/openstack/test_adminapi.py index 73120c31d..dfce1b127 100644 --- a/nova/tests/api/openstack/test_adminapi.py +++ b/nova/tests/api/openstack/test_adminapi.py @@ -15,13 +15,13 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest import stubout import webob from paste import urlmap from nova import flags +from nova import test from nova.api import openstack from nova.api.openstack import ratelimiting from nova.api.openstack import auth @@ -30,9 +30,10 @@ from nova.tests.api.openstack import fakes FLAGS = flags.FLAGS -class AdminAPITest(unittest.TestCase): +class AdminAPITest(test.TestCase): def setUp(self): + super(AdminAPITest, self).setUp() self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} fakes.FakeAuthDatabase.data = {} @@ -44,6 +45,7 @@ class AdminAPITest(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() FLAGS.allow_admin_api = self.allow_admin + super(AdminAPITest, self).tearDown() def test_admin_enabled(self): FLAGS.allow_admin_api = True @@ -58,8 +60,5 @@ class AdminAPITest(unittest.TestCase): # We should still be able to access public operations. req = webob.Request.blank('/v1.0/flavors') res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) # TODO: Confirm admin operations are unavailable. - -if __name__ == '__main__': - unittest.main() + self.assertEqual(res.status_int, 200) diff --git a/nova/tests/api/openstack/test_api.py b/nova/tests/api/openstack/test_api.py index db0fe1060..5112c486f 100644 --- a/nova/tests/api/openstack/test_api.py +++ b/nova/tests/api/openstack/test_api.py @@ -15,17 +15,17 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest import webob.exc import webob.dec from webob import Request +from nova import test from nova.api import openstack from nova.api.openstack import faults -class APITest(unittest.TestCase): +class APITest(test.TestCase): def _wsgi_app(self, inner_app): # simpler version of the app than fakes.wsgi_app diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py index 0dd65d321..13f6c3a1c 100644 --- a/nova/tests/api/openstack/test_auth.py +++ b/nova/tests/api/openstack/test_auth.py @@ -16,7 +16,6 @@ # under the License. import datetime -import unittest import stubout import webob @@ -27,12 +26,14 @@ import nova.api.openstack.auth import nova.auth.manager from nova import auth from nova import context +from nova import test from nova.tests.api.openstack import fakes -class Test(unittest.TestCase): +class Test(test.TestCase): def setUp(self): + super(Test, self).setUp() self.stubs = stubout.StubOutForTesting() self.stubs.Set(nova.api.openstack.auth.AuthMiddleware, '__init__', fakes.fake_auth_init) @@ -45,6 +46,7 @@ class Test(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() fakes.fake_data_store = {} + super(Test, self).tearDown() def test_authorize_user(self): f = fakes.FakeAuthManager() @@ -128,8 +130,9 @@ class Test(unittest.TestCase): self.assertEqual(result.status, '401 Unauthorized') -class TestLimiter(unittest.TestCase): +class TestLimiter(test.TestCase): def setUp(self): + super(TestLimiter, self).setUp() self.stubs = stubout.StubOutForTesting() self.stubs.Set(nova.api.openstack.auth.AuthMiddleware, '__init__', fakes.fake_auth_init) @@ -141,6 +144,7 @@ class TestLimiter(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() fakes.fake_data_store = {} + super(TestLimiter, self).tearDown() def test_authorize_token(self): f = fakes.FakeAuthManager() @@ -161,7 +165,3 @@ class TestLimiter(unittest.TestCase): result = req.get_response(fakes.wsgi_app()) self.assertEqual(result.status, '200 OK') self.assertEqual(result.headers['X-Test-Success'], 'True') - - -if __name__ == '__main__': - unittest.main() diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py index 9d9837cc9..59d850157 100644 --- a/nova/tests/api/openstack/test_common.py +++ b/nova/tests/api/openstack/test_common.py @@ -19,14 +19,14 @@ Test suites for 'common' code used throughout the OpenStack HTTP API. """ -import unittest from webob import Request +from nova import test from nova.api.openstack.common import limited -class LimiterTest(unittest.TestCase): +class LimiterTest(test.TestCase): """ Unit tests for the `nova.api.openstack.common.limited` method which takes in a list of items and, depending on the 'offset' and 'limit' GET params, @@ -37,6 +37,7 @@ class LimiterTest(unittest.TestCase): """ Run before each test. """ + super(LimiterTest, self).setUp() self.tiny = range(1) self.small = range(10) self.medium = range(1000) diff --git a/nova/tests/api/openstack/test_faults.py b/nova/tests/api/openstack/test_faults.py index fda2b5ede..7667753f4 100644 --- a/nova/tests/api/openstack/test_faults.py +++ b/nova/tests/api/openstack/test_faults.py @@ -15,15 +15,15 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest import webob import webob.dec import webob.exc +from nova import test from nova.api.openstack import faults -class TestFaults(unittest.TestCase): +class TestFaults(test.TestCase): def test_fault_parts(self): req = webob.Request.blank('/.xml') diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 1bdaea161..761265965 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -15,18 +15,18 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest - import stubout import webob +from nova import test import nova.api from nova.api.openstack import flavors from nova.tests.api.openstack import fakes -class FlavorsTest(unittest.TestCase): +class FlavorsTest(test.TestCase): def setUp(self): + super(FlavorsTest, self).setUp() self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} fakes.FakeAuthDatabase.data = {} @@ -36,6 +36,7 @@ class FlavorsTest(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() + super(FlavorsTest, self).tearDown() def test_get_flavor_list(self): req = webob.Request.blank('/v1.0/flavors') @@ -43,6 +44,3 @@ class FlavorsTest(unittest.TestCase): def test_get_flavor_by_id(self): pass - -if __name__ == '__main__': - unittest.main() diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 8ab4d7569..e232bc3d5 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -22,7 +22,6 @@ and as a WSGI layer import json import datetime -import unittest import stubout import webob @@ -30,6 +29,7 @@ import webob from nova import context from nova import exception from nova import flags +from nova import test from nova import utils import nova.api.openstack from nova.api.openstack import images @@ -130,12 +130,13 @@ class BaseImageServiceTests(object): self.assertEquals(1, num_images) -class LocalImageServiceTest(unittest.TestCase, +class LocalImageServiceTest(test.TestCase, BaseImageServiceTests): """Tests the local image service""" def setUp(self): + super(LocalImageServiceTest, self).setUp() self.stubs = stubout.StubOutForTesting() service_class = 'nova.image.local.LocalImageService' self.service = utils.import_object(service_class) @@ -145,14 +146,16 @@ class LocalImageServiceTest(unittest.TestCase, self.service.delete_all() self.service.delete_imagedir() self.stubs.UnsetAll() + super(LocalImageServiceTest, self).tearDown() -class GlanceImageServiceTest(unittest.TestCase, +class GlanceImageServiceTest(test.TestCase, BaseImageServiceTests): """Tests the local image service""" def setUp(self): + super(GlanceImageServiceTest, self).setUp() self.stubs = stubout.StubOutForTesting() fakes.stub_out_glance(self.stubs) fakes.stub_out_compute_api_snapshot(self.stubs) @@ -163,9 +166,10 @@ class GlanceImageServiceTest(unittest.TestCase, def tearDown(self): self.stubs.UnsetAll() + super(GlanceImageServiceTest, self).tearDown() -class ImageControllerWithGlanceServiceTest(unittest.TestCase): +class ImageControllerWithGlanceServiceTest(test.TestCase): """Test of the OpenStack API /images application controller""" @@ -194,6 +198,7 @@ class ImageControllerWithGlanceServiceTest(unittest.TestCase): 'image_type': 'ramdisk'}] def setUp(self): + super(ImageControllerWithGlanceServiceTest, self).setUp() self.orig_image_service = FLAGS.image_service FLAGS.image_service = 'nova.image.glance.GlanceImageService' self.stubs = stubout.StubOutForTesting() @@ -208,6 +213,7 @@ class ImageControllerWithGlanceServiceTest(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() FLAGS.image_service = self.orig_image_service + super(ImageControllerWithGlanceServiceTest, self).tearDown() def test_get_image_index(self): req = webob.Request.blank('/v1.0/images') diff --git a/nova/tests/api/openstack/test_ratelimiting.py b/nova/tests/api/openstack/test_ratelimiting.py index 4c9d6bc23..9ae90ee20 100644 --- a/nova/tests/api/openstack/test_ratelimiting.py +++ b/nova/tests/api/openstack/test_ratelimiting.py @@ -1,15 +1,16 @@ import httplib import StringIO import time -import unittest import webob +from nova import test import nova.api.openstack.ratelimiting as ratelimiting -class LimiterTest(unittest.TestCase): +class LimiterTest(test.TestCase): def setUp(self): + super(LimiterTest, self).setUp() self.limits = { 'a': (5, ratelimiting.PER_SECOND), 'b': (5, ratelimiting.PER_MINUTE), @@ -83,9 +84,10 @@ class FakeLimiter(object): return self._delay -class WSGIAppTest(unittest.TestCase): +class WSGIAppTest(test.TestCase): def setUp(self): + super(WSGIAppTest, self).setUp() self.limiter = FakeLimiter(self) self.app = ratelimiting.WSGIApp(self.limiter) @@ -206,7 +208,7 @@ def wire_HTTPConnection_to_WSGI(host, app): httplib.HTTPConnection = HTTPConnectionDecorator(httplib.HTTPConnection) -class WSGIAppProxyTest(unittest.TestCase): +class WSGIAppProxyTest(test.TestCase): def setUp(self): """Our WSGIAppProxy is going to call across an HTTPConnection to a @@ -218,6 +220,7 @@ class WSGIAppProxyTest(unittest.TestCase): at the WSGIApp. And the limiter isn't real -- it's a fake that behaves the way we tell it to. """ + super(WSGIAppProxyTest, self).setUp() self.limiter = FakeLimiter(self) app = ratelimiting.WSGIApp(self.limiter) wire_HTTPConnection_to_WSGI('100.100.100.100:80', app) @@ -238,7 +241,3 @@ class WSGIAppProxyTest(unittest.TestCase): self.limiter.mock('murder', 'brutus', None) self.proxy.perform('stab', 'brutus') self.assertRaises(AssertionError, shouldRaise) - - -if __name__ == '__main__': - unittest.main() diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index a7be0796e..ea29dcf9b 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -17,13 +17,13 @@ import datetime import json -import unittest import stubout import webob from nova import db from nova import flags +from nova import test import nova.api.openstack from nova.api.openstack import servers import nova.db.api @@ -108,9 +108,10 @@ def fake_compute_api(cls, req, id): return True -class ServersTest(unittest.TestCase): +class ServersTest(test.TestCase): def setUp(self): + super(ServersTest, self).setUp() self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} fakes.FakeAuthDatabase.data = {} @@ -141,6 +142,7 @@ class ServersTest(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() FLAGS.allow_admin_api = self.allow_admin + super(ServersTest, self).tearDown() def test_get_server_by_id(self): req = webob.Request.blank('/v1.0/servers/1') @@ -410,7 +412,3 @@ class ServersTest(unittest.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status, '202 Accepted') self.assertEqual(self.server_delete_called, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/nova/tests/api/openstack/test_shared_ip_groups.py b/nova/tests/api/openstack/test_shared_ip_groups.py index c2fc3a203..b4de2ef41 100644 --- a/nova/tests/api/openstack/test_shared_ip_groups.py +++ b/nova/tests/api/openstack/test_shared_ip_groups.py @@ -15,19 +15,20 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest - import stubout +from nova import test from nova.api.openstack import shared_ip_groups -class SharedIpGroupsTest(unittest.TestCase): +class SharedIpGroupsTest(test.TestCase): def setUp(self): + super(SharedIpGroupsTest, self).setUp() self.stubs = stubout.StubOutForTesting() def tearDown(self): self.stubs.UnsetAll() + super(SharedIpGroupsTest, self).tearDown() def test_get_shared_ip_groups(self): pass diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index df497ef1b..555b206b9 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest import stubout import webob @@ -22,6 +21,7 @@ import json import nova.db from nova import context from nova import flags +from nova import test from nova.api.openstack import zones from nova.tests.api.openstack import fakes @@ -60,8 +60,9 @@ def zone_get_all(context): password='qwerty')] -class ZonesTest(unittest.TestCase): +class ZonesTest(test.TestCase): def setUp(self): + super(ZonesTest, self).setUp() self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} fakes.FakeAuthDatabase.data = {} @@ -81,6 +82,7 @@ class ZonesTest(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() FLAGS.allow_admin_api = self.allow_admin + super(ZonesTest, self).tearDown() def test_get_zone_list(self): req = webob.Request.blank('/v1.0/zones') @@ -134,7 +136,3 @@ class ZonesTest(unittest.TestCase): self.assertEqual(res_dict['zone']['id'], 1) self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') self.assertFalse('username' in res_dict['zone']) - - -if __name__ == '__main__': - unittest.main() diff --git a/nova/tests/api/test_wsgi.py b/nova/tests/api/test_wsgi.py index 44e2d615c..2c7852214 100644 --- a/nova/tests/api/test_wsgi.py +++ b/nova/tests/api/test_wsgi.py @@ -21,7 +21,7 @@ Test WSGI basics and provide some helper functions for other WSGI tests. """ -import unittest +from nova import test import routes import webob @@ -29,7 +29,7 @@ import webob from nova import wsgi -class Test(unittest.TestCase): +class Test(test.TestCase): def test_debug(self): @@ -92,7 +92,7 @@ class Test(unittest.TestCase): self.assertNotEqual(result.body, "123") -class SerializerTest(unittest.TestCase): +class SerializerTest(test.TestCase): def match(self, url, accept, expect): input_dict = dict(servers=dict(a=(2, 3))) diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py index da86e6e11..5a1be08eb 100644 --- a/nova/tests/objectstore_unittest.py +++ b/nova/tests/objectstore_unittest.py @@ -311,4 +311,5 @@ class S3APITestCase(test.TestCase): self.auth_manager.delete_user('admin') self.auth_manager.delete_project('admin') stop_listening = defer.maybeDeferred(self.listening_port.stopListening) + super(S3APITestCase, self).tearDown() return defer.DeferredList([stop_listening]) diff --git a/nova/tests/test_direct.py b/nova/tests/test_direct.py index 7656f5396..b6bfab534 100644 --- a/nova/tests/test_direct.py +++ b/nova/tests/test_direct.py @@ -52,6 +52,7 @@ class DirectTestCase(test.TestCase): def tearDown(self): direct.ROUTES = {} + super(DirectTestCase, self).tearDown() def test_delegated_auth(self): req = webob.Request.blank('/fake/context') diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py index 9d458244b..1bad364e5 100644 --- a/nova/tests/test_scheduler.py +++ b/nova/tests/test_scheduler.py @@ -150,6 +150,7 @@ class SimpleDriverTestCase(test.TestCase): def tearDown(self): self.manager.delete_user(self.user) self.manager.delete_project(self.project) + super(SimpleDriverTestCase, self).tearDown() def _create_instance(self, **kwargs): """Create a test instance""" diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 6e5a0114b..7aadd65d5 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -206,9 +206,9 @@ class LibvirtConnTestCase(test.TestCase): self.assertEquals(uri, testuri) def tearDown(self): - super(LibvirtConnTestCase, self).tearDown() self.manager.delete_project(self.project) self.manager.delete_user(self.user) + super(LibvirtConnTestCase, self).tearDown() class IptablesFirewallTestCase(test.TestCase): @@ -388,6 +388,7 @@ class NWFilterTestCase(test.TestCase): def tearDown(self): self.manager.delete_project(self.project) self.manager.delete_user(self.user) + super(NWFilterTestCase, self).tearDown() def test_cidr_rule_nwfilter_xml(self): cloud_controller = cloud.CloudController() -- cgit From 3b2a8b516fd9dbd08563c709e14323d571b8efee Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 01:52:07 -0800 Subject: speed up network tests --- nova/tests/__init__.py | 3 ++- nova/tests/fake_flags.py | 4 ++-- nova/tests/test_network.py | 8 +++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/nova/tests/__init__.py b/nova/tests/__init__.py index 5472bdaf2..5afd9389d 100644 --- a/nova/tests/__init__.py +++ b/nova/tests/__init__.py @@ -48,7 +48,8 @@ def setup(): ctxt = context.get_admin_context() network_manager.VlanManager().create_networks(ctxt, FLAGS.fixed_range, - 5, 16, + FLAGS.num_networks, + FLAGS.network_size, FLAGS.fixed_range_v6, FLAGS.vlan_start, FLAGS.vpn_start, diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index 575fefff6..a8291a968 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -29,8 +29,8 @@ FLAGS.auth_driver = 'nova.auth.dbdriver.DbDriver' flags.DECLARE('network_size', 'nova.network.manager') flags.DECLARE('num_networks', 'nova.network.manager') flags.DECLARE('fake_network', 'nova.network.manager') -FLAGS.network_size = 16 -FLAGS.num_networks = 5 +FLAGS.network_size = 8 +FLAGS.num_networks = 2 FLAGS.fake_network = True flags.DECLARE('num_shelves', 'nova.volume.driver') flags.DECLARE('blades_per_shelf', 'nova.volume.driver') diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 00f9323f3..ccb5298bd 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -42,15 +42,13 @@ class NetworkTestCase(test.TestCase): # flags in the corresponding section in nova-dhcpbridge self.flags(connection_type='fake', fake_call=True, - fake_network=True, - network_size=16, - num_networks=5) + fake_network=True) self.manager = manager.AuthManager() self.user = self.manager.create_user('netuser', 'netuser', 'netuser') self.projects = [] self.network = utils.import_object(FLAGS.network_manager) self.context = context.RequestContext(project=None, user=self.user) - for i in range(5): + for i in range(FLAGS.num_networks): name = 'project%s' % i project = self.manager.create_project(name, 'netuser', name) self.projects.append(project) @@ -192,7 +190,7 @@ class NetworkTestCase(test.TestCase): first = self._create_address(0) lease_ip(first) instance_ids = [] - for i in range(1, 5): + for i in range(1, FLAGS.num_networks): instance_ref = self._create_instance(i, mac=utils.generate_mac()) instance_ids.append(instance_ref['id']) address = self._create_address(i, instance_ref['id']) -- cgit From a9075d4edc126b95910258face7f00073449073d Mon Sep 17 00:00:00 2001 From: Salvatore Orlando <salvatore.orlando@eu.citrix.com> Date: Wed, 23 Feb 2011 10:27:30 +0000 Subject: FlatManager.init_host now inhibits call to method in superclass. Floating IP methods have been redefined in FlatManager to raise NotImplementedError --- nova/network/manager.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/nova/network/manager.py b/nova/network/manager.py index c6eba225e..a7f263daa 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -322,6 +322,17 @@ class FlatManager(NetworkManager): """ timeout_fixed_ips = False + def init_host(self): + """Do any initialization that needs to be run if this is a + standalone service. + """ + #Fix for bug 723298 - do not call init_host on superclass + #Following code has been copied for NetworkManager.init_host + ctxt = context.get_admin_context() + for network in self.db.host_get_networks(ctxt, self.host): + self._on_set_network_host(ctxt, network['id']) + + def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Gets a fixed ip from the pool.""" # TODO(vish): when this is called by compute, we can associate compute @@ -406,6 +417,22 @@ class FlatManager(NetworkManager): net['dns'] = FLAGS.flat_network_dns self.db.network_update(context, network_id, net) + def allocate_floating_ip(self, context, project_id): + #Fix for bug 723298 + raise NotImplementedError() + + def associate_floating_ip(self, context, floating_address, fixed_address): + #Fix for bug 723298 + raise NotImplementedError() + + def disassociate_floating_ip(self, context, floating_address): + #Fix for bug 723298 + raise NotImplementedError() + + def deallocate_floating_ip(self, context, floating_address): + #Fix for bug 723298 + raise NotImplementedError() + class FlatDHCPManager(FlatManager): """Flat networking with dhcp. -- cgit From 79a4c527fbb75bc563721fa23be4ea4aa97b39ee Mon Sep 17 00:00:00 2001 From: Salvatore Orlando <salvatore.orlando@eu.citrix.com> Date: Wed, 23 Feb 2011 11:49:47 +0000 Subject: Fixed pep8 errors --- nova/network/manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index a7f263daa..1df193be0 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -332,7 +332,6 @@ class FlatManager(NetworkManager): for network in self.db.host_get_networks(ctxt, self.host): self._on_set_network_host(ctxt, network['id']) - def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Gets a fixed ip from the pool.""" # TODO(vish): when this is called by compute, we can associate compute -- cgit From d160455b77d7e180f252f4b412e3f65d7286b51f Mon Sep 17 00:00:00 2001 From: termie <github@anarkystic.com> Date: Wed, 23 Feb 2011 08:45:27 -0800 Subject: allow users to omit 'nova.tests' with run_tests --- run_tests.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/run_tests.py b/run_tests.py index 6d96454b9..47e3ee317 100644 --- a/run_tests.py +++ b/run_tests.py @@ -17,6 +17,23 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Unittest runner for Nova. + +To run all tests + python run_tests.py + +To run a single test: + python run_tests.py test_compute:ComputeTestCase.test_run_terminate + +To run a single test module: + python run_tests.py test_compute + + or + + python run_tests.py api.test_wsgi + +""" + import gettext import os import unittest @@ -62,6 +79,15 @@ class NovaTestRunner(core.TextTestRunner): if __name__ == '__main__': logging.setup() + # If any argument looks like a test name but doesn't have "nova.tests" in + # front of it, automatically add that so we don't have to type as much + argv = [] + for x in sys.argv: + if x.startswith('test_'): + argv.append('nova.tests.%s' % x) + else: + argv.append(x) + c = config.Config(stream=sys.stdout, env=os.environ, verbosity=3, @@ -70,4 +96,4 @@ if __name__ == '__main__': runner = NovaTestRunner(stream=c.stream, verbosity=c.verbosity, config=c) - sys.exit(not core.run(config=c, testRunner=runner)) + sys.exit(not core.run(config=c, testRunner=runner, argv=argv)) -- cgit From 2bbbfc5af62db57158a8d6aa26912ba234d0296e Mon Sep 17 00:00:00 2001 From: termie <github@anarkystic.com> Date: Wed, 23 Feb 2011 08:46:11 -0800 Subject: dump error output directly on short import errors --- run_tests.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/run_tests.sh b/run_tests.sh index 70212cc6a..4e8159e7b 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -40,7 +40,18 @@ done function run_tests { # Just run the test suites in current environment ${wrapper} rm -f nova.sqlite - ${wrapper} $NOSETESTS + ${wrapper} $NOSETESTS 2> run_tests.err.log + # If we get some short import error right away, print the error log directly + RESULT=$? + if [ "$RESULT" -ne "0" ]; + then + ERRSIZE=`wc -l run_tests.err.log | awk '{print \$1}'` + if [ "$ERRSIZE" -lt "40" ]; + then + cat run_tests.err.log + fi + fi + return $RESULT } NOSETESTS="python run_tests.py $noseargs" -- cgit From ef0dfb6809f31cfe8ca8056892fc9dcc2f00a0d7 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 09:40:43 -0800 Subject: Changed unit test to refer to compute API, per Todd's suggestion. Avoids needing to extend our implementation of the EC2 API. --- nova/api/ec2/cloud.py | 6 +----- nova/tests/test_quota.py | 3 ++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 5db865b02..882cdcfc9 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -783,9 +783,6 @@ class CloudController(object): def run_instances(self, context, **kwargs): max_count = int(kwargs.get('max_count', 1)) - # NOTE(justinsb): the EC2 API doesn't support metadata here, but this - # is needed for the unit tests. Maybe the unit tests shouldn't be - # calling the EC2 code instances = self.compute_api.create(context, instance_type=instance_types.get_by_type( kwargs.get('instance_type', None)), @@ -800,8 +797,7 @@ class CloudController(object): user_data=kwargs.get('user_data'), security_group=kwargs.get('security_group'), availability_zone=kwargs.get('placement', {}).get( - 'AvailabilityZone'), - metadata=kwargs.get('metadata', [])) + 'AvailabilityZone')) return self._format_run_instances(context, instances[0]['reservation_id']) diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 36ccc273e..1e42fddf3 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -16,6 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. +from nova import compute from nova import context from nova import db from nova import flags @@ -168,7 +169,7 @@ class QuotaTestCase(test.TestCase): metadata = {} for i in range(FLAGS.quota_metadata_items + 1): metadata['key%s' % i] = 'value%s' % i - self.assertRaises(quota.QuotaError, self.cloud.run_instances, + self.assertRaises(quota.QuotaError, compute.API().create, self.context, min_count=1, max_count=1, -- cgit From 94bd7e2c45ca971e318813bb1e897fb5e79ab7bd Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh.kearney@rackspace.com> Date: Wed, 23 Feb 2011 12:05:46 -0600 Subject: Removed pass --- nova/virt/xenapi/vmops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index bd57dc9a1..7bffd8c6c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -501,7 +501,6 @@ class VMOps(object): # Plug the VBD into the target instance self._session.call_xenapi("Async.VBD.plug", vbd_ref) - pass def unrescue(self, instance, callback): """Unrescue the specified instance""" -- cgit From 3c6a44327f0015cb531b06fbce38eb4e3a04ce6a Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz <emaildericky@gmail.com> Date: Wed, 23 Feb 2011 20:13:50 +0100 Subject: Fixes lp715424, code now checks network range can fit num_networks * network_size --- nova/network/manager.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nova/network/manager.py b/nova/network/manager.py index c6eba225e..1b8627562 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -521,6 +521,11 @@ class VlanManager(NetworkManager): ' than 4094')) fixed_net = IPy.IP(cidr) + if fixed_net.len() < num_networks * network_size: + raise ValueError(_('The network range is not big enough to fit %s' + ' networks of size %s' % + (num_networks, network_size))) + fixed_net_v6 = IPy.IP(cidr_v6) network_size_v6 = 1 << 64 significant_bits_v6 = 64 -- cgit From 3c09d486f862de7069b848e8124787cfbf4247f8 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 11:20:52 -0800 Subject: use flags for sqlite db names and fix flags in dhcpbridge --- bin/nova-dhcpbridge | 13 +------------ nova/flags.py | 3 ++- nova/test.py | 11 +++++++---- nova/tests/__init__.py | 7 ++++++- nova/tests/fake_flags.py | 2 +- run_tests.py | 11 ++++++++--- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge index 04a1771f0..3dd9de367 100755 --- a/bin/nova-dhcpbridge +++ b/bin/nova-dhcpbridge @@ -105,18 +105,7 @@ def main(): logging.setup() interface = os.environ.get('DNSMASQ_INTERFACE', 'br0') if int(os.environ.get('TESTING', '0')): - FLAGS.fake_rabbit = True - FLAGS.network_size = 16 - FLAGS.connection_type = 'fake' - FLAGS.fake_network = True - FLAGS.auth_driver = 'nova.auth.dbdriver.DbDriver' - FLAGS.num_networks = 5 - path = os.path.abspath(os.path.join(os.path.dirname(__file__), - '..', - 'nova', - 'tests', - 'tests.sqlite')) - FLAGS.sql_connection = 'sqlite:///%s' % path + from nova.tests import fake_flags action = argv[1] if action in ['add', 'del', 'old']: mac = argv[2] diff --git a/nova/flags.py b/nova/flags.py index f64a62da9..ab1adc6e3 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -285,8 +285,9 @@ DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../'), DEFINE_string('logdir', None, 'output to a per-service log file in named ' 'directory') +DEFINE_string('sqlite_db', 'nova.sqlite', 'file name for sqlite') DEFINE_string('sql_connection', - 'sqlite:///$state_path/nova.sqlite', + 'sqlite:///$state_path/$sqlite_db', 'connection string for sql database') DEFINE_integer('sql_idle_timeout', 3600, diff --git a/nova/test.py b/nova/test.py index 6cbbb9e8e..9c961a987 100644 --- a/nova/test.py +++ b/nova/test.py @@ -22,12 +22,14 @@ Allows overriding of flags for use of fakes, and some black magic for inline callbacks. """ + import datetime +import os +import shutil import uuid import unittest import mox -import shutil import stubout from nova import context @@ -39,8 +41,8 @@ from nova import service FLAGS = flags.FLAGS -flags.DEFINE_bool('flush_db', True, - 'Flush the database before running fake tests') +flags.DEFINE_string('sqlite_clean_db', 'clean.sqlite', + 'File name of clean sqlite db') flags.DEFINE_bool('fake_tests', True, 'should we use everything for testing') @@ -65,7 +67,8 @@ class TestCase(unittest.TestCase): # now that we have some required db setup for the system # to work properly. self.start = datetime.datetime.utcnow() - shutil.copyfile("clean.sqlite", "tests.sqlite") + shutil.copyfile(os.path.join(FLAGS.state_path, FLAGS.sqlite_clean_db), + os.path.join(FLAGS.state_path, FLAGS.sqlite_db)) # emulate some of the mox stuff, we can't use the metaclass # because it screws with our generators diff --git a/nova/tests/__init__.py b/nova/tests/__init__.py index 5afd9389d..dbd433054 100644 --- a/nova/tests/__init__.py +++ b/nova/tests/__init__.py @@ -37,13 +37,17 @@ setattr(__builtin__, '_', lambda x: x) def setup(): + import os import shutil + from nova import context from nova import flags from nova.db import migration from nova.network import manager as network_manager from nova.tests import fake_flags + FLAGS = flags.FLAGS + migration.db_sync() ctxt = context.get_admin_context() network_manager.VlanManager().create_networks(ctxt, @@ -54,4 +58,5 @@ def setup(): FLAGS.vlan_start, FLAGS.vpn_start, ) - shutil.copyfile("tests.sqlite", "clean.sqlite") + shutil.copyfile(os.path.join(FLAGS.state_path, FLAGS.sqlite_db), + os.path.join(FLAGS.state_path, FLAGS.sqlite_clean_db)) diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index a8291a968..dcc8a676d 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -39,6 +39,6 @@ FLAGS.num_shelves = 2 FLAGS.blades_per_shelf = 4 FLAGS.iscsi_num_targets = 8 FLAGS.verbose = True -FLAGS.sql_connection = 'sqlite:///tests.sqlite' +FLAGS.sqlite_db = "tests.sqlite" FLAGS.use_ipv6 = True FLAGS.logfile = 'tests.log' diff --git a/run_tests.py b/run_tests.py index 01a1cf8bc..88c42bd31 100644 --- a/run_tests.py +++ b/run_tests.py @@ -46,13 +46,17 @@ import unittest import sys from nose import config -from nose import result from nose import core +from nose import result +from nova import flags from nova import log as logging from nova.tests import fake_flags +FLAGS = flags.FLAGS + + class _AnsiColorizer(object): """ A colorizer is an object that loosely wraps around a stream, allowing @@ -259,10 +263,11 @@ class NovaTestRunner(core.TextTestRunner): if __name__ == '__main__': logging.setup() - testdir = os.path.abspath(os.path.join("nova","tests")) - testdb = os.path.join(testdir, "tests.sqlite") + testdb = os.path.join(FLAGS.state_path, + FLAGS.sqlite_db) if os.path.exists(testdb): os.unlink(testdb) + testdir = os.path.abspath(os.path.join("nova","tests")) c = config.Config(stream=sys.stdout, env=os.environ, verbosity=3, -- cgit From 48d4054e093a2faccbd819de8e9e02c03d28cda0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 11:26:38 -0800 Subject: fix for failing describe_instances test --- nova/api/ec2/cloud.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 1b96567eb..e219fb30c 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -675,7 +675,8 @@ class CloudController(object): instances = [] for ec2_id in instance_id: internal_id = ec2_id_to_id(ec2_id) - instance = self.compute_api.get(context, internal_id) + instance = self.compute_api.get(context, + instance_id=internal_id) instances.append(instance) else: instances = self.compute_api.get_all(context, **kwargs) -- cgit From 1b2d67e769ff1a6fe68a933e8b966d72588ce8ac Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 11:44:09 -0800 Subject: merged trunk --- nova/test.py | 18 ++++-------------- nova/tests/__init__.py | 13 +++++++++++++ nova/tests/api/openstack/__init__.py | 4 ++-- nova/tests/api/openstack/test_adminapi.py | 8 ++++---- nova/tests/api/openstack/test_api.py | 4 ++-- nova/tests/api/openstack/test_auth.py | 14 +++++++------- nova/tests/api/openstack/test_common.py | 5 +++-- nova/tests/api/openstack/test_faults.py | 4 ++-- nova/tests/api/openstack/test_flavors.py | 10 ++++------ nova/tests/api/openstack/test_images.py | 14 ++++++++++---- nova/tests/api/openstack/test_ratelimiting.py | 15 +++++++-------- nova/tests/api/openstack/test_servers.py | 10 ++++------ nova/tests/api/openstack/test_shared_ip_groups.py | 7 ++++--- nova/tests/api/openstack/test_zones.py | 10 ++++------ nova/tests/api/test_wsgi.py | 6 +++--- nova/tests/objectstore_unittest.py | 1 + nova/tests/test_direct.py | 1 + nova/tests/test_scheduler.py | 1 + nova/tests/test_virt.py | 3 ++- 19 files changed, 78 insertions(+), 70 deletions(-) diff --git a/nova/test.py b/nova/test.py index bff43b6c7..826a4bd11 100644 --- a/nova/test.py +++ b/nova/test.py @@ -26,15 +26,14 @@ import datetime import unittest import mox +import shutil import stubout from nova import context from nova import db from nova import fakerabbit from nova import flags -from nova import log as logging from nova import rpc -from nova.network import manager as network_manager FLAGS = flags.FLAGS @@ -65,14 +64,8 @@ class TestCase(unittest.TestCase): # to work properly. self.start = datetime.datetime.utcnow() ctxt = context.get_admin_context() - if db.network_count(ctxt) != 5: - network_manager.VlanManager().create_networks(ctxt, - FLAGS.fixed_range, - 5, 16, - FLAGS.fixed_range_v6, - FLAGS.vlan_start, - FLAGS.vpn_start, - ) + shutil.copyfile("tests.sqlite", "clean.sqlite") + assert(db.security_group_get_all(ctxt) == []) # emulate some of the mox stuff, we can't use the metaclass # because it screws with our generators @@ -86,6 +79,7 @@ class TestCase(unittest.TestCase): def tearDown(self): """Runs after each test method to finalize/tear down test environment.""" + shutil.copyfile("clean.sqlite", "tests.sqlite") try: self.mox.UnsetStubs() self.stubs.UnsetAll() @@ -93,9 +87,6 @@ class TestCase(unittest.TestCase): self.mox.VerifyAll() # NOTE(vish): Clean up any ips associated during the test. ctxt = context.get_admin_context() - db.fixed_ip_disassociate_all_by_timeout(ctxt, FLAGS.host, - self.start) - db.network_disassociate_all(ctxt) rpc.Consumer.attach_to_eventlet = self.originalAttach for x in self.injected: try: @@ -106,7 +97,6 @@ class TestCase(unittest.TestCase): if FLAGS.fake_rabbit: fakerabbit.reset_all() - db.security_group_destroy_all(ctxt) super(TestCase, self).tearDown() finally: self.reset_flags() diff --git a/nova/tests/__init__.py b/nova/tests/__init__.py index 592d5bea9..d3ab02887 100644 --- a/nova/tests/__init__.py +++ b/nova/tests/__init__.py @@ -38,4 +38,17 @@ setattr(__builtin__, '_', lambda x: x) def setup(): from nova.db import migration + from nova.network import manager as network_manager + from nova import context + from nova import flags + from nova.tests import fake_flags + FLAGS = flags.FLAGS migration.db_sync() + ctxt = context.get_admin_context() + network_manager.VlanManager().create_networks(ctxt, + FLAGS.fixed_range, + 5, 16, + FLAGS.fixed_range_v6, + FLAGS.vlan_start, + FLAGS.vpn_start, + ) diff --git a/nova/tests/api/openstack/__init__.py b/nova/tests/api/openstack/__init__.py index 77b1dd37f..e18120285 100644 --- a/nova/tests/api/openstack/__init__.py +++ b/nova/tests/api/openstack/__init__.py @@ -16,7 +16,7 @@ # under the License. import webob.dec -import unittest +from nova import test from nova import context from nova import flags @@ -33,7 +33,7 @@ def simple_wsgi(req): return "" -class RateLimitingMiddlewareTest(unittest.TestCase): +class RateLimitingMiddlewareTest(test.TestCase): def test_get_action_name(self): middleware = RateLimitingMiddleware(simple_wsgi) diff --git a/nova/tests/api/openstack/test_adminapi.py b/nova/tests/api/openstack/test_adminapi.py index 73120c31d..125fbe973 100644 --- a/nova/tests/api/openstack/test_adminapi.py +++ b/nova/tests/api/openstack/test_adminapi.py @@ -15,13 +15,13 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest import stubout import webob from paste import urlmap from nova import flags +from nova import test from nova.api import openstack from nova.api.openstack import ratelimiting from nova.api.openstack import auth @@ -30,9 +30,10 @@ from nova.tests.api.openstack import fakes FLAGS = flags.FLAGS -class AdminAPITest(unittest.TestCase): +class AdminAPITest(test.TestCase): def setUp(self): + super(AdminAPITest, self).setUp() self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} fakes.FakeAuthDatabase.data = {} @@ -44,6 +45,7 @@ class AdminAPITest(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() FLAGS.allow_admin_api = self.allow_admin + super(AdminAPITest, self).tearDown() def test_admin_enabled(self): FLAGS.allow_admin_api = True @@ -61,5 +63,3 @@ class AdminAPITest(unittest.TestCase): self.assertEqual(res.status_int, 200) # TODO: Confirm admin operations are unavailable. -if __name__ == '__main__': - unittest.main() diff --git a/nova/tests/api/openstack/test_api.py b/nova/tests/api/openstack/test_api.py index db0fe1060..5112c486f 100644 --- a/nova/tests/api/openstack/test_api.py +++ b/nova/tests/api/openstack/test_api.py @@ -15,17 +15,17 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest import webob.exc import webob.dec from webob import Request +from nova import test from nova.api import openstack from nova.api.openstack import faults -class APITest(unittest.TestCase): +class APITest(test.TestCase): def _wsgi_app(self, inner_app): # simpler version of the app than fakes.wsgi_app diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py index 0dd65d321..13f6c3a1c 100644 --- a/nova/tests/api/openstack/test_auth.py +++ b/nova/tests/api/openstack/test_auth.py @@ -16,7 +16,6 @@ # under the License. import datetime -import unittest import stubout import webob @@ -27,12 +26,14 @@ import nova.api.openstack.auth import nova.auth.manager from nova import auth from nova import context +from nova import test from nova.tests.api.openstack import fakes -class Test(unittest.TestCase): +class Test(test.TestCase): def setUp(self): + super(Test, self).setUp() self.stubs = stubout.StubOutForTesting() self.stubs.Set(nova.api.openstack.auth.AuthMiddleware, '__init__', fakes.fake_auth_init) @@ -45,6 +46,7 @@ class Test(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() fakes.fake_data_store = {} + super(Test, self).tearDown() def test_authorize_user(self): f = fakes.FakeAuthManager() @@ -128,8 +130,9 @@ class Test(unittest.TestCase): self.assertEqual(result.status, '401 Unauthorized') -class TestLimiter(unittest.TestCase): +class TestLimiter(test.TestCase): def setUp(self): + super(TestLimiter, self).setUp() self.stubs = stubout.StubOutForTesting() self.stubs.Set(nova.api.openstack.auth.AuthMiddleware, '__init__', fakes.fake_auth_init) @@ -141,6 +144,7 @@ class TestLimiter(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() fakes.fake_data_store = {} + super(TestLimiter, self).tearDown() def test_authorize_token(self): f = fakes.FakeAuthManager() @@ -161,7 +165,3 @@ class TestLimiter(unittest.TestCase): result = req.get_response(fakes.wsgi_app()) self.assertEqual(result.status, '200 OK') self.assertEqual(result.headers['X-Test-Success'], 'True') - - -if __name__ == '__main__': - unittest.main() diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py index 9d9837cc9..59d850157 100644 --- a/nova/tests/api/openstack/test_common.py +++ b/nova/tests/api/openstack/test_common.py @@ -19,14 +19,14 @@ Test suites for 'common' code used throughout the OpenStack HTTP API. """ -import unittest from webob import Request +from nova import test from nova.api.openstack.common import limited -class LimiterTest(unittest.TestCase): +class LimiterTest(test.TestCase): """ Unit tests for the `nova.api.openstack.common.limited` method which takes in a list of items and, depending on the 'offset' and 'limit' GET params, @@ -37,6 +37,7 @@ class LimiterTest(unittest.TestCase): """ Run before each test. """ + super(LimiterTest, self).setUp() self.tiny = range(1) self.small = range(10) self.medium = range(1000) diff --git a/nova/tests/api/openstack/test_faults.py b/nova/tests/api/openstack/test_faults.py index fda2b5ede..7667753f4 100644 --- a/nova/tests/api/openstack/test_faults.py +++ b/nova/tests/api/openstack/test_faults.py @@ -15,15 +15,15 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest import webob import webob.dec import webob.exc +from nova import test from nova.api.openstack import faults -class TestFaults(unittest.TestCase): +class TestFaults(test.TestCase): def test_fault_parts(self): req = webob.Request.blank('/.xml') diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 1bdaea161..761265965 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -15,18 +15,18 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest - import stubout import webob +from nova import test import nova.api from nova.api.openstack import flavors from nova.tests.api.openstack import fakes -class FlavorsTest(unittest.TestCase): +class FlavorsTest(test.TestCase): def setUp(self): + super(FlavorsTest, self).setUp() self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} fakes.FakeAuthDatabase.data = {} @@ -36,6 +36,7 @@ class FlavorsTest(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() + super(FlavorsTest, self).tearDown() def test_get_flavor_list(self): req = webob.Request.blank('/v1.0/flavors') @@ -43,6 +44,3 @@ class FlavorsTest(unittest.TestCase): def test_get_flavor_by_id(self): pass - -if __name__ == '__main__': - unittest.main() diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 8ab4d7569..e232bc3d5 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -22,7 +22,6 @@ and as a WSGI layer import json import datetime -import unittest import stubout import webob @@ -30,6 +29,7 @@ import webob from nova import context from nova import exception from nova import flags +from nova import test from nova import utils import nova.api.openstack from nova.api.openstack import images @@ -130,12 +130,13 @@ class BaseImageServiceTests(object): self.assertEquals(1, num_images) -class LocalImageServiceTest(unittest.TestCase, +class LocalImageServiceTest(test.TestCase, BaseImageServiceTests): """Tests the local image service""" def setUp(self): + super(LocalImageServiceTest, self).setUp() self.stubs = stubout.StubOutForTesting() service_class = 'nova.image.local.LocalImageService' self.service = utils.import_object(service_class) @@ -145,14 +146,16 @@ class LocalImageServiceTest(unittest.TestCase, self.service.delete_all() self.service.delete_imagedir() self.stubs.UnsetAll() + super(LocalImageServiceTest, self).tearDown() -class GlanceImageServiceTest(unittest.TestCase, +class GlanceImageServiceTest(test.TestCase, BaseImageServiceTests): """Tests the local image service""" def setUp(self): + super(GlanceImageServiceTest, self).setUp() self.stubs = stubout.StubOutForTesting() fakes.stub_out_glance(self.stubs) fakes.stub_out_compute_api_snapshot(self.stubs) @@ -163,9 +166,10 @@ class GlanceImageServiceTest(unittest.TestCase, def tearDown(self): self.stubs.UnsetAll() + super(GlanceImageServiceTest, self).tearDown() -class ImageControllerWithGlanceServiceTest(unittest.TestCase): +class ImageControllerWithGlanceServiceTest(test.TestCase): """Test of the OpenStack API /images application controller""" @@ -194,6 +198,7 @@ class ImageControllerWithGlanceServiceTest(unittest.TestCase): 'image_type': 'ramdisk'}] def setUp(self): + super(ImageControllerWithGlanceServiceTest, self).setUp() self.orig_image_service = FLAGS.image_service FLAGS.image_service = 'nova.image.glance.GlanceImageService' self.stubs = stubout.StubOutForTesting() @@ -208,6 +213,7 @@ class ImageControllerWithGlanceServiceTest(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() FLAGS.image_service = self.orig_image_service + super(ImageControllerWithGlanceServiceTest, self).tearDown() def test_get_image_index(self): req = webob.Request.blank('/v1.0/images') diff --git a/nova/tests/api/openstack/test_ratelimiting.py b/nova/tests/api/openstack/test_ratelimiting.py index 4c9d6bc23..9ae90ee20 100644 --- a/nova/tests/api/openstack/test_ratelimiting.py +++ b/nova/tests/api/openstack/test_ratelimiting.py @@ -1,15 +1,16 @@ import httplib import StringIO import time -import unittest import webob +from nova import test import nova.api.openstack.ratelimiting as ratelimiting -class LimiterTest(unittest.TestCase): +class LimiterTest(test.TestCase): def setUp(self): + super(LimiterTest, self).setUp() self.limits = { 'a': (5, ratelimiting.PER_SECOND), 'b': (5, ratelimiting.PER_MINUTE), @@ -83,9 +84,10 @@ class FakeLimiter(object): return self._delay -class WSGIAppTest(unittest.TestCase): +class WSGIAppTest(test.TestCase): def setUp(self): + super(WSGIAppTest, self).setUp() self.limiter = FakeLimiter(self) self.app = ratelimiting.WSGIApp(self.limiter) @@ -206,7 +208,7 @@ def wire_HTTPConnection_to_WSGI(host, app): httplib.HTTPConnection = HTTPConnectionDecorator(httplib.HTTPConnection) -class WSGIAppProxyTest(unittest.TestCase): +class WSGIAppProxyTest(test.TestCase): def setUp(self): """Our WSGIAppProxy is going to call across an HTTPConnection to a @@ -218,6 +220,7 @@ class WSGIAppProxyTest(unittest.TestCase): at the WSGIApp. And the limiter isn't real -- it's a fake that behaves the way we tell it to. """ + super(WSGIAppProxyTest, self).setUp() self.limiter = FakeLimiter(self) app = ratelimiting.WSGIApp(self.limiter) wire_HTTPConnection_to_WSGI('100.100.100.100:80', app) @@ -238,7 +241,3 @@ class WSGIAppProxyTest(unittest.TestCase): self.limiter.mock('murder', 'brutus', None) self.proxy.perform('stab', 'brutus') self.assertRaises(AssertionError, shouldRaise) - - -if __name__ == '__main__': - unittest.main() diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index a7be0796e..ea29dcf9b 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -17,13 +17,13 @@ import datetime import json -import unittest import stubout import webob from nova import db from nova import flags +from nova import test import nova.api.openstack from nova.api.openstack import servers import nova.db.api @@ -108,9 +108,10 @@ def fake_compute_api(cls, req, id): return True -class ServersTest(unittest.TestCase): +class ServersTest(test.TestCase): def setUp(self): + super(ServersTest, self).setUp() self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} fakes.FakeAuthDatabase.data = {} @@ -141,6 +142,7 @@ class ServersTest(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() FLAGS.allow_admin_api = self.allow_admin + super(ServersTest, self).tearDown() def test_get_server_by_id(self): req = webob.Request.blank('/v1.0/servers/1') @@ -410,7 +412,3 @@ class ServersTest(unittest.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status, '202 Accepted') self.assertEqual(self.server_delete_called, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/nova/tests/api/openstack/test_shared_ip_groups.py b/nova/tests/api/openstack/test_shared_ip_groups.py index c2fc3a203..b4de2ef41 100644 --- a/nova/tests/api/openstack/test_shared_ip_groups.py +++ b/nova/tests/api/openstack/test_shared_ip_groups.py @@ -15,19 +15,20 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest - import stubout +from nova import test from nova.api.openstack import shared_ip_groups -class SharedIpGroupsTest(unittest.TestCase): +class SharedIpGroupsTest(test.TestCase): def setUp(self): + super(SharedIpGroupsTest, self).setUp() self.stubs = stubout.StubOutForTesting() def tearDown(self): self.stubs.UnsetAll() + super(SharedIpGroupsTest, self).tearDown() def test_get_shared_ip_groups(self): pass diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index df497ef1b..555b206b9 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest import stubout import webob @@ -22,6 +21,7 @@ import json import nova.db from nova import context from nova import flags +from nova import test from nova.api.openstack import zones from nova.tests.api.openstack import fakes @@ -60,8 +60,9 @@ def zone_get_all(context): password='qwerty')] -class ZonesTest(unittest.TestCase): +class ZonesTest(test.TestCase): def setUp(self): + super(ZonesTest, self).setUp() self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} fakes.FakeAuthDatabase.data = {} @@ -81,6 +82,7 @@ class ZonesTest(unittest.TestCase): def tearDown(self): self.stubs.UnsetAll() FLAGS.allow_admin_api = self.allow_admin + super(ZonesTest, self).tearDown() def test_get_zone_list(self): req = webob.Request.blank('/v1.0/zones') @@ -134,7 +136,3 @@ class ZonesTest(unittest.TestCase): self.assertEqual(res_dict['zone']['id'], 1) self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') self.assertFalse('username' in res_dict['zone']) - - -if __name__ == '__main__': - unittest.main() diff --git a/nova/tests/api/test_wsgi.py b/nova/tests/api/test_wsgi.py index 44e2d615c..2c7852214 100644 --- a/nova/tests/api/test_wsgi.py +++ b/nova/tests/api/test_wsgi.py @@ -21,7 +21,7 @@ Test WSGI basics and provide some helper functions for other WSGI tests. """ -import unittest +from nova import test import routes import webob @@ -29,7 +29,7 @@ import webob from nova import wsgi -class Test(unittest.TestCase): +class Test(test.TestCase): def test_debug(self): @@ -92,7 +92,7 @@ class Test(unittest.TestCase): self.assertNotEqual(result.body, "123") -class SerializerTest(unittest.TestCase): +class SerializerTest(test.TestCase): def match(self, url, accept, expect): input_dict = dict(servers=dict(a=(2, 3))) diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py index da86e6e11..5a1be08eb 100644 --- a/nova/tests/objectstore_unittest.py +++ b/nova/tests/objectstore_unittest.py @@ -311,4 +311,5 @@ class S3APITestCase(test.TestCase): self.auth_manager.delete_user('admin') self.auth_manager.delete_project('admin') stop_listening = defer.maybeDeferred(self.listening_port.stopListening) + super(S3APITestCase, self).tearDown() return defer.DeferredList([stop_listening]) diff --git a/nova/tests/test_direct.py b/nova/tests/test_direct.py index 7656f5396..b6bfab534 100644 --- a/nova/tests/test_direct.py +++ b/nova/tests/test_direct.py @@ -52,6 +52,7 @@ class DirectTestCase(test.TestCase): def tearDown(self): direct.ROUTES = {} + super(DirectTestCase, self).tearDown() def test_delegated_auth(self): req = webob.Request.blank('/fake/context') diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py index 9d458244b..1bad364e5 100644 --- a/nova/tests/test_scheduler.py +++ b/nova/tests/test_scheduler.py @@ -150,6 +150,7 @@ class SimpleDriverTestCase(test.TestCase): def tearDown(self): self.manager.delete_user(self.user) self.manager.delete_project(self.project) + super(SimpleDriverTestCase, self).tearDown() def _create_instance(self, **kwargs): """Create a test instance""" diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 6e5a0114b..7aadd65d5 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -206,9 +206,9 @@ class LibvirtConnTestCase(test.TestCase): self.assertEquals(uri, testuri) def tearDown(self): - super(LibvirtConnTestCase, self).tearDown() self.manager.delete_project(self.project) self.manager.delete_user(self.user) + super(LibvirtConnTestCase, self).tearDown() class IptablesFirewallTestCase(test.TestCase): @@ -388,6 +388,7 @@ class NWFilterTestCase(test.TestCase): def tearDown(self): self.manager.delete_project(self.project) self.manager.delete_user(self.user) + super(NWFilterTestCase, self).tearDown() def test_cidr_rule_nwfilter_xml(self): cloud_controller = cloud.CloudController() -- cgit From 7c19fe87693b89d56ef9d9402d2667eacf297b97 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz <emaildericky@gmail.com> Date: Wed, 23 Feb 2011 20:51:27 +0100 Subject: Deleted trailing whitespace --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 1b8627562..7bdd56d7b 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -523,7 +523,7 @@ class VlanManager(NetworkManager): fixed_net = IPy.IP(cidr) if fixed_net.len() < num_networks * network_size: raise ValueError(_('The network range is not big enough to fit %s' - ' networks of size %s' % + ' networks of size %s' % (num_networks, network_size))) fixed_net_v6 = IPy.IP(cidr_v6) -- cgit From b09534dac05a3b4c127c633d8c050bb310a27166 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 11:52:10 -0800 Subject: put the redirection back in to run_tests.sh and fix terminal colors by using original stdout --- nova/tests/fake_flags.py | 1 - run_tests.py | 16 +++++++++------- run_tests.sh | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index 575fefff6..2b1919407 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -41,4 +41,3 @@ FLAGS.iscsi_num_targets = 8 FLAGS.verbose = True FLAGS.sql_connection = 'sqlite:///tests.sqlite' FLAGS.use_ipv6 = True -FLAGS.logfile = 'tests.log' diff --git a/run_tests.py b/run_tests.py index 01a1cf8bc..c78f88831 100644 --- a/run_tests.py +++ b/run_tests.py @@ -82,7 +82,7 @@ class _AnsiColorizer(object): try: return curses.tigetnum("colors") > 2 except curses.error: - curses.setupterm(fd=stream.fileno()) + curses.setupterm() return curses.tigetnum("colors") > 2 except: raise @@ -107,13 +107,13 @@ class _Win32Colorizer(object): See _AnsiColorizer docstring. """ def __init__(self, stream): - from win32console import GetStdHandle, STD_ERROR_HANDLE, \ + from win32console import GetStdHandle, STD_OUT_HANDLE, \ FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \ FOREGROUND_INTENSITY red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN, FOREGROUND_BLUE, FOREGROUND_INTENSITY) self.stream = stream - self.screenBuffer = GetStdHandle(STD_ERROR_HANDLE) + self.screenBuffer = GetStdHandle(STD_OUT_HANDLE) self._colors = { 'normal': red | green | blue, 'red': red | bold, @@ -129,7 +129,7 @@ class _Win32Colorizer(object): try: import win32console screenBuffer = win32console.GetStdHandle( - win32console.STD_ERROR_HANDLE) + win32console.STD_OUT_HANDLE) except ImportError: return False import pywintypes @@ -170,12 +170,14 @@ class NovaTestResult(result.TextTestResult): result.TextTestResult.__init__(self, *args, **kw) self._last_case = None self.colorizer = None + # NOTE(vish): reset stdout for the terminal check + stdout = sys.stdout + sys.stdout = sys.__stdout__ for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]: - # NOTE(vish): nose does funky stuff with stdout, so use stderr - # to setup the colorizer - if colorizer.supported(sys.stderr): + if colorizer.supported(): self.colorizer = colorizer(self.stream) break + sys.stdout = stdout def getDescription(self, test): return str(test) diff --git a/run_tests.sh b/run_tests.sh index e8433bc06..ebe236baf 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -39,7 +39,7 @@ done function run_tests { # Just run the test suites in current environment - ${wrapper} $NOSETESTS + ${wrapper} $NOSETESTS 2> run_tests.log } NOSETESTS="python run_tests.py $noseargs" -- cgit From f7751eedc0e895f90d48104e2110bc2b320735fc Mon Sep 17 00:00:00 2001 From: Dan Prince <dan.prince@rackspace.com> Date: Wed, 23 Feb 2011 13:53:02 -0600 Subject: Revert commit 709. This fixes issues with the Openstack API causing 'No user for access key admin' errors. --- nova/api/openstack/auth.py | 4 ++-- nova/tests/api/openstack/fakes.py | 8 ++------ nova/tests/api/openstack/test_auth.py | 6 +++--- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index c3fe0cc8c..1dfdd5318 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -120,8 +120,8 @@ class AuthMiddleware(wsgi.Middleware): req - webob.Request object """ ctxt = context.get_admin_context() - user = self.auth.get_user_from_access_key(username) - if user and user.secret == key: + user = self.auth.get_user_from_access_key(key) + if user and user.name == username: token_hash = hashlib.sha1('%s%s%f' % (username, key, time.time())).hexdigest() token_dict = {} diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index e0b7b8029..fb282f1c9 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -221,8 +221,7 @@ class FakeAuthDatabase(object): class FakeAuthManager(object): auth_data = {} - def add_user(self, user): - key = user.id + def add_user(self, key, user): FakeAuthManager.auth_data[key] = user def get_user(self, uid): @@ -235,10 +234,7 @@ class FakeAuthManager(object): return None def get_user_from_access_key(self, key): - for k, v in FakeAuthManager.auth_data.iteritems(): - if v.access == key: - return v - return None + return FakeAuthManager.auth_data.get(key, None) class FakeRateLimiter(object): diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py index eab78b50c..0dd65d321 100644 --- a/nova/tests/api/openstack/test_auth.py +++ b/nova/tests/api/openstack/test_auth.py @@ -48,7 +48,7 @@ class Test(unittest.TestCase): def test_authorize_user(self): f = fakes.FakeAuthManager() - f.add_user(nova.auth.manager.User(1, 'herp', 'herp', 'derp', None)) + f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None)) req = webob.Request.blank('/v1.0/') req.headers['X-Auth-User'] = 'herp' @@ -62,7 +62,7 @@ class Test(unittest.TestCase): def test_authorize_token(self): f = fakes.FakeAuthManager() - f.add_user(nova.auth.manager.User(1, 'herp', 'herp', 'derp', None)) + f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None)) req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'}) req.headers['X-Auth-User'] = 'herp' @@ -144,7 +144,7 @@ class TestLimiter(unittest.TestCase): def test_authorize_token(self): f = fakes.FakeAuthManager() - f.add_user(nova.auth.manager.User(1, 'herp', 'herp', 'derp', None)) + f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None)) req = webob.Request.blank('/v1.0/') req.headers['X-Auth-User'] = 'herp' -- cgit From 19dc13131b7fe512cb7897a888093b5c9a62e69d Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 12:00:02 -0800 Subject: move the deletion of the db into fixtures --- nova/tests/__init__.py | 8 ++++++-- run_tests.py | 4 ---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nova/tests/__init__.py b/nova/tests/__init__.py index dbd433054..7fba02a93 100644 --- a/nova/tests/__init__.py +++ b/nova/tests/__init__.py @@ -48,6 +48,9 @@ def setup(): FLAGS = flags.FLAGS + testdb = os.path.join(FLAGS.state_path, FLAGS.sqlite_db) + if os.path.exists(testdb): + os.unlink(testdb) migration.db_sync() ctxt = context.get_admin_context() network_manager.VlanManager().create_networks(ctxt, @@ -58,5 +61,6 @@ def setup(): FLAGS.vlan_start, FLAGS.vpn_start, ) - shutil.copyfile(os.path.join(FLAGS.state_path, FLAGS.sqlite_db), - os.path.join(FLAGS.state_path, FLAGS.sqlite_clean_db)) + + cleandb = os.path.join(FLAGS.state_path, FLAGS.sqlite_clean_db) + shutil.copyfile(testdb, cleandb) diff --git a/run_tests.py b/run_tests.py index bb6f0dd1e..5fb5ba32f 100644 --- a/run_tests.py +++ b/run_tests.py @@ -265,10 +265,6 @@ class NovaTestRunner(core.TextTestRunner): if __name__ == '__main__': logging.setup() - testdb = os.path.join(FLAGS.state_path, - FLAGS.sqlite_db) - if os.path.exists(testdb): - os.unlink(testdb) testdir = os.path.abspath(os.path.join("nova","tests")) c = config.Config(stream=sys.stdout, env=os.environ, -- cgit From 5283e1c131a21ea4963c702a7137536f7b894bb6 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 12:05:49 -0800 Subject: Created mini XPath implementation, to simplify mapping logic --- nova/api/openstack/servers.py | 21 ++----- nova/tests/test_minixpath.py | 141 ++++++++++++++++++++++++++++++++++++++++++ nova/utils.py | 45 ++++++++++++++ 3 files changed, 191 insertions(+), 16 deletions(-) create mode 100644 nova/tests/test_minixpath.py diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index b54e28c0c..794705306 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -30,7 +30,7 @@ from nova.auth import manager as auth_manager from nova.compute import instance_types from nova.compute import power_state import nova.api.openstack - +import types LOG = logging.getLogger('server') LOG.setLevel(logging.DEBUG) @@ -63,22 +63,11 @@ def _translate_detail_keys(inst): inst_dict['status'] = power_mapping[inst_dict['status']] inst_dict['addresses'] = dict(public=[], private=[]) - fixed_ip = inst['fixed_ip'] - if fixed_ip: - # grab single private fixed ip - try: - private_ip = fixed_ip['address'] - if private_ip: - inst_dict['addresses']['private'].append(private_ip) - except KeyError: - LOG.debug(_("Failed to read private ip")) + private_ips = utils.minixpath_select(inst, 'fixed_ip/address') + inst_dict['addresses']['private'] = private_ips - # grab all public floating ips - try: - for floating in fixed_ip['floating_ips']: - inst_dict['addresses']['public'].append(floating['address']) - except KeyError: - LOG.debug(_("Failed to read public ip(s)")) + public_ips = utils.minixpath_select(inst, 'fixed_ip/floating_ips/address') + inst_dict['addresses']['public'] = public_ips inst_dict['metadata'] = {} inst_dict['hostId'] = '' diff --git a/nova/tests/test_minixpath.py b/nova/tests/test_minixpath.py new file mode 100644 index 000000000..7fddcf9e9 --- /dev/null +++ b/nova/tests/test_minixpath.py @@ -0,0 +1,141 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Justin Santa Barbara +# +# 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 nova import test +from nova import utils +from nova import exception + + +class MiniXPathTestCase(test.TestCase): + def test_tolerates_nones(self): + xp = utils.minixpath_select + + input = [] + self.assertEquals([], xp(input, "a")) + self.assertEquals([], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [None] + self.assertEquals([], xp(input, "a")) + self.assertEquals([], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [{'a': None}] + self.assertEquals([], xp(input, "a")) + self.assertEquals([], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [{'a': {'b': None}}] + self.assertEquals([{'b': None}], xp(input, "a")) + self.assertEquals([], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [{'a': {'b': {'c': None}}}] + self.assertEquals([{'b': {'c': None}}], xp(input, "a")) + self.assertEquals([{'c': None}], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [{'a': {'b': {'c': None}}}, {'a': None}] + self.assertEquals([{'b': {'c': None}}], xp(input, "a")) + self.assertEquals([{'c': None}], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [{'a': {'b': {'c': None}}}, {'a': {'b': None}}] + self.assertEquals([{'b': {'c': None}}, {'b': None}], xp(input, "a")) + self.assertEquals([{'c': None}], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + def test_does_select(self): + xp = utils.minixpath_select + + input = [{'a': 'a_1'}] + self.assertEquals(['a_1'], xp(input, "a")) + self.assertEquals([], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [{'a': {'b': 'b_1'}}] + self.assertEquals([{'b': 'b_1'}], xp(input, "a")) + self.assertEquals(['b_1'], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [{'a': {'b': {'c': 'c_1'}}}] + self.assertEquals([{'b': {'c': 'c_1'}}], xp(input, "a")) + self.assertEquals([{'c': 'c_1'}], xp(input, "a/b")) + self.assertEquals(['c_1'], xp(input, "a/b/c")) + + input = [{'a': {'b': {'c': 'c_1'}}}, {'a': None}] + self.assertEquals([{'b': {'c': 'c_1'}}], + xp(input, "a")) + self.assertEquals([{'c': 'c_1'}], xp(input, "a/b")) + self.assertEquals(['c_1'], xp(input, "a/b/c")) + + input = [{'a': {'b': {'c': 'c_1'}}}, + {'a': {'b': None}}] + self.assertEquals([{'b': {'c': 'c_1'}}, {'b': None}], + xp(input, "a")) + self.assertEquals([{'c': 'c_1'}], xp(input, "a/b")) + self.assertEquals(['c_1'], xp(input, "a/b/c")) + + input = [{'a': {'b': {'c': 'c_1'}}}, + {'a': {'b': {'c': 'c_2'}}}] + self.assertEquals([{'b': {'c': 'c_1'}}, {'b': {'c': 'c_2'}}], + xp(input, "a")) + self.assertEquals([{'c': 'c_1'}, {'c': 'c_2'}], + xp(input, "a/b")) + self.assertEquals(['c_1', 'c_2'], xp(input, "a/b/c")) + + self.assertEquals([], xp(input, "a/b/c/d")) + self.assertEquals([], xp(input, "c/a/b/d")) + self.assertEquals([], xp(input, "i/r/t")) + + def test_flattens_lists(self): + xp = utils.minixpath_select + + input = [{'a': [1, 2, 3]}] + self.assertEquals([1, 2, 3], xp(input, "a")) + self.assertEquals([], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [{'a': {'b': [1, 2, 3]}}] + self.assertEquals([{'b': [1, 2, 3]}], xp(input, "a")) + self.assertEquals([1, 2, 3], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [{'a': {'b': [1, 2, 3]}}, {'a': {'b': [4, 5, 6]}}] + self.assertEquals([1, 2, 3, 4, 5, 6], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [{'a': [{'b': [1, 2, 3]}, {'b': [4, 5, 6]}]}] + self.assertEquals([1, 2, 3, 4, 5, 6], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = [{'a': [1, 2, {'b': 'b_1'}]}] + self.assertEquals([1, 2, {'b': 'b_1'}], xp(input, "a")) + self.assertEquals(['b_1'], xp(input, "a/b")) + + def test_bad_xpath(self): + xp = utils.minixpath_select + + self.assertRaises(exception.Error, xp, [], None) + self.assertRaises(exception.Error, xp, [], "") + self.assertRaises(exception.Error, xp, [], "/") + self.assertRaises(exception.Error, xp, [], "/a") + self.assertRaises(exception.Error, xp, [], "/a/") + self.assertRaises(exception.Error, xp, [], "//") + self.assertRaises(exception.Error, xp, [], "//a") + self.assertRaises(exception.Error, xp, [], "a//a") + self.assertRaises(exception.Error, xp, [], "a//a/") + self.assertRaises(exception.Error, xp, [], "a/a/") diff --git a/nova/utils.py b/nova/utils.py index 42efa0008..2f926bd82 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -2,6 +2,7 @@ # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Justin Santa Barbara # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -34,6 +35,7 @@ import time from xml.sax import saxutils import re import netaddr +import types from eventlet import event from eventlet import greenthread @@ -499,3 +501,46 @@ def ensure_b64_encoding(val): return val except TypeError: return base64.b64encode(val) + + +def minixpath_select(items, minixpath): + """ Takes an xpath-like expression e.g. prop1/prop2/prop3, and for each + item in items, looks up items[prop1][prop2][prop3]. Like XPath, if any of + the intermediate results are lists it will treat each list item + individually. A 'None' in items or any child expressions will be ignored, + this function will not throw because of None (anywhere) in items""" + + if minixpath is None: + raise exception.Error("Invalid mini_xpath") + + (first_token, sep, remainder) = minixpath.partition("/") + + if first_token == "": + raise exception.Error("Invalid mini_xpath") + + results = [] + + if items is None: + return results + + for item in items: + if item is None: + continue + get_method = getattr(item, "get", None) + if get_method is None: + continue + child = get_method(first_token) + if child is None: + continue + if isinstance(child, types.ListType): + # Flatten intermediate lists + for x in child: + results.append(x) + else: + results.append(child) + + if not sep: + # No more tokens + return results + else: + return minixpath_select(results, remainder) -- cgit From b3b005f50de54b5ef6c62e387dcec5a123f93cf6 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 12:36:09 -0800 Subject: Cope when we pass a non-list to xpath_select - wrap it in a list --- nova/tests/test_minixpath.py | 38 ++++++++++++++++++++++++++++++++++++++ nova/utils.py | 8 +++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_minixpath.py b/nova/tests/test_minixpath.py index 7fddcf9e9..3b1bdf40b 100644 --- a/nova/tests/test_minixpath.py +++ b/nova/tests/test_minixpath.py @@ -139,3 +139,41 @@ class MiniXPathTestCase(test.TestCase): self.assertRaises(exception.Error, xp, [], "a//a") self.assertRaises(exception.Error, xp, [], "a//a/") self.assertRaises(exception.Error, xp, [], "a/a/") + + def test_real_failure1(self): + # Real world failure case... + # We weren't coping when the input was a Dictionary instead of a List + # This led to test_accepts_dictionaries + xp = utils.minixpath_select + + inst = {'fixed_ip': {'floating_ips': [{'address': '1.2.3.4'}], + 'address': '192.168.0.3'}, + 'hostname': ''} + + private_ips = xp(inst, 'fixed_ip/address') + public_ips = xp(inst, 'fixed_ip/floating_ips/address') + self.assertEquals(['192.168.0.3'], private_ips) + self.assertEquals(['1.2.3.4'], public_ips) + + def test_accepts_dictionaries(self): + xp = utils.minixpath_select + + input = {'a': [1, 2, 3]} + self.assertEquals([1, 2, 3], xp(input, "a")) + self.assertEquals([], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = {'a': {'b': [1, 2, 3]}} + self.assertEquals([{'b': [1, 2, 3]}], xp(input, "a")) + self.assertEquals([1, 2, 3], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = {'a': [{'b': [1, 2, 3]}, {'b': [4, 5, 6]}]} + self.assertEquals([1, 2, 3, 4, 5, 6], xp(input, "a/b")) + self.assertEquals([], xp(input, "a/b/c")) + + input = {'a': [1, 2, {'b': 'b_1'}]} + self.assertEquals([1, 2, {'b': 'b_1'}], xp(input, "a")) + self.assertEquals(['b_1'], xp(input, "a/b")) + + diff --git a/nova/utils.py b/nova/utils.py index 2f926bd82..c2cbeb2a7 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -508,7 +508,8 @@ def minixpath_select(items, minixpath): item in items, looks up items[prop1][prop2][prop3]. Like XPath, if any of the intermediate results are lists it will treat each list item individually. A 'None' in items or any child expressions will be ignored, - this function will not throw because of None (anywhere) in items""" + this function will not throw because of None (anywhere) in items. The + returned list will contain no None values.""" if minixpath is None: raise exception.Error("Invalid mini_xpath") @@ -523,6 +524,10 @@ def minixpath_select(items, minixpath): if items is None: return results + if not isinstance(items, types.ListType): + # Wrap single objects in a list + items = [items] + for item in items: if item is None: continue @@ -532,6 +537,7 @@ def minixpath_select(items, minixpath): child = get_method(first_token) if child is None: continue + #print "%s => %s" % (first_token, child) if isinstance(child, types.ListType): # Flatten intermediate lists for x in child: -- cgit From 21ebea24b4b77f8bd1fd42152454f1b0189843d4 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 12:54:46 -0800 Subject: fix describe_availability_zones --- nova/api/ec2/cloud.py | 5 +++-- nova/db/api.py | 4 ++-- nova/db/sqlalchemy/api.py | 9 +++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 99b6d5cb6..9e8764836 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -198,8 +198,9 @@ class CloudController(object): return self._describe_availability_zones(context, **kwargs) def _describe_availability_zones(self, context, **kwargs): - enabled_services = db.service_get_all(context) - disabled_services = db.service_get_all(context, True) + ctxt = context.elevated() + enabled_services = db.service_get_all(ctxt) + disabled_services = db.service_get_all(ctxt, True) available_zones = [] for zone in [service.availability_zone for service in enabled_services]: diff --git a/nova/db/api.py b/nova/db/api.py index d7f3746d2..0a010e727 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -85,8 +85,8 @@ def service_get(context, service_id): def service_get_all(context, disabled=False): - """Get all service.""" - return IMPL.service_get_all(context, None, disabled) + """Get all services.""" + return IMPL.service_get_all(context, disabled) def service_get_all_by_topic(context, topic): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index be29fe2a0..d8751bef4 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -136,15 +136,12 @@ def service_get(context, service_id, session=None): @require_admin_context -def service_get_all(context, session=None, disabled=False): - if not session: - session = get_session() - - result = session.query(models.Service).\ +def service_get_all(context, disabled=False): + session = get_session() + return session.query(models.Service).\ filter_by(deleted=can_read_deleted(context)).\ filter_by(disabled=disabled).\ all() - return result @require_admin_context -- cgit From 89ade95d2eaabf77f9c81a8d50c7cc11aa175464 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 12:55:07 -0800 Subject: Fix pep8 violation (trailing whitespace) --- nova/tests/test_minixpath.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nova/tests/test_minixpath.py b/nova/tests/test_minixpath.py index 3b1bdf40b..cc4a35ef3 100644 --- a/nova/tests/test_minixpath.py +++ b/nova/tests/test_minixpath.py @@ -175,5 +175,3 @@ class MiniXPathTestCase(test.TestCase): input = {'a': [1, 2, {'b': 'b_1'}]} self.assertEquals([1, 2, {'b': 'b_1'}], xp(input, "a")) self.assertEquals(['b_1'], xp(input, "a/b")) - - -- cgit From 1183c9e11b12984b1f5007ace831864e80483712 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 14:07:08 -0800 Subject: Rename minixpath_select to get_from_path --- nova/api/openstack/servers.py | 5 +- nova/tests/test_minixpath.py | 177 ------------------------------------------ nova/tests/test_utils.py | 174 +++++++++++++++++++++++++++++++++++++++++ nova/utils.py | 24 +++--- 4 files changed, 188 insertions(+), 192 deletions(-) delete mode 100644 nova/tests/test_minixpath.py create mode 100644 nova/tests/test_utils.py diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 794705306..ce4a6256a 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -30,7 +30,6 @@ from nova.auth import manager as auth_manager from nova.compute import instance_types from nova.compute import power_state import nova.api.openstack -import types LOG = logging.getLogger('server') LOG.setLevel(logging.DEBUG) @@ -63,10 +62,10 @@ def _translate_detail_keys(inst): inst_dict['status'] = power_mapping[inst_dict['status']] inst_dict['addresses'] = dict(public=[], private=[]) - private_ips = utils.minixpath_select(inst, 'fixed_ip/address') + private_ips = utils.get_from_path(inst, 'fixed_ip/address') inst_dict['addresses']['private'] = private_ips - public_ips = utils.minixpath_select(inst, 'fixed_ip/floating_ips/address') + public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address') inst_dict['addresses']['public'] = public_ips inst_dict['metadata'] = {} diff --git a/nova/tests/test_minixpath.py b/nova/tests/test_minixpath.py deleted file mode 100644 index cc4a35ef3..000000000 --- a/nova/tests/test_minixpath.py +++ /dev/null @@ -1,177 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Justin Santa Barbara -# -# 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 nova import test -from nova import utils -from nova import exception - - -class MiniXPathTestCase(test.TestCase): - def test_tolerates_nones(self): - xp = utils.minixpath_select - - input = [] - self.assertEquals([], xp(input, "a")) - self.assertEquals([], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [None] - self.assertEquals([], xp(input, "a")) - self.assertEquals([], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [{'a': None}] - self.assertEquals([], xp(input, "a")) - self.assertEquals([], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [{'a': {'b': None}}] - self.assertEquals([{'b': None}], xp(input, "a")) - self.assertEquals([], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [{'a': {'b': {'c': None}}}] - self.assertEquals([{'b': {'c': None}}], xp(input, "a")) - self.assertEquals([{'c': None}], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [{'a': {'b': {'c': None}}}, {'a': None}] - self.assertEquals([{'b': {'c': None}}], xp(input, "a")) - self.assertEquals([{'c': None}], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [{'a': {'b': {'c': None}}}, {'a': {'b': None}}] - self.assertEquals([{'b': {'c': None}}, {'b': None}], xp(input, "a")) - self.assertEquals([{'c': None}], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - def test_does_select(self): - xp = utils.minixpath_select - - input = [{'a': 'a_1'}] - self.assertEquals(['a_1'], xp(input, "a")) - self.assertEquals([], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [{'a': {'b': 'b_1'}}] - self.assertEquals([{'b': 'b_1'}], xp(input, "a")) - self.assertEquals(['b_1'], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [{'a': {'b': {'c': 'c_1'}}}] - self.assertEquals([{'b': {'c': 'c_1'}}], xp(input, "a")) - self.assertEquals([{'c': 'c_1'}], xp(input, "a/b")) - self.assertEquals(['c_1'], xp(input, "a/b/c")) - - input = [{'a': {'b': {'c': 'c_1'}}}, {'a': None}] - self.assertEquals([{'b': {'c': 'c_1'}}], - xp(input, "a")) - self.assertEquals([{'c': 'c_1'}], xp(input, "a/b")) - self.assertEquals(['c_1'], xp(input, "a/b/c")) - - input = [{'a': {'b': {'c': 'c_1'}}}, - {'a': {'b': None}}] - self.assertEquals([{'b': {'c': 'c_1'}}, {'b': None}], - xp(input, "a")) - self.assertEquals([{'c': 'c_1'}], xp(input, "a/b")) - self.assertEquals(['c_1'], xp(input, "a/b/c")) - - input = [{'a': {'b': {'c': 'c_1'}}}, - {'a': {'b': {'c': 'c_2'}}}] - self.assertEquals([{'b': {'c': 'c_1'}}, {'b': {'c': 'c_2'}}], - xp(input, "a")) - self.assertEquals([{'c': 'c_1'}, {'c': 'c_2'}], - xp(input, "a/b")) - self.assertEquals(['c_1', 'c_2'], xp(input, "a/b/c")) - - self.assertEquals([], xp(input, "a/b/c/d")) - self.assertEquals([], xp(input, "c/a/b/d")) - self.assertEquals([], xp(input, "i/r/t")) - - def test_flattens_lists(self): - xp = utils.minixpath_select - - input = [{'a': [1, 2, 3]}] - self.assertEquals([1, 2, 3], xp(input, "a")) - self.assertEquals([], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [{'a': {'b': [1, 2, 3]}}] - self.assertEquals([{'b': [1, 2, 3]}], xp(input, "a")) - self.assertEquals([1, 2, 3], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [{'a': {'b': [1, 2, 3]}}, {'a': {'b': [4, 5, 6]}}] - self.assertEquals([1, 2, 3, 4, 5, 6], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [{'a': [{'b': [1, 2, 3]}, {'b': [4, 5, 6]}]}] - self.assertEquals([1, 2, 3, 4, 5, 6], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = [{'a': [1, 2, {'b': 'b_1'}]}] - self.assertEquals([1, 2, {'b': 'b_1'}], xp(input, "a")) - self.assertEquals(['b_1'], xp(input, "a/b")) - - def test_bad_xpath(self): - xp = utils.minixpath_select - - self.assertRaises(exception.Error, xp, [], None) - self.assertRaises(exception.Error, xp, [], "") - self.assertRaises(exception.Error, xp, [], "/") - self.assertRaises(exception.Error, xp, [], "/a") - self.assertRaises(exception.Error, xp, [], "/a/") - self.assertRaises(exception.Error, xp, [], "//") - self.assertRaises(exception.Error, xp, [], "//a") - self.assertRaises(exception.Error, xp, [], "a//a") - self.assertRaises(exception.Error, xp, [], "a//a/") - self.assertRaises(exception.Error, xp, [], "a/a/") - - def test_real_failure1(self): - # Real world failure case... - # We weren't coping when the input was a Dictionary instead of a List - # This led to test_accepts_dictionaries - xp = utils.minixpath_select - - inst = {'fixed_ip': {'floating_ips': [{'address': '1.2.3.4'}], - 'address': '192.168.0.3'}, - 'hostname': ''} - - private_ips = xp(inst, 'fixed_ip/address') - public_ips = xp(inst, 'fixed_ip/floating_ips/address') - self.assertEquals(['192.168.0.3'], private_ips) - self.assertEquals(['1.2.3.4'], public_ips) - - def test_accepts_dictionaries(self): - xp = utils.minixpath_select - - input = {'a': [1, 2, 3]} - self.assertEquals([1, 2, 3], xp(input, "a")) - self.assertEquals([], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = {'a': {'b': [1, 2, 3]}} - self.assertEquals([{'b': [1, 2, 3]}], xp(input, "a")) - self.assertEquals([1, 2, 3], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = {'a': [{'b': [1, 2, 3]}, {'b': [4, 5, 6]}]} - self.assertEquals([1, 2, 3, 4, 5, 6], xp(input, "a/b")) - self.assertEquals([], xp(input, "a/b/c")) - - input = {'a': [1, 2, {'b': 'b_1'}]} - self.assertEquals([1, 2, {'b': 'b_1'}], xp(input, "a")) - self.assertEquals(['b_1'], xp(input, "a/b")) diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py new file mode 100644 index 000000000..34a407f1a --- /dev/null +++ b/nova/tests/test_utils.py @@ -0,0 +1,174 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Justin Santa Barbara +# +# 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 nova import test +from nova import utils +from nova import exception + + +class GetFromPathTestCase(test.TestCase): + def test_tolerates_nones(self): + f = utils.get_from_path + + input = [] + self.assertEquals([], f(input, "a")) + self.assertEquals([], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [None] + self.assertEquals([], f(input, "a")) + self.assertEquals([], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [{'a': None}] + self.assertEquals([], f(input, "a")) + self.assertEquals([], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [{'a': {'b': None}}] + self.assertEquals([{'b': None}], f(input, "a")) + self.assertEquals([], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [{'a': {'b': {'c': None}}}] + self.assertEquals([{'b': {'c': None}}], f(input, "a")) + self.assertEquals([{'c': None}], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [{'a': {'b': {'c': None}}}, {'a': None}] + self.assertEquals([{'b': {'c': None}}], f(input, "a")) + self.assertEquals([{'c': None}], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [{'a': {'b': {'c': None}}}, {'a': {'b': None}}] + self.assertEquals([{'b': {'c': None}}, {'b': None}], f(input, "a")) + self.assertEquals([{'c': None}], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + def test_does_select(self): + f = utils.get_from_path + + input = [{'a': 'a_1'}] + self.assertEquals(['a_1'], f(input, "a")) + self.assertEquals([], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [{'a': {'b': 'b_1'}}] + self.assertEquals([{'b': 'b_1'}], f(input, "a")) + self.assertEquals(['b_1'], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [{'a': {'b': {'c': 'c_1'}}}] + self.assertEquals([{'b': {'c': 'c_1'}}], f(input, "a")) + self.assertEquals([{'c': 'c_1'}], f(input, "a/b")) + self.assertEquals(['c_1'], f(input, "a/b/c")) + + input = [{'a': {'b': {'c': 'c_1'}}}, {'a': None}] + self.assertEquals([{'b': {'c': 'c_1'}}], f(input, "a")) + self.assertEquals([{'c': 'c_1'}], f(input, "a/b")) + self.assertEquals(['c_1'], f(input, "a/b/c")) + + input = [{'a': {'b': {'c': 'c_1'}}}, + {'a': {'b': None}}] + self.assertEquals([{'b': {'c': 'c_1'}}, {'b': None}], f(input, "a")) + self.assertEquals([{'c': 'c_1'}], f(input, "a/b")) + self.assertEquals(['c_1'], f(input, "a/b/c")) + + input = [{'a': {'b': {'c': 'c_1'}}}, + {'a': {'b': {'c': 'c_2'}}}] + self.assertEquals([{'b': {'c': 'c_1'}}, {'b': {'c': 'c_2'}}], + f(input, "a")) + self.assertEquals([{'c': 'c_1'}, {'c': 'c_2'}], f(input, "a/b")) + self.assertEquals(['c_1', 'c_2'], f(input, "a/b/c")) + + self.assertEquals([], f(input, "a/b/c/d")) + self.assertEquals([], f(input, "c/a/b/d")) + self.assertEquals([], f(input, "i/r/t")) + + def test_flattens_lists(self): + f = utils.get_from_path + + input = [{'a': [1, 2, 3]}] + self.assertEquals([1, 2, 3], f(input, "a")) + self.assertEquals([], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [{'a': {'b': [1, 2, 3]}}] + self.assertEquals([{'b': [1, 2, 3]}], f(input, "a")) + self.assertEquals([1, 2, 3], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [{'a': {'b': [1, 2, 3]}}, {'a': {'b': [4, 5, 6]}}] + self.assertEquals([1, 2, 3, 4, 5, 6], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [{'a': [{'b': [1, 2, 3]}, {'b': [4, 5, 6]}]}] + self.assertEquals([1, 2, 3, 4, 5, 6], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = [{'a': [1, 2, {'b': 'b_1'}]}] + self.assertEquals([1, 2, {'b': 'b_1'}], f(input, "a")) + self.assertEquals(['b_1'], f(input, "a/b")) + + def test_bad_xpath(self): + f = utils.get_from_path + + self.assertRaises(exception.Error, f, [], None) + self.assertRaises(exception.Error, f, [], "") + self.assertRaises(exception.Error, f, [], "/") + self.assertRaises(exception.Error, f, [], "/a") + self.assertRaises(exception.Error, f, [], "/a/") + self.assertRaises(exception.Error, f, [], "//") + self.assertRaises(exception.Error, f, [], "//a") + self.assertRaises(exception.Error, f, [], "a//a") + self.assertRaises(exception.Error, f, [], "a//a/") + self.assertRaises(exception.Error, f, [], "a/a/") + + def test_real_failure1(self): + # Real world failure case... + # We weren't coping when the input was a Dictionary instead of a List + # This led to test_accepts_dictionaries + f = utils.get_from_path + + inst = {'fixed_ip': {'floating_ips': [{'address': '1.2.3.4'}], + 'address': '192.168.0.3'}, + 'hostname': ''} + + private_ips = f(inst, 'fixed_ip/address') + public_ips = f(inst, 'fixed_ip/floating_ips/address') + self.assertEquals(['192.168.0.3'], private_ips) + self.assertEquals(['1.2.3.4'], public_ips) + + def test_accepts_dictionaries(self): + f = utils.get_from_path + + input = {'a': [1, 2, 3]} + self.assertEquals([1, 2, 3], f(input, "a")) + self.assertEquals([], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = {'a': {'b': [1, 2, 3]}} + self.assertEquals([{'b': [1, 2, 3]}], f(input, "a")) + self.assertEquals([1, 2, 3], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = {'a': [{'b': [1, 2, 3]}, {'b': [4, 5, 6]}]} + self.assertEquals([1, 2, 3, 4, 5, 6], f(input, "a/b")) + self.assertEquals([], f(input, "a/b/c")) + + input = {'a': [1, 2, {'b': 'b_1'}]} + self.assertEquals([1, 2, {'b': 'b_1'}], f(input, "a")) + self.assertEquals(['b_1'], f(input, "a/b")) diff --git a/nova/utils.py b/nova/utils.py index c2cbeb2a7..65e28c648 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -32,10 +32,10 @@ import string import struct import sys import time +import types from xml.sax import saxutils import re import netaddr -import types from eventlet import event from eventlet import greenthread @@ -503,18 +503,19 @@ def ensure_b64_encoding(val): return base64.b64encode(val) -def minixpath_select(items, minixpath): - """ Takes an xpath-like expression e.g. prop1/prop2/prop3, and for each - item in items, looks up items[prop1][prop2][prop3]. Like XPath, if any of - the intermediate results are lists it will treat each list item - individually. A 'None' in items or any child expressions will be ignored, - this function will not throw because of None (anywhere) in items. The - returned list will contain no None values.""" +def get_from_path(items, path): + """ Returns a list of items matching the specified path. Takes an + XPath-like expression e.g. prop1/prop2/prop3, and for each item in items, + looks up items[prop1][prop2][prop3]. Like XPath, if any of the + intermediate results are lists it will treat each list item individually. + A 'None' in items or any child expressions will be ignored, this function + will not throw because of None (anywhere) in items. The returned list + will contain no None values.""" - if minixpath is None: + if path is None: raise exception.Error("Invalid mini_xpath") - (first_token, sep, remainder) = minixpath.partition("/") + (first_token, sep, remainder) = path.partition("/") if first_token == "": raise exception.Error("Invalid mini_xpath") @@ -537,7 +538,6 @@ def minixpath_select(items, minixpath): child = get_method(first_token) if child is None: continue - #print "%s => %s" % (first_token, child) if isinstance(child, types.ListType): # Flatten intermediate lists for x in child: @@ -549,4 +549,4 @@ def minixpath_select(items, minixpath): # No more tokens return results else: - return minixpath_select(results, remainder) + return get_from_path(results, remainder) -- cgit From a508e2dce781b98db5a719df75a451d9a2727fca Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 14:12:43 -0800 Subject: Make sure there are two blank links after the import --- nova/api/openstack/servers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index ce4a6256a..6c227d71a 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -31,6 +31,7 @@ from nova.compute import instance_types from nova.compute import power_state import nova.api.openstack + LOG = logging.getLogger('server') LOG.setLevel(logging.DEBUG) -- cgit From 8c007b56b586257d048b6db4ecfbed8f502381fd Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 14:16:31 -0800 Subject: Put back the comments I accidentally removed --- nova/api/openstack/servers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 6c227d71a..97323f66f 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -63,9 +63,11 @@ def _translate_detail_keys(inst): inst_dict['status'] = power_mapping[inst_dict['status']] inst_dict['addresses'] = dict(public=[], private=[]) + # grab single private fixed ip private_ips = utils.get_from_path(inst, 'fixed_ip/address') inst_dict['addresses']['private'] = private_ips + # grab all public floating ips public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address') inst_dict['addresses']['public'] = public_ips -- cgit From 24090232272e0db163060e0ca32dbf97c05120c9 Mon Sep 17 00:00:00 2001 From: termie <github@anarkystic.com> Date: Wed, 23 Feb 2011 15:14:16 -0800 Subject: updates to nova.flags to get help working better Fixes some old bugs that were brought up on the mailing list. First step towards moving flags into the places where they belong. Also moves manager import into service's init so that we can get all the dynamically loaded flags shortly after loading. --- nova/flags.py | 49 +++++++++++++++++++++++++++++++++++++++++----- nova/service.py | 31 +++++++++++++++++------------ nova/tests/test_service.py | 7 ------- 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index f64a62da9..24bca0caf 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -160,9 +160,45 @@ class StrWrapper(object): raise KeyError(name) -FLAGS = FlagValues() -gflags.FLAGS = FLAGS -gflags.DEFINE_flag(gflags.HelpFlag(), FLAGS) +# Copied from gflags with small mods to get the naming correct. +# Originally gflags checks for the first module that is not gflags that is +# in the call chain, we want to check for the first module that is not gflags +# and not this module. +def _GetCallingModule(): + """Returns the name of the module that's calling into this module. + + We generally use this function to get the name of the module calling a + DEFINE_foo... function. + """ + # Walk down the stack to find the first globals dict that's not ours. + for depth in range(1, sys.getrecursionlimit()): + if not sys._getframe(depth).f_globals is globals(): + module_name = __GetModuleName(sys._getframe(depth).f_globals) + if module_name == 'gflags': + continue + if module_name is not None: + return module_name + raise AssertionError("No module was found") + + +# Copied from gflags because it is a private function +def __GetModuleName(globals_dict): + """Given a globals dict, returns the name of the module that defines it. + + Args: + globals_dict: A dictionary that should correspond to an environment + providing the values of the globals. + + Returns: + A string (the name of the module) or None (if the module could not + be identified. + """ + for name, module in sys.modules.iteritems(): + if getattr(module, '__dict__', None) is globals_dict: + if name == '__main__': + return sys.argv[0] + return name + return None def _wrapper(func): @@ -173,6 +209,11 @@ def _wrapper(func): return _wrapped +FLAGS = FlagValues() +gflags.FLAGS = FLAGS +gflags._GetCallingModule = _GetCallingModule + + DEFINE = _wrapper(gflags.DEFINE) DEFINE_string = _wrapper(gflags.DEFINE_string) DEFINE_integer = _wrapper(gflags.DEFINE_integer) @@ -185,8 +226,6 @@ DEFINE_spaceseplist = _wrapper(gflags.DEFINE_spaceseplist) DEFINE_multistring = _wrapper(gflags.DEFINE_multistring) DEFINE_multi_int = _wrapper(gflags.DEFINE_multi_int) DEFINE_flag = _wrapper(gflags.DEFINE_flag) - - HelpFlag = gflags.HelpFlag HelpshortFlag = gflags.HelpshortFlag HelpXMLFlag = gflags.HelpXMLFlag diff --git a/nova/service.py b/nova/service.py index cc88ac233..f47358089 100644 --- a/nova/service.py +++ b/nova/service.py @@ -45,15 +45,10 @@ FLAGS = flags.FLAGS flags.DEFINE_integer('report_interval', 10, 'seconds between nodes reporting state to datastore', lower_bound=1) - flags.DEFINE_integer('periodic_interval', 60, 'seconds between running periodic tasks', lower_bound=1) -flags.DEFINE_flag(flags.HelpFlag()) -flags.DEFINE_flag(flags.HelpshortFlag()) -flags.DEFINE_flag(flags.HelpXMLFlag()) - class Service(object): """Base class for workers that run on hosts.""" @@ -64,6 +59,8 @@ class Service(object): self.binary = binary self.topic = topic self.manager_class_name = manager + manager_class = utils.import_class(self.manager_class_name) + self.manager = manager_class(host=self.host, *args, **kwargs) self.report_interval = report_interval self.periodic_interval = periodic_interval super(Service, self).__init__(*args, **kwargs) @@ -71,9 +68,9 @@ class Service(object): self.timers = [] def start(self): - manager_class = utils.import_class(self.manager_class_name) - self.manager = manager_class(host=self.host, *self.saved_args, - **self.saved_kwargs) + vcs_string = version.version_string_with_vcs() + logging.audit(_("Starting %(topic)s node (version %(vcs_string)s)"), + {'topic': self.topic, 'vcs_string': vcs_string}) self.manager.init_host() self.model_disconnected = False ctxt = context.get_admin_context() @@ -153,9 +150,6 @@ class Service(object): report_interval = FLAGS.report_interval if not periodic_interval: periodic_interval = FLAGS.periodic_interval - vcs_string = version.version_string_with_vcs() - logging.audit(_("Starting %(topic)s node (version %(vcs_string)s)") - % locals()) service_obj = cls(host, binary, topic, manager, report_interval, periodic_interval) @@ -217,8 +211,19 @@ class Service(object): def serve(*services): - if not services: - services = [Service.create()] + try: + if not services: + services = [Service.create()] + except Exception: + logging.exception('in Service.create()') + raise + finally: + # After we've loaded up all our dynamic bits, check + # whether we should print help + flags.DEFINE_flag(flags.HelpFlag()) + flags.DEFINE_flag(flags.HelpshortFlag()) + flags.DEFINE_flag(flags.HelpXMLFlag()) + FLAGS.ParseNewFlags() name = '_'.join(x.binary for x in services) logging.debug(_("Serving %s"), name) diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py index a67c8d1e8..45d9afa6c 100644 --- a/nova/tests/test_service.py +++ b/nova/tests/test_service.py @@ -50,13 +50,6 @@ class ExtendedService(service.Service): class ServiceManagerTestCase(test.TestCase): """Test cases for Services""" - def test_attribute_error_for_no_manager(self): - serv = service.Service('test', - 'test', - 'test', - 'nova.tests.test_service.FakeManager') - self.assertRaises(AttributeError, getattr, serv, 'test_method') - def test_message_gets_to_manager(self): serv = service.Service('test', 'test', -- cgit From b7d89758af54b291492eecae74cee29461ca28b9 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 15:16:56 -0800 Subject: remove processName from debug output since we aren't using multiprocessing and it doesn't exist in python 2.6.1 --- nova/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/log.py b/nova/log.py index 10c14d74b..7866c34f7 100644 --- a/nova/log.py +++ b/nova/log.py @@ -54,7 +54,7 @@ flags.DEFINE_string('logging_default_format_string', 'format string to use for log messages without context') flags.DEFINE_string('logging_debug_format_suffix', - 'from %(processName)s (pid=%(process)d) %(funcName)s' + 'from (pid=%(process)d) %(funcName)s' ' %(pathname)s:%(lineno)d', 'data to append to log format when level is DEBUG') -- cgit From fbfc2b21657a2878ab97138c133a253f7c88303e Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 15:17:32 -0800 Subject: Alphabetize imports --- bin/nova-api | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-api b/bin/nova-api index 96c784624..933202dc8 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -34,9 +34,9 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): gettext.install('nova', unicode=1) +from nova import apiservice from nova import flags from nova import log as logging -from nova import apiservice from nova import wsgi FLAGS = flags.FLAGS -- cgit From 3115a65f9981371ea8587a288b360c3c519de865 Mon Sep 17 00:00:00 2001 From: termie <github@anarkystic.com> Date: Wed, 23 Feb 2011 15:26:52 -0800 Subject: add help back to the scripts that don't use service.py --- bin/nova-ajax-console-proxy | 4 +++- bin/nova-api | 3 +++ bin/nova-direct-api | 4 ++++ bin/nova-manage | 3 +++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/bin/nova-ajax-console-proxy b/bin/nova-ajax-console-proxy index 392b328b1..1e11c6d58 100755 --- a/bin/nova-ajax-console-proxy +++ b/bin/nova-ajax-console-proxy @@ -47,9 +47,11 @@ from nova import utils from nova import wsgi FLAGS = flags.FLAGS - flags.DEFINE_integer('ajax_console_idle_timeout', 300, 'Seconds before idle connection destroyed') +flags.DEFINE_flag(flags.HelpFlag()) +flags.DEFINE_flag(flags.HelpshortFlag()) +flags.DEFINE_flag(flags.HelpXMLFlag()) LOG = logging.getLogger('nova.ajax_console_proxy') LOG.setLevel(logging.DEBUG) diff --git a/bin/nova-api b/bin/nova-api index d5efb4687..cf140570a 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -48,6 +48,9 @@ flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen') flags.DEFINE_string('osapi_listen', "0.0.0.0", 'IP address for OpenStack API to listen') flags.DEFINE_integer('osapi_listen_port', 8774, 'port for os api to listen') +flags.DEFINE_flag(flags.HelpFlag()) +flags.DEFINE_flag(flags.HelpshortFlag()) +flags.DEFINE_flag(flags.HelpXMLFlag()) API_ENDPOINTS = ['ec2', 'osapi'] diff --git a/bin/nova-direct-api b/bin/nova-direct-api index 6c63bd26b..bf29d9a5e 100755 --- a/bin/nova-direct-api +++ b/bin/nova-direct-api @@ -45,6 +45,10 @@ from nova.compute import api as compute_api FLAGS = flags.FLAGS flags.DEFINE_integer('direct_port', 8001, 'Direct API port') flags.DEFINE_string('direct_host', '0.0.0.0', 'Direct API host') +flags.DEFINE_flag(flags.HelpFlag()) +flags.DEFINE_flag(flags.HelpshortFlag()) +flags.DEFINE_flag(flags.HelpXMLFlag()) + if __name__ == '__main__': utils.default_flagfile() diff --git a/bin/nova-manage b/bin/nova-manage index 5189de0e1..b603c8b07 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -93,6 +93,9 @@ flags.DECLARE('network_size', 'nova.network.manager') flags.DECLARE('vlan_start', 'nova.network.manager') flags.DECLARE('vpn_start', 'nova.network.manager') flags.DECLARE('fixed_range_v6', 'nova.network.manager') +flags.DEFINE_flag(flags.HelpFlag()) +flags.DEFINE_flag(flags.HelpshortFlag()) +flags.DEFINE_flag(flags.HelpXMLFlag()) def param2id(object_id): -- cgit From 5e2f82b1487b8f8e43539d0c71466fbbfed23121 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 15:29:02 -0800 Subject: revert logfile redirection and make colors work by temporarily switching stdout --- nova/tests/fake_flags.py | 1 - run_tests.py | 16 +++++++++------- run_tests.sh | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index 575fefff6..2b1919407 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -41,4 +41,3 @@ FLAGS.iscsi_num_targets = 8 FLAGS.verbose = True FLAGS.sql_connection = 'sqlite:///tests.sqlite' FLAGS.use_ipv6 = True -FLAGS.logfile = 'tests.log' diff --git a/run_tests.py b/run_tests.py index 877849ab5..2c04a0641 100644 --- a/run_tests.py +++ b/run_tests.py @@ -97,7 +97,7 @@ class _AnsiColorizer(object): try: return curses.tigetnum("colors") > 2 except curses.error: - curses.setupterm(fd=stream.fileno()) + curses.setupterm() return curses.tigetnum("colors") > 2 except: raise @@ -122,13 +122,13 @@ class _Win32Colorizer(object): See _AnsiColorizer docstring. """ def __init__(self, stream): - from win32console import GetStdHandle, STD_ERROR_HANDLE, \ + from win32console import GetStdHandle, STD_OUT_HANDLE, \ FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \ FOREGROUND_INTENSITY red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN, FOREGROUND_BLUE, FOREGROUND_INTENSITY) self.stream = stream - self.screenBuffer = GetStdHandle(STD_ERROR_HANDLE) + self.screenBuffer = GetStdHandle(STD_OUT_HANDLE) self._colors = { 'normal': red | green | blue, 'red': red | bold, @@ -144,7 +144,7 @@ class _Win32Colorizer(object): try: import win32console screenBuffer = win32console.GetStdHandle( - win32console.STD_ERROR_HANDLE) + win32console.STD_OUT_HANDLE) except ImportError: return False import pywintypes @@ -185,12 +185,14 @@ class NovaTestResult(result.TextTestResult): result.TextTestResult.__init__(self, *args, **kw) self._last_case = None self.colorizer = None + # NOTE(vish): reset stdout for the terminal check + stdout = sys.stdout + sys.stdout = sys.__stdout__ for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]: - # NOTE(vish): nose does funky stuff with stdout, so use stderr - # to setup the colorizer - if colorizer.supported(sys.stderr): + if colorizer.supported(): self.colorizer = colorizer(self.stream) break + sys.stdout = stdout def getDescription(self, test): return str(test) diff --git a/run_tests.sh b/run_tests.sh index 7aab9ecd9..d4586a57e 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -39,12 +39,12 @@ done function run_tests { # Just run the test suites in current environment - ${wrapper} $NOSETESTS + ${wrapper} $NOSETESTS 2> run_tests.log # If we get some short import error right away, print the error log directly RESULT=$? if [ "$RESULT" -ne "0" ]; then - ERRSIZE=`wc -l run_tests.err.log | awk '{print \$1}'` + ERRSIZE=`wc -l run_tests.log | awk '{print \$1}'` if [ "$ERRSIZE" -lt "40" ]; then cat run_tests.err.log -- cgit From 861a7f2b53f02af2ef196411171182394edd7e17 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 15:31:40 -0800 Subject: Changed create from a @staticmethod to a @classmethod --- nova/apiservice.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/apiservice.py b/nova/apiservice.py index 6340e9b9b..03aa781fb 100644 --- a/nova/apiservice.py +++ b/nova/apiservice.py @@ -76,12 +76,12 @@ class ApiService(object): def wait(self): self.wsgi_app.wait() - @staticmethod - def create(): + @classmethod + def create(cls): conf = wsgi.paste_config_file('nova-api.conf') LOG.audit(_("Starting nova-api node (version %s)"), version.version_string_with_vcs()) - service = ApiService(conf) + service = cls(conf) return service -- cgit From c27c19ea316b343f1623a7c1bf21c53cd426603b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 15:35:30 -0800 Subject: fix pep8 --- run_tests.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/run_tests.py b/run_tests.py index 2c04a0641..eef0120bc 100644 --- a/run_tests.py +++ b/run_tests.py @@ -87,7 +87,7 @@ class _AnsiColorizer(object): coloring terminal output using this method. Returns False otherwise. """ if not stream.isatty(): - return False # auto color only on TTYs + return False # auto color only on TTYs try: import curses except ImportError: @@ -180,6 +180,7 @@ class _NullColorizer(object): def write(self, text, color): self.stream.write(text) + class NovaTestResult(result.TextTestResult): def __init__(self, *args, **kw): result.TextTestResult.__init__(self, *args, **kw) @@ -276,7 +277,7 @@ class NovaTestRunner(core.TextTestRunner): if __name__ == '__main__': logging.setup() - testdir = os.path.abspath(os.path.join("nova","tests")) + testdir = os.path.abspath(os.path.join("nova", "tests")) testdb = os.path.join(testdir, "tests.sqlite") if os.path.exists(testdb): os.unlink(testdb) -- cgit From 78eddce564cccf0d9be19b303cbc122966f5fa71 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 15:42:59 -0800 Subject: added comments about where code came from --- run_tests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/run_tests.py b/run_tests.py index eef0120bc..8025548e5 100644 --- a/run_tests.py +++ b/run_tests.py @@ -198,6 +198,7 @@ class NovaTestResult(result.TextTestResult): def getDescription(self, test): return str(test) + # NOTE(vish): copied from unittest with edit to add color def addSuccess(self, test): unittest.TestResult.addSuccess(self, test) if self.showAll: @@ -207,6 +208,7 @@ class NovaTestResult(result.TextTestResult): self.stream.write('.') self.stream.flush() + # NOTE(vish): copied from unittest with edit to add color def addFailure(self, test, err): unittest.TestResult.addFailure(self, test, err) if self.showAll: @@ -216,6 +218,7 @@ class NovaTestResult(result.TextTestResult): self.stream.write('F') self.stream.flush() + # NOTE(vish): copied from nose with edit to add color def addError(self, test, err): """Overrides normal addError to add support for errorClasses. If the exception is a registered class, the -- cgit From 503fe37427247b2728051426d3c40266de69bd71 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 15:59:54 -0800 Subject: Reverted bad-fix to sqlalchemy code --- nova/db/sqlalchemy/api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 53498fbc5..d8751bef4 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1209,9 +1209,7 @@ def project_get_network_v6(context, project_id): def queue_get_for(_context, topic, physical_node_id): # FIXME(ja): this should be servername? - if physical_node_id: - return "%s.%s" % (topic, physical_node_id) - return topic + return "%s.%s" % (topic, physical_node_id) ################### -- cgit From 3aa0183bca5585c706b4b02e0a7542422015c693 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 18:54:13 -0800 Subject: fix missed err.log --- run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_tests.sh b/run_tests.sh index d4586a57e..7ac3ff33f 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -47,7 +47,7 @@ function run_tests { ERRSIZE=`wc -l run_tests.log | awk '{print \$1}'` if [ "$ERRSIZE" -lt "40" ]; then - cat run_tests.err.log + cat run_tests.log fi fi return $RESULT -- cgit From f86c45764d6cf62b1ded928e807e93198ed6ffd1 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 22:02:06 -0800 Subject: PEP 257 fixes --- nova/volume/driver.py | 42 +++++++++++++++++++++++------------------- nova/volume/san.py | 22 +++++++++++++++------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 22c2c2fc3..4263a6a8d 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -227,14 +227,16 @@ class FakeAOEDriver(AOEDriver): class ISCSIDriver(VolumeDriver): - """Executes commands relating to ISCSI volumes. We make use of model - provider properties as follows: - provider_location - if present, contains the iSCSI target information - in the same format as an ietadm discovery - i.e. '<target ip/port>,<target portal> <target IQN>' - provider_auth - if present, contains a space-separated triple: - '<auth method> <auth username> <auth password>'. CHAP is the only - auth_method in use at the moment.""" + """Executes commands relating to ISCSI volumes. + + We make use of model provider properties as follows: + :provider_location: if present, contains the iSCSI target information + in the same format as an ietadm discovery + i.e. '<ip>:<port>,<portal> <target IQN>' + :provider_auth: if present, contains a space-separated triple: + '<auth method> <auth username> <auth password>'. + `CHAP` is the only auth_method in use at the moment. + """ def ensure_export(self, context, volume): """Synchronously recreates an export for a logical volume.""" @@ -321,17 +323,19 @@ class ISCSIDriver(VolumeDriver): return None def _get_iscsi_properties(self, volume): - """Gets iscsi configuration, ideally from saved information in the - volume entity, but falling back to discovery if need be. The - properties are: - target_discovered - boolean indicating whether discovery was used, - target_iqn - the IQN of the iSCSI target, - target_portal - the portal of the iSCSI target, - and auth_method, auth_username and auth_password - - the authentication details. Right now, either - auth_method is not present meaning no authentication, or - auth_method == 'CHAP' meaning use CHAP with the specified - credentials.""" + """Gets iscsi configuration + + We ideally get saved information in the volume entity, but fall back + to discovery if need be. Discovery may be completely removed in future + The properties are: + :target_discovered: boolean indicating whether discovery was used + :target_iqn: the IQN of the iSCSI target, + :target_portal: the portal of the iSCSI target + :auth_method:, :auth_username:, :auth_password: + the authentication details. Right now, either auth_method is not + present meaning no authentication, or auth_method == `CHAP` + meaning use CHAP with the specified credentials. + """ properties = {} diff --git a/nova/volume/san.py b/nova/volume/san.py index 09192bc9f..d6598d16e 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -16,8 +16,9 @@ # under the License. """ Drivers for san-stored volumes. + The unique thing about a SAN is that we don't expect that we can run the volume - controller on the SAN hardware. We expect to access it over SSH or some API. +controller on the SAN hardware. We expect to access it over SSH or some API. """ import os @@ -51,7 +52,11 @@ flags.DEFINE_integer('san_ssh_port', 22, class SanISCSIDriver(ISCSIDriver): """ Base class for SAN-style storage volumes - (storage providers we access over SSH)""" + + A SAN-style storage value is 'different' because the volume controller + probably won't run on it, so we need to access is over SSH or another + remote protocol. + """ def _build_iscsi_target_name(self, volume): return "%s%s" % (FLAGS.iscsi_target_prefix, volume['name']) @@ -137,6 +142,7 @@ def _get_prefixed_values(data, prefix): class SolarisISCSIDriver(SanISCSIDriver): """Executes commands relating to Solaris-hosted ISCSI volumes. + Basic setup for a Solaris iSCSI server: pkg install storage-server SUNWiscsit svcadm enable stmf @@ -330,13 +336,14 @@ class SolarisISCSIDriver(SanISCSIDriver): class HpSanISCSIDriver(SanISCSIDriver): """Executes commands relating to HP/Lefthand SAN ISCSI volumes. + We use the CLIQ interface, over SSH. Rough overview of CLIQ commands used: - CLIQ createVolume (creates the volume) - CLIQ getVolumeInfo (to discover the IQN etc) - CLIQ getClusterInfo (to discover the iSCSI target IP address) - CLIQ assignVolumeChap (exports it with CHAP security) + :createVolume: (creates the volume) + :getVolumeInfo: (to discover the IQN etc) + :getClusterInfo: (to discover the iSCSI target IP address) + :assignVolumeChap: (exports it with CHAP security) The 'trick' here is that the HP SAN enforces security by default, so normally a volume mount would need both to configure the SAN in the volume @@ -344,7 +351,8 @@ class HpSanISCSIDriver(SanISCSIDriver): not catered for at the moment in the nova architecture, so instead we share the volume using CHAP at volume creation time. Then the mount need only use those CHAP credentials, so can take place exclusively in the - compute layer""" + compute layer. + """ def _cliq_run(self, verb, cliq_args): """Runs a CLIQ command over SSH, without doing any result parsing""" -- cgit From 0a649493d21a8766f416ceca2c0122c20945ca1b Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 22:47:57 -0800 Subject: Documentation fixes so that output looks better --- nova/volume/driver.py | 9 ++++++++- nova/volume/san.py | 13 +++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 4263a6a8d..e3744c790 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -230,9 +230,11 @@ class ISCSIDriver(VolumeDriver): """Executes commands relating to ISCSI volumes. We make use of model provider properties as follows: + :provider_location: if present, contains the iSCSI target information in the same format as an ietadm discovery i.e. '<ip>:<port>,<portal> <target IQN>' + :provider_auth: if present, contains a space-separated triple: '<auth method> <auth username> <auth password>'. `CHAP` is the only auth_method in use at the moment. @@ -328,10 +330,15 @@ class ISCSIDriver(VolumeDriver): We ideally get saved information in the volume entity, but fall back to discovery if need be. Discovery may be completely removed in future The properties are: + :target_discovered: boolean indicating whether discovery was used - :target_iqn: the IQN of the iSCSI target, + + :target_iqn: the IQN of the iSCSI target + :target_portal: the portal of the iSCSI target + :auth_method:, :auth_username:, :auth_password: + the authentication details. Right now, either auth_method is not present meaning no authentication, or auth_method == `CHAP` meaning use CHAP with the specified credentials. diff --git a/nova/volume/san.py b/nova/volume/san.py index d6598d16e..9532c8116 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -144,16 +144,25 @@ class SolarisISCSIDriver(SanISCSIDriver): """Executes commands relating to Solaris-hosted ISCSI volumes. Basic setup for a Solaris iSCSI server: + pkg install storage-server SUNWiscsit + svcadm enable stmf + svcadm enable -r svc:/network/iscsi/target:default + pfexec itadm create-tpg e1000g0 ${MYIP} + pfexec itadm create-target -t e1000g0 + Then grant the user that will be logging on lots of permissions. I'm not sure exactly which though: + zfs allow justinsb create,mount,destroy rpool + usermod -P'File System Management' justinsb + usermod -P'Primary Administrator' justinsb Also make sure you can login using san_login & san_password/san_privatekey @@ -340,9 +349,13 @@ class HpSanISCSIDriver(SanISCSIDriver): We use the CLIQ interface, over SSH. Rough overview of CLIQ commands used: + :createVolume: (creates the volume) + :getVolumeInfo: (to discover the IQN etc) + :getClusterInfo: (to discover the iSCSI target IP address) + :assignVolumeChap: (exports it with CHAP security) The 'trick' here is that the HP SAN enforces security by default, so -- cgit From 701e1c15944062f7d229e59f2ede06398226b165 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Wed, 23 Feb 2011 22:53:44 -0800 Subject: Hotfix to not require metadata --- nova/api/openstack/servers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 63e047b39..841bab6d0 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -177,7 +177,7 @@ class Controller(wsgi.Controller): # However, the CloudServers API is not definitive on this front, # and we want to be compatible. metadata = [] - if env['server']['metadata']: + if env['server'].get('metadata'): for k, v in env['server']['metadata'].items(): metadata.append({'key': k, 'value': v}) -- cgit From cc8276655cd2424b71689776f24e2a5cc767e844 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 23 Feb 2011 23:37:52 -0800 Subject: move relevant code to baseclass and make flatdhcp not inherit from flat --- nova/network/manager.py | 141 +++++++++++++++++++++--------------------------- 1 file changed, 62 insertions(+), 79 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 1df193be0..e999adae5 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -163,11 +163,22 @@ class NetworkManager(manager.Manager): def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Gets a fixed ip from the pool.""" - raise NotImplementedError() + # TODO(vish): when this is called by compute, we can associate compute + # with a network, or a cluster of computes with a network + # and use that network here with a method like + # network_get_by_compute_host + network_ref = self.db.network_get_by_bridge(context, + FLAGS.flat_network_bridge) + address = self.db.fixed_ip_associate_pool(context.elevated(), + network_ref['id'], + instance_id) + self.db.fixed_ip_update(context, address, {'allocated': True}) + return address def deallocate_fixed_ip(self, context, address, *args, **kwargs): """Returns a fixed ip to the pool.""" - raise NotImplementedError() + self.db.fixed_ip_update(context, address, {'allocated': False}) + self.db.fixed_ip_disassociate(context.elevated(), address) def setup_fixed_ip(self, context, address): """Sets up rules for fixed ip.""" @@ -257,12 +268,57 @@ class NetworkManager(manager.Manager): def get_network_host(self, context): """Get the network host for the current context.""" - raise NotImplementedError() + network_ref = self.db.network_get_by_bridge(context, + FLAGS.flat_network_bridge) + # NOTE(vish): If the network has no host, use the network_host flag. + # This could eventually be a a db lookup of some sort, but + # a flag is easy to handle for now. + host = network_ref['host'] + if not host: + topic = self.db.queue_get_for(context, + FLAGS.network_topic, + FLAGS.network_host) + if FLAGS.fake_call: + return self.set_network_host(context, network_ref['id']) + host = rpc.call(context, + FLAGS.network_topic, + {"method": "set_network_host", + "args": {"network_id": network_ref['id']}}) + return host def create_networks(self, context, cidr, num_networks, network_size, - cidr_v6, *args, **kwargs): + cidr_v6, label, *args, **kwargs): """Create networks based on parameters.""" - raise NotImplementedError() + fixed_net = IPy.IP(cidr) + fixed_net_v6 = IPy.IP(cidr_v6) + significant_bits_v6 = 64 + count = 1 + for index in range(num_networks): + start = index * network_size + significant_bits = 32 - int(math.log(network_size, 2)) + cidr = "%s/%s" % (fixed_net[start], significant_bits) + project_net = IPy.IP(cidr) + net = {} + net['bridge'] = FLAGS.flat_network_bridge + net['cidr'] = cidr + net['netmask'] = str(project_net.netmask()) + net['gateway'] = str(project_net[1]) + net['broadcast'] = str(project_net.broadcast()) + net['dhcp_start'] = str(project_net[2]) + if num_networks > 1: + net['label'] = "%s_%d" % (label, count) + else: + net['label'] = label + count += 1 + + if(FLAGS.use_ipv6): + cidr_v6 = "%s/%s" % (fixed_net_v6[0], significant_bits_v6) + net['cidr_v6'] = cidr_v6 + + network_ref = self.db.network_create_safe(context, net) + + if network_ref: + self._create_fixed_ips(context, network_ref['id']) @property def _bottom_reserved_ips(self): # pylint: disable-msg=R0201 @@ -332,83 +388,10 @@ class FlatManager(NetworkManager): for network in self.db.host_get_networks(ctxt, self.host): self._on_set_network_host(ctxt, network['id']) - def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): - """Gets a fixed ip from the pool.""" - # TODO(vish): when this is called by compute, we can associate compute - # with a network, or a cluster of computes with a network - # and use that network here with a method like - # network_get_by_compute_host - network_ref = self.db.network_get_by_bridge(context, - FLAGS.flat_network_bridge) - address = self.db.fixed_ip_associate_pool(context.elevated(), - network_ref['id'], - instance_id) - self.db.fixed_ip_update(context, address, {'allocated': True}) - return address - - def deallocate_fixed_ip(self, context, address, *args, **kwargs): - """Returns a fixed ip to the pool.""" - self.db.fixed_ip_update(context, address, {'allocated': False}) - self.db.fixed_ip_disassociate(context.elevated(), address) - def setup_compute_network(self, context, instance_id): """Network is created manually.""" pass - def create_networks(self, context, cidr, num_networks, network_size, - cidr_v6, label, *args, **kwargs): - """Create networks based on parameters.""" - fixed_net = IPy.IP(cidr) - fixed_net_v6 = IPy.IP(cidr_v6) - significant_bits_v6 = 64 - count = 1 - for index in range(num_networks): - start = index * network_size - significant_bits = 32 - int(math.log(network_size, 2)) - cidr = "%s/%s" % (fixed_net[start], significant_bits) - project_net = IPy.IP(cidr) - net = {} - net['bridge'] = FLAGS.flat_network_bridge - net['cidr'] = cidr - net['netmask'] = str(project_net.netmask()) - net['gateway'] = str(project_net[1]) - net['broadcast'] = str(project_net.broadcast()) - net['dhcp_start'] = str(project_net[2]) - if num_networks > 1: - net['label'] = "%s_%d" % (label, count) - else: - net['label'] = label - count += 1 - - if(FLAGS.use_ipv6): - cidr_v6 = "%s/%s" % (fixed_net_v6[0], significant_bits_v6) - net['cidr_v6'] = cidr_v6 - - network_ref = self.db.network_create_safe(context, net) - - if network_ref: - self._create_fixed_ips(context, network_ref['id']) - - def get_network_host(self, context): - """Get the network host for the current context.""" - network_ref = self.db.network_get_by_bridge(context, - FLAGS.flat_network_bridge) - # NOTE(vish): If the network has no host, use the network_host flag. - # This could eventually be a a db lookup of some sort, but - # a flag is easy to handle for now. - host = network_ref['host'] - if not host: - topic = self.db.queue_get_for(context, - FLAGS.network_topic, - FLAGS.network_host) - if FLAGS.fake_call: - return self.set_network_host(context, network_ref['id']) - host = rpc.call(context, - FLAGS.network_topic, - {"method": "set_network_host", - "args": {"network_id": network_ref['id']}}) - return host - def _on_set_network_host(self, context, network_id): """Called when this host becomes the host for a network.""" net = {} @@ -433,7 +416,7 @@ class FlatManager(NetworkManager): raise NotImplementedError() -class FlatDHCPManager(FlatManager): +class FlatDHCPManager(NetworkManager): """Flat networking with dhcp. FlatDHCPManager will start up one dhcp server to give out addresses. -- cgit From 7e463d262339acc7c611b86471b1422dea50d1ee Mon Sep 17 00:00:00 2001 From: Salvatore Orlando <salvatore.orlando@eu.citrix.com> Date: Thu, 24 Feb 2011 11:43:40 +0000 Subject: xenapi plugin function now checks whether /boot/guest already exists. If not, it creates the directory --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 8cb439259..61b947c25 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -73,6 +73,10 @@ def _copy_kernel_vdi(dest, copy_args): logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s", dest, vdi_uuid) filename = KERNEL_DIR + '/' + vdi_uuid + #make sure KERNEL_DIR exists, otherwise create it + if not os.path.isdir(KERNEL_DIR): + logging.debug("Creating directory %s", KERNEL_DIR) + os.makedirs(KERNEL_DIR) #read data from /dev/ and write into a file on /boot/guest of = open(filename, 'wb') f = open(dest, 'rb') -- cgit From f8640adff4beff1e7d77bbd67771a6d072d42140 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio <armando.migliaccio@citrix.com> Date: Thu, 24 Feb 2011 13:58:31 +0000 Subject: Get DNS value from Flag, when working in FlatNetworking mode. Passing the flag was ineffective previously. This is an easy fix. I think we would need nova-manage to accept dns also from command line --- nova/network/manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/network/manager.py b/nova/network/manager.py index 1df193be0..12a0c5018 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -369,6 +369,7 @@ class FlatManager(NetworkManager): project_net = IPy.IP(cidr) net = {} net['bridge'] = FLAGS.flat_network_bridge + net['dns'] = FLAGS.flat_network_dns net['cidr'] = cidr net['netmask'] = str(project_net.netmask()) net['gateway'] = str(project_net[1]) -- cgit From a721d2208ed9d88884925d6f1e2c8e26d7d1ea27 Mon Sep 17 00:00:00 2001 From: Thierry Carrez <thierry@openstack.org> Date: Thu, 24 Feb 2011 15:19:29 +0100 Subject: Globally exclude .pyc files from tarball contents --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index f0a9cffb3..2ceed34f3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -38,3 +38,4 @@ include nova/tests/db/nova.austin.sqlite include plugins/xenapi/README include plugins/xenapi/etc/xapi.d/plugins/objectstore include plugins/xenapi/etc/xapi.d/plugins/pluginlib_nova.py +global-exclude *.pyc -- cgit From 8e2ebb1a963f58514d8fb6aab4a75627e72484b9 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando <salvatore.orlando@eu.citrix.com> Date: Thu, 24 Feb 2011 16:04:13 +0000 Subject: stubbing out _is_vdi_pv for test purposes --- nova/tests/test_xenapi.py | 1 + nova/tests/xenapi/stubs.py | 6 ++++++ nova/virt/xenapi/vm_utils.py | 27 ++++++++++++++------------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 6b8efc9d8..2cbe58aab 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -167,6 +167,7 @@ class XenAPIVMTestCase(test.TestCase): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) stubs.stubout_get_this_vm_uuid(self.stubs) stubs.stubout_stream_disk(self.stubs) + stubs.stubout_is_vdi_pv(self.stubs) self.stubs.Set(VMOps, 'reset_network', reset_network) glance_stubs.stubout_glance_client(self.stubs, glance_stubs.FakeGlance) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 624995ada..4fec2bd75 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -130,6 +130,12 @@ def stubout_stream_disk(stubs): stubs.Set(vm_utils, '_stream_disk', f) +def stubout_is_vdi_pv(stubs): + def f(_1): + return False + stubs.Set(vm_utils, '_is_vdi_pv', f) + + class FakeSessionForVMTests(fake.SessionBase): """ Stubs out a XenAPISession for VM tests """ def __init__(self, uri): diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 80cc3035d..564a25057 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -400,19 +400,7 @@ class VMHelper(HelperBase): @classmethod def _lookup_image_glance(cls, session, vdi_ref): LOG.debug(_("Looking up vdi %s for PV kernel"), vdi_ref) - - def is_vdi_pv(dev): - LOG.debug(_("Running pygrub against %s"), dev) - output = os.popen('pygrub -qn /dev/%s' % dev) - for line in output.readlines(): - #try to find kernel string - m = re.search('(?<=kernel:)/.*(?:>)', line) - if m and m.group(0).find('xen') != -1: - LOG.debug(_("Found Xen kernel %s") % m.group(0)) - return True - LOG.debug(_("No Xen kernel found. Booting HVM.")) - return False - return with_vdi_attached_here(session, vdi_ref, True, is_vdi_pv) + return with_vdi_attached_here(session, vdi_ref, True, _is_vdi_pv) @classmethod def lookup(cls, session, i): @@ -714,6 +702,19 @@ def get_this_vm_ref(session): return session.get_xenapi().VM.get_by_uuid(get_this_vm_uuid()) +def _is_vdi_pv(dev): + LOG.debug(_("Running pygrub against %s"), dev) + output = os.popen('pygrub -qn /dev/%s' % dev) + for line in output.readlines(): + #try to find kernel string + m = re.search('(?<=kernel:)/.*(?:>)', line) + if m and m.group(0).find('xen') != -1: + LOG.debug(_("Found Xen kernel %s") % m.group(0)) + return True + LOG.debug(_("No Xen kernel found. Booting HVM.")) + return False + + def _stream_disk(dev, type, virtual_size, image_file): offset = 0 if type == ImageType.DISK: -- cgit From 9e018bbee1d4a308fdf1700bd25aa733cedc26e6 Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Thu, 24 Feb 2011 13:01:16 -0600 Subject: Updated email in Authors --- .mailmap | 1 + Authors | 2 +- nova/virt/xenapi/vmops.py | 20 +++++++++----------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.mailmap b/.mailmap index a839eba6c..d479bfdf2 100644 --- a/.mailmap +++ b/.mailmap @@ -19,6 +19,7 @@ <jmckenty@gmail.com> <jmckenty@joshua-mckentys-macbook-pro.local> <jmckenty@gmail.com> <jmckenty@yyj-dhcp171.corp.flock.com> <jmckenty@gmail.com> <joshua.mckenty@nasa.gov> +<josh@jk0.org> <josh.kearney@rackspace.com> <justin@fathomdb.com> <justinsb@justinsb-desktop> <justin@fathomdb.com> <superstack@superstack.org> <masumotok@nttdata.co.jp> Masumoto<masumotok@nttdata.co.jp> diff --git a/Authors b/Authors index 494e614a0..39241b45c 100644 --- a/Authors +++ b/Authors @@ -31,7 +31,7 @@ John Dewey <john@dewey.ws> Jonathan Bryce <jbryce@jbryce.com> Jordan Rinke <jordan@openstack.org> Josh Durgin <joshd@hq.newdream.net> -Josh Kearney <josh.kearney@rackspace.com> +Josh Kearney <josh@jk0.org> Joshua McKenty <jmckenty@gmail.com> Justin Santa Barbara <justin@fathomdb.com> Kei Masumoto <masumotok@nttdata.co.jp> diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7bffd8c6c..a47d23f88 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -476,19 +476,15 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - self._shutdown(instance, vm) + + # Temporary instance target_vm = VMHelper.lookup(self._session, "instance-00000001") - #target_vm = self.compute_api.create( - # context=context.get_admin_context(), - # instance_type="m1.tiny", - # image_id=1, - # kernel_id=3, - # ramdisk_id=2, - # display_name="test", - # display_description="test") - #print context.get_admin_context().__dict__ - #print target_vm + + # Plan of action + # Create `instance` object for rescue_vm + # rescue_instance = self._make_rescue_instance() # Write this + #rescue_vm = self.spawn(instance) vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] @@ -505,6 +501,8 @@ class VMOps(object): def unrescue(self, instance, callback): """Unrescue the specified instance""" vm = self._get_vm_opaque_ref(instance) + + # Temporary instance target_vm = VMHelper.lookup(self._session, "instance-00000001") vbds = self._session.get_xenapi().VM.get_VBDs(target_vm) -- cgit From bf222b9173e0a5a0bfbcf4705caab390ee33334b Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Thu, 24 Feb 2011 11:17:42 -0800 Subject: moved migrate script to 007 (again..sigh) --- .../versions/006_add_instance_types.py | 86 ---------------------- .../versions/007_add_instance_types.py | 86 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 86 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py deleted file mode 100644 index fec191214..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/006_add_instance_types.py +++ /dev/null @@ -1,86 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from sqlalchemy import * -from migrate import * - -from nova import api -from nova import db -from nova import log as logging - -import datetime - -meta = MetaData() - - -# -# New Tables -# -instance_types = Table('instance_types', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('name', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False), - unique=True), - Column('id', Integer(), primary_key=True, nullable=False), - Column('memory_mb', Integer(), nullable=False), - Column('vcpus', Integer(), nullable=False), - Column('local_gb', Integer(), nullable=False), - Column('flavorid', Integer(), nullable=False, unique=True), - Column('swap', Integer(), nullable=False, default=0), - Column('rxtx_quota', Integer(), nullable=False, default=0), - Column('rxtx_cap', Integer(), nullable=False, default=0)) - - -def upgrade(migrate_engine): - # Upgrade operations go here - # Don't create your own engine; bind migrate_engine - # to your metadata - meta.bind = migrate_engine - try: - instance_types.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating instance_types table') - raise - - # Here are the old static instance types - INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - try: - i = instance_types.insert() - for name, values in INSTANCE_TYPES.iteritems(): - # FIXME(kpepple) should we be seeding created_at / updated_at ? - # now = datetime.datatime.utcnow() - i.execute({'name': name, 'memory_mb': values["memory_mb"], - 'vcpus': values["vcpus"], 'deleted': 0, - 'local_gb': values["local_gb"], - 'flavorid': values["flavorid"]}) - except Exception: - logging.info(repr(table)) - logging.exception('Exception while seeding instance_types table') - raise - - -def downgrade(migrate_engine): - # Operations to reverse the above upgrade go here. - for table in (instance_types): - table.drop() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py new file mode 100644 index 000000000..fec191214 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py @@ -0,0 +1,86 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import api +from nova import db +from nova import log as logging + +import datetime + +meta = MetaData() + + +# +# New Tables +# +instance_types = Table('instance_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + unique=True), + Column('id', Integer(), primary_key=True, nullable=False), + Column('memory_mb', Integer(), nullable=False), + Column('vcpus', Integer(), nullable=False), + Column('local_gb', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False, unique=True), + Column('swap', Integer(), nullable=False, default=0), + Column('rxtx_quota', Integer(), nullable=False, default=0), + Column('rxtx_cap', Integer(), nullable=False, default=0)) + + +def upgrade(migrate_engine): + # Upgrade operations go here + # Don't create your own engine; bind migrate_engine + # to your metadata + meta.bind = migrate_engine + try: + instance_types.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating instance_types table') + raise + + # Here are the old static instance types + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + try: + i = instance_types.insert() + for name, values in INSTANCE_TYPES.iteritems(): + # FIXME(kpepple) should we be seeding created_at / updated_at ? + # now = datetime.datatime.utcnow() + i.execute({'name': name, 'memory_mb': values["memory_mb"], + 'vcpus': values["vcpus"], 'deleted': 0, + 'local_gb': values["local_gb"], + 'flavorid': values["flavorid"]}) + except Exception: + logging.info(repr(table)) + logging.exception('Exception while seeding instance_types table') + raise + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + for table in (instance_types): + table.drop() -- cgit From 377b59ec5ae13d37cc375dd9b9cf7eb8d89be196 Mon Sep 17 00:00:00 2001 From: Todd Willey <todd@ansolabs.com> Date: Thu, 24 Feb 2011 14:36:15 -0500 Subject: Fix copypasta pep8 violation. --- nova/adminclient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/adminclient.py b/nova/adminclient.py index fe2aca351..fc3c5c5fe 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -198,7 +198,7 @@ class HostInfo(object): class Vpn(object): """ Information about a Vpn, as parsed through SAX - + **Fields Include** * instance_id -- cgit From 907df1b90e5c22eb82ce85bc12f48d8abf09b665 Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Thu, 24 Feb 2011 13:57:11 -0600 Subject: nothing --- nova/virt/xenapi/vmops.py | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a47d23f88..0b9b7d0a4 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -475,37 +475,29 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" - vm = self._get_vm_opaque_ref(instance) - self._shutdown(instance, vm) - - # Temporary instance - target_vm = VMHelper.lookup(self._session, "instance-00000001") + #vm = self._get_vm_opaque_ref(instance) + #self._shutdown(instance, vm) - # Plan of action - # Create `instance` object for rescue_vm - # rescue_instance = self._make_rescue_instance() # Write this + print instance.__dict__ + print instance.name + print "%s-rescue" % instance.name #rescue_vm = self.spawn(instance) - vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] - vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] - vbd_ref = VMHelper.create_vbd( - self._session, - target_vm, - vdi_ref, - 1, - False) + #vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] + #vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] + #vbd_ref = VMHelper.create_vbd( + # self._session, + # rescue_vm, + # vdi_ref, + # 1, + # False) - # Plug the VBD into the target instance - self._session.call_xenapi("Async.VBD.plug", vbd_ref) + #self._session.call_xenapi("Async.VBD.plug", vbd_ref) def unrescue(self, instance, callback): """Unrescue the specified instance""" vm = self._get_vm_opaque_ref(instance) - - # Temporary instance - target_vm = VMHelper.lookup(self._session, "instance-00000001") - - vbds = self._session.get_xenapi().VM.get_VBDs(target_vm) + vbds = self._session.get_xenapi().VM.get_VBDs(vm) for vbd_ref in vbds: vbd = self._session.get_xenapi().VBD.get_record(vbd_ref) @@ -513,7 +505,7 @@ class VMOps(object): VMHelper.unplug_vbd(self._session, vbd_ref) VMHelper.destroy_vbd(self._session, vbd_ref) - self._start(instance, vm) + self.reboot(instance) def get_info(self, instance): """Return data about VM instance""" -- cgit From 8635f7a306e4cfa41ad09a18602efa7793f6da95 Mon Sep 17 00:00:00 2001 From: Trey Morris <trey.morris@rackspace.com> Date: Thu, 24 Feb 2011 15:04:07 -0600 Subject: moved network injection and vif creation to above vm start in vmops spawn --- nova/virt/xenapi/vmops.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 76b88a8bd..3aa5a23e5 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -104,6 +104,10 @@ class VMOps(object): instance, kernel, ramdisk, pv_kernel) VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True) + # inject_network_info and create vifs + networks = self.inject_network_info(instance) + self.create_vifs(instance, networks) + LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) instance_name = instance.name @@ -134,9 +138,7 @@ class VMOps(object): timer.f = _wait_for_boot - # call to reset network to inject network info and configure - networks = self.inject_network_info(instance) - self.create_vifs(instance, networks) + # call to reset network to configure network from xenstore self.reset_network(instance) return timer.start(interval=0.5, now=True) -- cgit From f4221346418ef103635b104fc152a2507d60a8dc Mon Sep 17 00:00:00 2001 From: Trey Morris <trey.morris@rackspace.com> Date: Thu, 24 Feb 2011 17:25:00 -0600 Subject: forgot to get vm_opaque_ref --- nova/virt/xenapi/vmops.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3aa5a23e5..8abaa14fa 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -450,6 +450,7 @@ class VMOps(object): Creates vifs for an instance """ + vm_opaque_ref = self._get_vm_opaque_ref(instance.id) logging.debug(_("creating vif(s) for vm: |%s|"), vm_opaque_ref) if networks is None: networks = db.network_get_all_by_instance(admin_context, -- cgit From 8cf6a0c01ee39066f17a11d5e9313c2828a59634 Mon Sep 17 00:00:00 2001 From: Rick Harris <rick.harris@rackspace.com> Date: Fri, 25 Feb 2011 01:50:18 +0000 Subject: No longer users image/ directory in tarball --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index e229a9358..bcdf34413 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -163,16 +163,14 @@ def _fixup_vhds(sr_path, staging_path, uuid_stack): "VHD %(path)s is marked as hidden without child" % locals()) - image_path = os.path.join(staging_path, 'image') - - orig_base_copy_path = os.path.join(image_path, 'image.vhd') + orig_base_copy_path = os.path.join(staging_path, 'image.vhd') if not os.path.exists(orig_base_copy_path): raise Exception("Invalid image: image.vhd not present") base_copy_path, base_copy_uuid = rename_with_uuid(orig_base_copy_path) vdi_uuid = base_copy_uuid - orig_snap_path = os.path.join(image_path, 'snap.vhd') + orig_snap_path = os.path.join(staging_path, 'snap.vhd') if os.path.exists(orig_snap_path): snap_path, snap_uuid = rename_with_uuid(orig_snap_path) vdi_uuid = snap_uuid @@ -192,11 +190,9 @@ def _prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids): """Hard-link VHDs into staging area with appropriate filename ('snap' or 'image.vhd') """ - image_path = os.path.join(staging_path, 'image') - os.mkdir(image_path) for name, uuid in vdi_uuids.items(): source = os.path.join(sr_path, "%s.vhd" % uuid) - link_name = os.path.join(image_path, "%s.vhd" % name) + link_name = os.path.join(staging_path, "%s.vhd" % name) os.link(source, link_name) -- cgit From ec9ede003c839248ca9593c03160a23ff8ec0db1 Mon Sep 17 00:00:00 2001 From: Rick Harris <rick.harris@rackspace.com> Date: Fri, 25 Feb 2011 02:26:46 +0000 Subject: Adding _make_subprocess function --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 57 ++++++++++++---------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index bcdf34413..869d46e70 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -75,17 +75,15 @@ def _download_tarball(sr_path, staging_path, image_id, glance_host, elif resp.status != httplib.OK: raise Exception("Unexpected response from Glance %i" % res.status) - tar_args = shlex.split( - "tar -zx --directory=%(staging_path)s" % locals()) - tar_proc = subprocess.Popen( - tar_args, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + tar_cmd = "tar -zx --directory=%(staging_path)s" % locals() + tar_proc = _make_subprocess(tar_cmd, stderr=True, stdin=True) chunk = resp.read(CHUNK_SIZE) while chunk: tar_proc.stdin.write(chunk) chunk = resp.read(CHUNK_SIZE) - _assert_process_success(tar_proc, "tar") + _finish_subprocess(tar_proc, "tar") conn.close() @@ -130,12 +128,10 @@ def _fixup_vhds(sr_path, staging_path, uuid_stack): This needs to be done before we move both VHDs into the SR to prevent the base_copy from being DOA (deleted-on-arrival). """ - modify_args = shlex.split( - "vhd-util modify -n %(child_path)s -p %(parent_path)s" - % locals()) - modify_proc = subprocess.Popen( - modify_args, stderr=subprocess.PIPE) - _assert_process_success(modify_proc, "vhd-util") + modify_cmd = ("vhd-util modify -n %(child_path)s -p %(parent_path)s" + % locals()) + modify_proc = _make_subprocess(modify_cmd, stderr=True) + _finish_subprocess(modify_proc, "vhd-util") def move_into_sr(orig_path): """Move a file into the SR""" @@ -150,11 +146,9 @@ def _fixup_vhds(sr_path, staging_path, uuid_stack): present, then the image.vhd better not be marked 'hidden' or it will be deleted when moved into the SR. """ - vhd_query_args = shlex.split( - "vhd-util query -n %(path)s -f" % locals()) - vhd_query_proc = subprocess.Popen( - vhd_query_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = _assert_process_success(vhd_query_proc, "vhd-util") + query_cmd = "vhd-util query -n %(path)s -f" % locals() + query_proc = _make_subprocess(query_cmd, stdout=True, stderr=True) + out, err = _finish_subprocess(query_proc, "vhd-util") for line in out.splitlines(): if line.startswith('hidden'): value = line.split(':')[1].strip() @@ -208,25 +202,24 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): # FIXME(sirp): nokernel signals Nova to use a raw image. This is defined by # FLAGS.null_kernel. Is there a way to get rid of this? + # TODO(sirp): make `store` configurable headers = { - 'x-image-meta-store': 'file', + 'content-type': 'application/octet-stream', + 'transfer-encoding': 'chunked', 'x-image-meta-is_public': 'True', - 'x-image-meta-type': 'vhd', 'x-image-meta-status': 'queued', + 'x-image-meta-store': 'file', + 'x-image-meta-type': 'vhd', 'x-image-meta-property-kernel-id': 'nokernel', 'x-image-meta-property-ramdisk-id': 'noramdisk', 'x-image-meta-property-container-format': 'tarball', - 'transfer-encoding': 'chunked', - 'content-type': 'application/octet-stream', } for header, value in headers.iteritems(): conn.putheader(header, value) conn.endheaders() - tar_args = shlex.split( - "tar -zc --directory=%(staging_path)s ." % locals()) - tar_proc = subprocess.Popen( - tar_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + tar_cmd = "tar -zc --directory=%(staging_path)s ." % locals() + tar_proc = _make_subprocess(tar_cmd, stdout=True, stderr=True) chunk = tar_proc.stdout.read(CHUNK_SIZE) while chunk: @@ -234,7 +227,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): chunk = tar_proc.stdout.read(CHUNK_SIZE) conn.send("0\r\n\r\n") - _assert_process_success(tar_proc, "tar") + _finish_subprocess(tar_proc, "tar") resp = conn.getresponse() if resp.status != httplib.OK: @@ -289,7 +282,19 @@ def _cleanup_staging_area(staging_path): shutil.rmtree(staging_path) -def _assert_process_success(proc, cmd): +def _make_subprocess(cmdline, stdout=False, stderr=False, stdin=False): + """Make a subprocess according to the given command-line string + """ + kwargs = {} + kwargs['stdout'] = stdout and subprocess.PIPE or None + kwargs['stderr'] = stderr and subprocess.PIPE or None + kwargs['stdin'] = stdin and subprocess.PIPE or None + args = shlex.split(cmdline) + proc = subprocess.Popen(args, **kwargs) + return proc + + +def _finish_subprocess(proc, cmd): """Ensure that the process returned a zero exit code indicating success """ out, err = proc.communicate() -- cgit From e3d6dc70a6b77d80afcf87473bc79549540ac4ce Mon Sep 17 00:00:00 2001 From: Rick Harris <rick.harris@rackspace.com> Date: Fri, 25 Feb 2011 02:51:14 +0000 Subject: Removing unecessary nokernel stuff --- nova/api/openstack/servers.py | 51 ++++++++++++---------- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 4 -- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index c8c94f1fd..f51da0cdd 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -136,28 +136,6 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotFound()) return exc.HTTPAccepted() - def _get_kernel_ramdisk_from_image(self, req, image_id): - """ - Machine images are associated with Kernels and Ramdisk images via - metadata stored in Glance as 'image_properties' - """ - # FIXME(sirp): Currently Nova requires us to specify the `null_kernel` - # identifier ('nokernel') to indicate a RAW (or VHD) image. It would - # be better if we could omit the kernel_id and ramdisk_id properties - # on the image - def lookup(image, param): - _image_id = image['id'] - try: - return image['properties'][param] - except KeyError: - LOG.debug( - _("%(param)s property not found for image %(_image_id)s") % - locals()) - return None - - image = self._image_service.show(req.environ['nova.context'], image_id) - return lookup(image, 'kernel_id'), lookup(image, 'ramdisk_id') - def create(self, req): """ Creates a new server for a given user """ env = self._deserialize(req.body, req) @@ -367,3 +345,32 @@ class Controller(wsgi.Controller): action=item.action, error=item.error)) return dict(actions=actions) + + def _get_kernel_ramdisk_from_image(self, req, image_id): + """Retrevies kernel and ramdisk IDs from Glance + + Only 'machine' (ami) type use kernel and ramdisk outside of the + image. + """ + # FIXME(sirp): Since we're retrieving the kernel_id from an + # image_property, this means only Glance is supported. + # The BaseImageService needs to expose a consistent way of accessing + # kernel_id and ramdisk_id + image = self._image_service.show(req.environ['nova.context'], image_id) + + if image['type'] != 'machine': + return None, None + + try: + kernel_id = image['properties']['kernel_id'] + except KeyError: + raise exception.NotFound( + _("Kernel not found for image %(image_id)s") % locals()) + + try: + ramdisk_id = image['properties']['ramdisk_id'] + except KeyError: + raise exception.NotFound( + _("Ramdisk not found for image %(image_id)s") % locals()) + + return kernel_id, ramdisk_id diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 869d46e70..18e4866f7 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -200,8 +200,6 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): # to request conn.putrequest('PUT', '/images/%s' % image_id) - # FIXME(sirp): nokernel signals Nova to use a raw image. This is defined by - # FLAGS.null_kernel. Is there a way to get rid of this? # TODO(sirp): make `store` configurable headers = { 'content-type': 'application/octet-stream', @@ -210,8 +208,6 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): 'x-image-meta-status': 'queued', 'x-image-meta-store': 'file', 'x-image-meta-type': 'vhd', - 'x-image-meta-property-kernel-id': 'nokernel', - 'x-image-meta-property-ramdisk-id': 'noramdisk', 'x-image-meta-property-container-format': 'tarball', } for header, value in headers.iteritems(): -- cgit From 2218cb025adca1ded3e6596acc182b88742e3a51 Mon Sep 17 00:00:00 2001 From: Todd Willey <todd@ansolabs.com> Date: Thu, 24 Feb 2011 21:59:36 -0500 Subject: Rename auth_token db methods to follow standard. --- nova/api/openstack/auth.py | 6 +++--- nova/db/api.py | 12 ++++++------ nova/db/sqlalchemy/api.py | 6 +++--- nova/tests/api/openstack/fakes.py | 6 +++--- nova/tests/api/openstack/test_auth.py | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 1dfdd5318..c844c6231 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -103,11 +103,11 @@ class AuthMiddleware(wsgi.Middleware): 2 days ago. """ ctxt = context.get_admin_context() - token = self.db.auth_get_token(ctxt, token_hash) + token = self.db.auth_token_get(ctxt, token_hash) if token: delta = datetime.datetime.now() - token.created_at if delta.days >= 2: - self.db.auth_destroy_token(ctxt, token) + self.db.auth_token_destroy(ctxt, token) else: return self.auth.get_user(token.user_id) return None @@ -131,6 +131,6 @@ class AuthMiddleware(wsgi.Middleware): token_dict['server_management_url'] = req.url token_dict['storage_url'] = '' token_dict['user_id'] = user.id - token = self.db.auth_create_token(ctxt, token_dict) + token = self.db.auth_token_create(ctxt, token_dict) return token, user return None, None diff --git a/nova/db/api.py b/nova/db/api.py index 0a010e727..aeb9b7ebf 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -630,19 +630,19 @@ def iscsi_target_create_safe(context, values): ############### -def auth_destroy_token(context, token): +def auth_token_destroy(context, token): """Destroy an auth token.""" - return IMPL.auth_destroy_token(context, token) + return IMPL.auth_token_destroy(context, token) -def auth_get_token(context, token_hash): +def auth_token_get(context, token_hash): """Retrieves a token given the hash representing it.""" - return IMPL.auth_get_token(context, token_hash) + return IMPL.auth_token_get(context, token_hash) -def auth_create_token(context, token): +def auth_token_create(context, token): """Creates a new token.""" - return IMPL.auth_create_token(context, token) + return IMPL.auth_token_create(context, token) ################### diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index d8751bef4..0c11b2982 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1262,13 +1262,13 @@ def iscsi_target_create_safe(context, values): @require_admin_context -def auth_destroy_token(_context, token): +def auth_token_destroy(_context, token): session = get_session() session.delete(token) @require_admin_context -def auth_get_token(_context, token_hash): +def auth_token_get(_context, token_hash): session = get_session() tk = session.query(models.AuthToken).\ filter_by(token_hash=token_hash).\ @@ -1279,7 +1279,7 @@ def auth_get_token(_context, token_hash): @require_admin_context -def auth_create_token(_context, token): +def auth_token_create(_context, token): tk = models.AuthToken() tk.update(token) tk.save() diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index fb282f1c9..142626de9 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -203,17 +203,17 @@ class FakeAuthDatabase(object): data = {} @staticmethod - def auth_get_token(context, token_hash): + def auth_token_get(context, token_hash): return FakeAuthDatabase.data.get(token_hash, None) @staticmethod - def auth_create_token(context, token): + def auth_token_create(context, token): fake_token = FakeToken(created_at=datetime.datetime.now(), **token) FakeAuthDatabase.data[fake_token.token_hash] = fake_token return fake_token @staticmethod - def auth_destroy_token(context, token): + def auth_token_destroy(context, token): if token.token_hash in FakeAuthDatabase.data: del FakeAuthDatabase.data['token_hash'] diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py index 13f6c3a1c..86dfb110f 100644 --- a/nova/tests/api/openstack/test_auth.py +++ b/nova/tests/api/openstack/test_auth.py @@ -99,10 +99,10 @@ class Test(test.TestCase): token_hash=token_hash, created_at=datetime.datetime(1990, 1, 1)) - self.stubs.Set(fakes.FakeAuthDatabase, 'auth_destroy_token', + self.stubs.Set(fakes.FakeAuthDatabase, 'auth_token_destroy', destroy_token_mock) - self.stubs.Set(fakes.FakeAuthDatabase, 'auth_get_token', + self.stubs.Set(fakes.FakeAuthDatabase, 'auth_token_get', bad_token) req = webob.Request.blank('/v1.0/') -- cgit From d27197ae53f3282280198d9bfe7f37a059fa8a35 Mon Sep 17 00:00:00 2001 From: Rick Harris <rick.harris@rackspace.com> Date: Fri, 25 Feb 2011 03:16:04 +0000 Subject: Removing unecessary headers --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 18e4866f7..7531af4ec 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -83,7 +83,7 @@ def _download_tarball(sr_path, staging_path, image_id, glance_host, tar_proc.stdin.write(chunk) chunk = resp.read(CHUNK_SIZE) - _finish_subprocess(tar_proc, "tar") + _finish_subprocess(tar_proc, tar_cmd) conn.close() @@ -131,7 +131,7 @@ def _fixup_vhds(sr_path, staging_path, uuid_stack): modify_cmd = ("vhd-util modify -n %(child_path)s -p %(parent_path)s" % locals()) modify_proc = _make_subprocess(modify_cmd, stderr=True) - _finish_subprocess(modify_proc, "vhd-util") + _finish_subprocess(modify_proc, modify_cmd) def move_into_sr(orig_path): """Move a file into the SR""" @@ -148,7 +148,8 @@ def _fixup_vhds(sr_path, staging_path, uuid_stack): """ query_cmd = "vhd-util query -n %(path)s -f" % locals() query_proc = _make_subprocess(query_cmd, stdout=True, stderr=True) - out, err = _finish_subprocess(query_proc, "vhd-util") + out, err = _finish_subprocess(query_proc, query_cmd) + for line in out.splitlines(): if line.startswith('hidden'): value = line.split(':')[1].strip() @@ -206,9 +207,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): 'transfer-encoding': 'chunked', 'x-image-meta-is_public': 'True', 'x-image-meta-status': 'queued', - 'x-image-meta-store': 'file', - 'x-image-meta-type': 'vhd', - 'x-image-meta-property-container-format': 'tarball', + 'x-image-meta-type': 'vhd' } for header, value in headers.iteritems(): conn.putheader(header, value) @@ -223,7 +222,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): chunk = tar_proc.stdout.read(CHUNK_SIZE) conn.send("0\r\n\r\n") - _finish_subprocess(tar_proc, "tar") + _finish_subprocess(tar_proc, tar_cmd) resp = conn.getresponse() if resp.status != httplib.OK: @@ -290,14 +289,14 @@ def _make_subprocess(cmdline, stdout=False, stderr=False, stdin=False): return proc -def _finish_subprocess(proc, cmd): +def _finish_subprocess(proc, cmdline): """Ensure that the process returned a zero exit code indicating success """ out, err = proc.communicate() ret = proc.returncode if ret != 0: - msg = "%(cmd)s returned non-zero exit code (%i): '%s'" % (ret, err) - raise Exception(msg) + raise Exception("'%(cmdline)s' returned non-zero exit code: " + "retcode=%(ret)i, stderr='%(err)s'" % locals()) return out, err -- cgit From 865c3d57f8b84dfcc493ecead12816874b160e35 Mon Sep 17 00:00:00 2001 From: Todd Willey <todd@ansolabs.com> Date: Thu, 24 Feb 2011 23:51:17 -0500 Subject: Pass id of token to be deleted to the db api, not the actual object. --- nova/api/openstack/auth.py | 2 +- nova/db/api.py | 4 ++-- nova/db/sqlalchemy/api.py | 9 ++++++--- nova/tests/api/openstack/fakes.py | 13 ++++++++++--- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index c844c6231..dff69a7f2 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -107,7 +107,7 @@ class AuthMiddleware(wsgi.Middleware): if token: delta = datetime.datetime.now() - token.created_at if delta.days >= 2: - self.db.auth_token_destroy(ctxt, token) + self.db.auth_token_destroy(ctxt, token.id) else: return self.auth.get_user(token.user_id) return None diff --git a/nova/db/api.py b/nova/db/api.py index aeb9b7ebf..4c7eb857f 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -630,9 +630,9 @@ def iscsi_target_create_safe(context, values): ############### -def auth_token_destroy(context, token): +def auth_token_destroy(context, token_id): """Destroy an auth token.""" - return IMPL.auth_token_destroy(context, token) + return IMPL.auth_token_destroy(context, token_id) def auth_token_get(context, token_hash): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 0c11b2982..0be08c4d1 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1262,16 +1262,19 @@ def iscsi_target_create_safe(context, values): @require_admin_context -def auth_token_destroy(_context, token): +def auth_token_destroy(context, token_id): session = get_session() - session.delete(token) + with session.begin(): + token_ref = auth_token_get(context, token_id, session=session) + token_ref.delete(session=session) @require_admin_context -def auth_token_get(_context, token_hash): +def auth_token_get(context, token_hash): session = get_session() tk = session.query(models.AuthToken).\ filter_by(token_hash=token_hash).\ + filter_by(deleted=can_read_deleted(context)).\ first() if not tk: raise exception.NotFound(_('Token %s does not exist') % token_hash) diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 142626de9..49ce8c1b5 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -188,7 +188,11 @@ def stub_out_glance(stubs, initial_fixtures=None): class FakeToken(object): + id = 0 + def __init__(self, **kwargs): + FakeToken.id += 1 + self.id = FakeToken.id for k, v in kwargs.iteritems(): setattr(self, k, v) @@ -210,12 +214,15 @@ class FakeAuthDatabase(object): def auth_token_create(context, token): fake_token = FakeToken(created_at=datetime.datetime.now(), **token) FakeAuthDatabase.data[fake_token.token_hash] = fake_token + FakeAuthDatabase.data['id_%i' % fake_token.id] = fake_token return fake_token @staticmethod - def auth_token_destroy(context, token): - if token.token_hash in FakeAuthDatabase.data: - del FakeAuthDatabase.data['token_hash'] + def auth_token_destroy(context, token_id): + token = FakeAuthDatabase.data.get('id_%i' % token_id) + if token and token.token_hash in FakeAuthDatabase.data: + del FakeAuthDatabase.data[token.token_hash] + del FakeAuthDatabase.data['id_%i' % token_id] class FakeAuthManager(object): -- cgit From 079b532a1080da9fe5d99e90fa9c60d16506de06 Mon Sep 17 00:00:00 2001 From: Rick Harris <rick.harris@rackspace.com> Date: Fri, 25 Feb 2011 16:24:51 +0000 Subject: Verify status of image is active --- nova/api/openstack/servers.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index f51da0cdd..bf5663f60 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -358,6 +358,11 @@ class Controller(wsgi.Controller): # kernel_id and ramdisk_id image = self._image_service.show(req.environ['nova.context'], image_id) + if image['status'] != 'active': + raise exception.Invalid( + _("Cannot build from image %(image_id)s, status not active") % + locals()) + if image['type'] != 'machine': return None, None -- cgit From 6033f657aad9bd5b244a21908caedfc92840c9cf Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Fri, 25 Feb 2011 10:54:37 -0600 Subject: Create rescue instance --- nova/db/sqlalchemy/models.py | 7 ++++- nova/virt/xenapi/vmops.py | 69 +++++++++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 1882efeba..b1eb1a7b7 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -126,11 +126,16 @@ class Certificate(BASE, NovaBase): class Instance(BASE, NovaBase): """Represents a guest vm.""" __tablename__ = 'instances' + onset_files = [] + id = Column(Integer, primary_key=True, autoincrement=True) @property def name(self): - return FLAGS.instance_name_template % self.id + base_name = FLAGS.instance_name_template % self.id + if getattr(self, '_rescue', False): + base_name += "-rescue" + return base_name admin_pass = Column(String(255)) user_id = Column(String(255)) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 0b9b7d0a4..d70129d80 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -65,20 +65,20 @@ class VMOps(object): def spawn(self, instance): """Create VM instance""" - vm = VMHelper.lookup(self._session, instance.name) + instance_name = instance.name + vm = VMHelper.lookup(self._session, instance_name) if vm is not None: raise exception.Duplicate(_('Attempted to create' - ' non-unique name %s') % instance.name) + ' non-unique name %s') % instance_name) #ensure enough free memory is available if not VMHelper.ensure_free_mem(self._session, instance): - name = instance['name'] - LOG.exception(_('instance %(name)s: not enough free memory') - % locals()) - db.instance_set_state(context.get_admin_context(), - instance['id'], - power_state.SHUTDOWN) - return + LOG.exception(_('instance %(instance_name)s: not enough free ' + 'memory') % locals()) + db.instance_set_state(context.get_admin_context(), + instance['id'], + power_state.SHUTDOWN) + return user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) @@ -150,9 +150,8 @@ class VMOps(object): LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) - instance_name = instance.name LOG.info(_('Spawning VM %(instance_name)s created %(vm_ref)s.') - % locals()) + % locals()) def _inject_onset_files(): onset_files = instance.onset_files @@ -176,18 +175,18 @@ class VMOps(object): def _wait_for_boot(): try: - state = self.get_info(instance['name'])['state'] + state = self.get_info(instance_name)['state'] db.instance_set_state(context.get_admin_context(), instance['id'], state) if state == power_state.RUNNING: - LOG.debug(_('Instance %s: booted'), instance['name']) + LOG.debug(_('Instance %s: booted'), instance_name) timer.stop() _inject_onset_files() return True except Exception, exc: LOG.warn(exc) LOG.exception(_('instance %s: failed to boot'), - instance['name']) + instance_name) db.instance_set_state(context.get_admin_context(), instance['id'], power_state.SHUTDOWN) @@ -475,24 +474,26 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" - #vm = self._get_vm_opaque_ref(instance) - #self._shutdown(instance, vm) + vm = self._get_vm_opaque_ref(instance) + self._shutdown(instance, vm) - print instance.__dict__ - print instance.name - print "%s-rescue" % instance.name - #rescue_vm = self.spawn(instance) + # log old instance + # log new instance - #vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] - #vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] - #vbd_ref = VMHelper.create_vbd( - # self._session, - # rescue_vm, - # vdi_ref, - # 1, - # False) + instance._rescue = True + self.spawn(instance) + rescue_vm = self._get_vm_opaque_ref(instance) - #self._session.call_xenapi("Async.VBD.plug", vbd_ref) + vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0] + vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"] + vbd_ref = VMHelper.create_vbd( + self._session, + rescue_vm, + vdi_ref, + 1, + False) + + self._session.call_xenapi("Async.VBD.plug", vbd_ref) def unrescue(self, instance, callback): """Unrescue the specified instance""" @@ -505,7 +506,15 @@ class VMOps(object): VMHelper.unplug_vbd(self._session, vbd_ref) VMHelper.destroy_vbd(self._session, vbd_ref) - self.reboot(instance) + # fetch old instance + # fetch new instance + # destroy new instance + # start old instance + + self.destroy(instance) + instance._rescue = False + + self._start(instance, vm) def get_info(self, instance): """Return data about VM instance""" -- cgit From 2dc9d8bfe4bcd61c4d634767715b2be3a214426e Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Fri, 25 Feb 2011 12:43:09 -0600 Subject: Teardown rescue instance --- nova/virt/xenapi/vmops.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 93c2ab0c7..6b531ca2c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -113,7 +113,7 @@ class VMOps(object): self.create_vifs(instance, networks) LOG.debug(_('Starting VM %s...'), vm_ref) - self._session.call_xenapi('VM.start', vm_ref, False, False) + self._start(instance, vm_ref) LOG.info(_('Spawning VM %(instance_name)s created %(vm_ref)s.') % locals()) @@ -438,12 +438,14 @@ class VMOps(object): def rescue(self, instance, callback): """Rescue the specified instance""" + rescue_vm = VMHelper.lookup(self._session, instance.name + "-rescue") + if rescue_vm: + raise RuntimeError(_( + "Instance is already in Rescue Mode: %s" % instance.name)) + vm = self._get_vm_opaque_ref(instance) self._shutdown(instance, vm) - # log old instance - # log new instance - instance._rescue = True self.spawn(instance) rescue_vm = self._get_vm_opaque_ref(instance) @@ -461,8 +463,16 @@ class VMOps(object): def unrescue(self, instance, callback): """Unrescue the specified instance""" - vm = self._get_vm_opaque_ref(instance) - vbds = self._session.get_xenapi().VM.get_VBDs(vm) + rescue_vm = VMHelper.lookup(self._session, instance.name + "-rescue") + + if not rescue_vm: + raise exception.NotFound(_( + "Instance is not in Rescue Mode: %s" % instance.name)) + + original_vm = self._get_vm_opaque_ref(instance) + vbds = self._session.get_xenapi().VM.get_VBDs(rescue_vm) + + instance._rescue = False for vbd_ref in vbds: vbd = self._session.get_xenapi().VBD.get_record(vbd_ref) @@ -470,15 +480,13 @@ class VMOps(object): VMHelper.unplug_vbd(self._session, vbd_ref) VMHelper.destroy_vbd(self._session, vbd_ref) - # fetch old instance - # fetch new instance - # destroy new instance - # start old instance + task1 = self._session.call_xenapi("Async.VM.hard_shutdown", rescue_vm) + self._session.wait_for_task(task1, instance.id) - self.destroy(instance) - instance._rescue = False + task2 = self._session.call_xenapi('Async.VM.destroy', rescue_vm) + self._session.wait_for_task(task2, instance.id) - self._start(instance, vm) + self._start(instance, original_vm) def get_info(self, instance): """Return data about VM instance""" -- cgit From 26d55d0d24dd5363d3b0dd6da0351fe389b79503 Mon Sep 17 00:00:00 2001 From: Christian Berendt <berendt@b1-systems.de> Date: Fri, 25 Feb 2011 19:59:26 +0100 Subject: check if QUERY_STRING is empty or not before building the request URL --- bin/nova-ajax-console-proxy | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/bin/nova-ajax-console-proxy b/bin/nova-ajax-console-proxy index 1e11c6d58..023e4023f 100755 --- a/bin/nova-ajax-console-proxy +++ b/bin/nova-ajax-console-proxy @@ -63,10 +63,16 @@ class AjaxConsoleProxy(object): def __call__(self, env, start_response): try: - req_url = '%s://%s%s?%s' % (env['wsgi.url_scheme'], - env['HTTP_HOST'], - env['PATH_INFO'], - env['QUERY_STRING']) + if env.has_key('QUERY_STRING'): + req_url = '%s://%s%s?%s' % (env['wsgi.url_scheme'], + env['HTTP_HOST'], + env['PATH_INFO'], + env['QUERY_STRING']) + else: + req_url = '%s://%s%s' % (env['wsgi.url_scheme'], + env['HTTP_HOST'], + env['PATH_INFO']) + if 'HTTP_REFERER' in env: auth_url = env['HTTP_REFERER'] else: -- cgit From 4453021476fac599c0cee126b6eaa426d4878145 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" <kevin.mitchell@rackspace.com> Date: Fri, 18 Mar 2011 02:09:46 +0000 Subject: Copy over to current trunk my tests, the 401/500 fix, and a couple of fixes to the committed fix which was actually brittle around the edges... --- Authors | 1 + nova/api/openstack/auth.py | 8 ++++++-- nova/db/api.py | 5 +++++ nova/db/sqlalchemy/api.py | 14 ++++++++++++-- nova/tests/api/openstack/test_auth.py | 28 ++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/Authors b/Authors index 494e614a0..a3c06dcf5 100644 --- a/Authors +++ b/Authors @@ -36,6 +36,7 @@ Joshua McKenty <jmckenty@gmail.com> Justin Santa Barbara <justin@fathomdb.com> Kei Masumoto <masumotok@nttdata.co.jp> Ken Pepple <ken.pepple@gmail.com> +Kevin L. Mitchell <kevin.mitchell@rackspace.com> Koji Iida <iida.koji@lab.ntt.co.jp> Lorin Hochstein <lorin@isi.edu> Matt Dietz <matt.dietz@rackspace.com> diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index dff69a7f2..6011e6115 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -26,6 +26,7 @@ import webob.dec from nova import auth from nova import context from nova import db +from nova import exception from nova import flags from nova import manager from nova import utils @@ -103,11 +104,14 @@ class AuthMiddleware(wsgi.Middleware): 2 days ago. """ ctxt = context.get_admin_context() - token = self.db.auth_token_get(ctxt, token_hash) + try: + token = self.db.auth_token_get(ctxt, token_hash) + except exception.NotFound: + return None if token: delta = datetime.datetime.now() - token.created_at if delta.days >= 2: - self.db.auth_token_destroy(ctxt, token.id) + self.db.auth_token_destroy(ctxt, token.token_hash) else: return self.auth.get_user(token.user_id) return None diff --git a/nova/db/api.py b/nova/db/api.py index 4c7eb857f..dcaf55e8f 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -640,6 +640,11 @@ def auth_token_get(context, token_hash): return IMPL.auth_token_get(context, token_hash) +def auth_token_update(context, token_hash, values): + """Updates a token given the hash representing it.""" + return IMPL.auth_token_update(context, token_hash, values) + + def auth_token_create(context, token): """Creates a new token.""" return IMPL.auth_token_create(context, token) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 0be08c4d1..6df2a8843 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1270,8 +1270,9 @@ def auth_token_destroy(context, token_id): @require_admin_context -def auth_token_get(context, token_hash): - session = get_session() +def auth_token_get(context, token_hash, session=None): + if session is None: + session = get_session() tk = session.query(models.AuthToken).\ filter_by(token_hash=token_hash).\ filter_by(deleted=can_read_deleted(context)).\ @@ -1281,6 +1282,15 @@ def auth_token_get(context, token_hash): return tk +@require_admin_context +def auth_token_update(context, token_hash, values): + session = get_session() + with session.begin(): + token_ref = auth_token_get(context, token_hash, session=session) + token_ref.update(values) + token_ref.save(session=session) + + @require_admin_context def auth_token_create(_context, token): tk = models.AuthToken() diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py index 86dfb110f..c42c12f7b 100644 --- a/nova/tests/api/openstack/test_auth.py +++ b/nova/tests/api/openstack/test_auth.py @@ -26,6 +26,7 @@ import nova.api.openstack.auth import nova.auth.manager from nova import auth from nova import context +from nova import db from nova import test from nova.tests.api.openstack import fakes @@ -130,6 +131,33 @@ class Test(test.TestCase): self.assertEqual(result.status, '401 Unauthorized') +class TestFunctional(test.TestCase): + def test_token_lp718999(self): + ctx = context.get_admin_context() + tok = db.auth_token_create(ctx, dict( + token_hash='bacon', + cdn_management_url='', + server_management_url='', + storage_url='', + user_id='ham', + )) + + db.auth_token_update(ctx, tok.token_hash, dict( + created_at=datetime.datetime(2000, 1, 1, 12, 0, 0), + )) + + req = webob.Request.blank('/v1.0/') + req.headers['X-Auth-Token'] = 'bacon' + result = req.get_response(fakes.wsgi_app()) + self.assertEqual(result.status, '401 Unauthorized') + + def test_token_doesnotexist(self): + req = webob.Request.blank('/v1.0/') + req.headers['X-Auth-Token'] = 'ham' + result = req.get_response(fakes.wsgi_app()) + self.assertEqual(result.status, '401 Unauthorized') + + class TestLimiter(test.TestCase): def setUp(self): super(TestLimiter, self).setUp() -- cgit From fa6778586ab303f9e65aa3c50b80d20a4f097c6f Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" <kevin.mitchell@rackspace.com> Date: Mon, 21 Mar 2011 00:41:23 +0000 Subject: Rename test to describe what it actually does --- nova/tests/api/openstack/test_auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py index c42c12f7b..ff8d42a14 100644 --- a/nova/tests/api/openstack/test_auth.py +++ b/nova/tests/api/openstack/test_auth.py @@ -132,7 +132,7 @@ class Test(test.TestCase): class TestFunctional(test.TestCase): - def test_token_lp718999(self): + def test_token_expiry(self): ctx = context.get_admin_context() tok = db.auth_token_create(ctx, dict( token_hash='bacon', -- cgit From 3368b32338f58ac6b77d0c142722c241961f858e Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Fri, 25 Feb 2011 12:16:58 -0800 Subject: use default flagfile in nova-api --- bin/nova-api | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/nova-api b/bin/nova-api index cf140570a..14be4b841 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -36,6 +36,7 @@ gettext.install('nova', unicode=1) from nova import flags from nova import log as logging +from nova import utils from nova import version from nova import wsgi @@ -80,6 +81,7 @@ def run_app(paste_config_file): if __name__ == '__main__': + utils.default_flagfile() FLAGS(sys.argv) logging.setup() LOG.audit(_("Starting nova-api node (version %s)"), -- cgit From 6cfa479a1375fb8274dd9130946eab38e1398538 Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Fri, 25 Feb 2011 14:35:29 -0600 Subject: Set rescue instance VIF device --- nova/virt/xenapi/vm_utils.py | 4 ++-- nova/virt/xenapi/vmops.py | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 206201817..f0b1e993c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -207,11 +207,11 @@ class VMHelper(HelperBase): raise StorageError(_('Unable to destroy VBD %s') % vbd_ref) @classmethod - def create_vif(cls, session, vm_ref, network_ref, mac_address): + def create_vif(cls, session, vm_ref, network_ref, mac_address, dev="0"): """Create a VIF record. Returns a Deferred that gives the new VIF reference.""" vif_rec = {} - vif_rec['device'] = '0' + vif_rec['device'] = dev vif_rec['network'] = network_ref vif_rec['VM'] = vm_ref vif_rec['MAC'] = mac_address diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 6b531ca2c..c1e9bec62 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -483,6 +483,14 @@ class VMOps(object): task1 = self._session.call_xenapi("Async.VM.hard_shutdown", rescue_vm) self._session.wait_for_task(task1, instance.id) + vdis = VMHelper.lookup_vm_vdis(self._session, rescue_vm) + for vdi in vdis: + try: + task = self._session.call_xenapi('Async.VDI.destroy', vdi) + self._session.wait_for_task(task, instance.id) + except self.XenAPI.Failure: + continue + task2 = self._session.call_xenapi('Async.VM.destroy', rescue_vm) self._session.wait_for_task(task2, instance.id) @@ -574,8 +582,17 @@ class VMOps(object): NetworkHelper.find_network_with_bridge(self._session, bridge) if network_ref: - VMHelper.create_vif(self._session, vm_opaque_ref, - network_ref, instance.mac_address) + try: + device = "1" if instance._rescue else "0" + except AttributeError: + device = "0" + + VMHelper.create_vif( + self._session, + vm_opaque_ref, + network_ref, + instance.mac_address, + device) def reset_network(self, instance): """ -- cgit From 7ad2e0a731e6b2fbace8528439f6a8c2f0d5aaad Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Fri, 25 Feb 2011 14:47:25 -0600 Subject: Removed unnecessary compute import --- nova/virt/xenapi/vmops.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c1e9bec62..3e3c9ff6e 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -27,7 +27,6 @@ import tempfile import uuid from nova import db -from nova import compute from nova import context from nova import log as logging from nova import exception @@ -50,7 +49,6 @@ class VMOps(object): def __init__(self, session): self.XenAPI = session.get_imported_xenapi() self._session = session - self.compute_api = compute.API() VMHelper.XenAPI = self.XenAPI -- cgit From e6ceb2987d1f30a1e972a4bf21a734f1a605d60e Mon Sep 17 00:00:00 2001 From: Christian Berendt <berendt@b1-systems.de> Date: Fri, 25 Feb 2011 22:15:51 +0100 Subject: fixed: bin/nova-ajax-console-proxy:66:19: W601 .has_key() is deprecated, use 'in' --- bin/nova-ajax-console-proxy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-ajax-console-proxy b/bin/nova-ajax-console-proxy index 023e4023f..bbd60bade 100755 --- a/bin/nova-ajax-console-proxy +++ b/bin/nova-ajax-console-proxy @@ -63,7 +63,7 @@ class AjaxConsoleProxy(object): def __call__(self, env, start_response): try: - if env.has_key('QUERY_STRING'): + if 'QUERY_STRING' in env: req_url = '%s://%s%s?%s' % (env['wsgi.url_scheme'], env['HTTP_HOST'], env['PATH_INFO'], -- cgit From 7c6638cc5effc7d727087ed4354c180d75476d1a Mon Sep 17 00:00:00 2001 From: Sandy Walsh <sandy.walsh@rackspace.com> Date: Fri, 25 Feb 2011 13:40:15 -0800 Subject: API changed to new style class --- nova/scheduler/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 8491bf3a9..2405f1343 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -25,7 +25,7 @@ FLAGS = flags.FLAGS LOG = logging.getLogger('nova.scheduler.api') -class API: +class API(object): """API for interacting with the scheduler.""" def _call_scheduler(self, method, context, params=None): -- cgit From 97566c04b3a04626f1bc1b66e72c61b4621b6c7d Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Fri, 25 Feb 2011 16:18:11 -0600 Subject: Bootlock original instance during rescue --- nova/virt/xenapi/vmops.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 3e3c9ff6e..21477a18c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -196,6 +196,19 @@ class VMOps(object): _('Instance not present %s') % instance_name) return vm + def _bootlock(self, vm, unlock=False): + """Prevent an instance from booting""" + if unlock: + self._session.call_xenapi( + "VM.remove_from_blocked_operations", + vm, + "start") + else: + self._session.call_xenapi( + "VM.set_blocked_operations", + vm, + {"start": ""}) + def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance @@ -443,6 +456,7 @@ class VMOps(object): vm = self._get_vm_opaque_ref(instance) self._shutdown(instance, vm) + self._bootlock(vm) instance._rescue = True self.spawn(instance) @@ -492,6 +506,7 @@ class VMOps(object): task2 = self._session.call_xenapi('Async.VM.destroy', rescue_vm) self._session.wait_for_task(task2, instance.id) + self._bootlock(original_vm, unlock=True) self._start(instance, original_vm) def get_info(self, instance): -- cgit From c7c3fe85186862d9d8989ebe01ad69e6cdaa4ee3 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" <kevin.mitchell@rackspace.com> Date: Sun, 20 Mar 2011 22:58:49 +0000 Subject: No reason to dump a stack trace just because we can't reach the AMQP servire; it ends up being just noise --- nova/rpc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/rpc.py b/nova/rpc.py index 205bb524a..089bf99c1 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -95,14 +95,14 @@ class Consumer(messaging.Consumer): fl_host = FLAGS.rabbit_host fl_port = FLAGS.rabbit_port fl_intv = FLAGS.rabbit_retry_interval - LOG.exception(_("AMQP server on %(fl_host)s:%(fl_port)d is" + LOG.error(_("AMQP server on %(fl_host)s:%(fl_port)d is" " unreachable. Trying again in %(fl_intv)d seconds.") % locals()) self.failed_connection = True if self.failed_connection: - LOG.exception(_("Unable to connect to AMQP server " - "after %d tries. Shutting down."), - FLAGS.rabbit_max_retries) + LOG.error(_("Unable to connect to AMQP server " + "after %d tries. Shutting down."), + FLAGS.rabbit_max_retries) sys.exit(1) def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False): -- cgit From 4a217b640c3e0f488c87572787c92b6808ab0a9a Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" <kevin.mitchell@rackspace.com> Date: Fri, 25 Mar 2011 05:30:36 +0000 Subject: Add error message to the error report so we know why the AMQP server is unreachable --- nova/rpc.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/rpc.py b/nova/rpc.py index 089bf99c1..8fe4565dd 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -91,12 +91,13 @@ class Consumer(messaging.Consumer): super(Consumer, self).__init__(*args, **kwargs) self.failed_connection = False break - except: # Catching all because carrot sucks + except Exception as e: # Catching all because carrot sucks fl_host = FLAGS.rabbit_host fl_port = FLAGS.rabbit_port fl_intv = FLAGS.rabbit_retry_interval LOG.error(_("AMQP server on %(fl_host)s:%(fl_port)d is" - " unreachable. Trying again in %(fl_intv)d seconds.") + " unreachable: %(e)s. Trying again in %(fl_intv)d" + " seconds.") % locals()) self.failed_connection = True if self.failed_connection: -- cgit From 4c83130464c88650df7dfc7974f2fd088a733fec Mon Sep 17 00:00:00 2001 From: Christian Berendt <berendt@b1-systems.de> Date: Sat, 26 Feb 2011 17:26:38 +0100 Subject: introduced new flag "maximum_nbd_devices" to set the number of possible NBD devices --- nova/virt/disk.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index cb639a102..8789d8123 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -40,6 +40,8 @@ flags.DEFINE_integer('block_size', 1024 * 1024 * 256, 'block_size to use for dd') flags.DEFINE_integer('timeout_nbd', 10, 'time to wait for a NBD device coming up') +flags.DEFINE_integer('maximum_nbd_devices', 16, + 'maximum number of possible nbd devices') def extend(image, size): @@ -141,7 +143,7 @@ def _unlink_device(device, nbd): utils.execute('sudo losetup --detach %s' % device) -_DEVICES = ['/dev/nbd%s' % i for i in xrange(16)] +_DEVICES = ['/dev/nbd%s' % i for i in xrange(FLAGS.maximum_nbd_devices)] def _allocate_device(): -- cgit From 2a3ea106d3aaac3b97295c14742f23b250aa6874 Mon Sep 17 00:00:00 2001 From: Anne Gentle <anne@openstack.org> Date: Sat, 26 Feb 2011 11:13:32 -0600 Subject: Deleting test dir from a pull from trunk --- test/.Python | 1 - test/bin/activate | 76 -- test/bin/activate.csh | 32 - test/bin/activate.fish | 79 -- test/bin/activate_this.py | 32 - test/bin/easy_install | 9 - test/bin/easy_install-2.6 | 9 - test/bin/pip | 9 - test/bin/pip-2.6 | 9 - test/bin/python | Bin 50720 -> 0 bytes test/bin/python2.6 | 1 - test/include/python2.6 | 1 - test/lib/python2.6/UserDict.py | 1 - test/lib/python2.6/_abcoll.py | 1 - test/lib/python2.6/abc.py | 1 - test/lib/python2.6/codecs.py | 1 - test/lib/python2.6/config | 1 - test/lib/python2.6/copy_reg.py | 1 - test/lib/python2.6/distutils/__init__.py | 91 -- test/lib/python2.6/distutils/distutils.cfg | 6 - test/lib/python2.6/encodings | 1 - test/lib/python2.6/fnmatch.py | 1 - test/lib/python2.6/genericpath.py | 1 - test/lib/python2.6/lib-dynload | 1 - test/lib/python2.6/linecache.py | 1 - test/lib/python2.6/locale.py | 1 - test/lib/python2.6/ntpath.py | 1 - test/lib/python2.6/orig-prefix.txt | 1 - test/lib/python2.6/os.py | 1 - test/lib/python2.6/posixpath.py | 1 - test/lib/python2.6/re.py | 1 - test/lib/python2.6/site-packages/easy-install.pth | 4 - .../pip-0.8.1-py2.6.egg/EGG-INFO/PKG-INFO | 348 ----- .../pip-0.8.1-py2.6.egg/EGG-INFO/SOURCES.txt | 57 - .../EGG-INFO/dependency_links.txt | 1 - .../pip-0.8.1-py2.6.egg/EGG-INFO/entry_points.txt | 4 - .../pip-0.8.1-py2.6.egg/EGG-INFO/not-zip-safe | 1 - .../pip-0.8.1-py2.6.egg/EGG-INFO/top_level.txt | 1 - .../pip-0.8.1-py2.6.egg/pip/__init__.py | 261 ---- .../pip-0.8.1-py2.6.egg/pip/_pkgutil.py | 589 -------- .../pip-0.8.1-py2.6.egg/pip/backwardcompat.py | 55 - .../pip-0.8.1-py2.6.egg/pip/basecommand.py | 203 --- .../pip-0.8.1-py2.6.egg/pip/baseparser.py | 231 ---- .../pip-0.8.1-py2.6.egg/pip/commands/__init__.py | 1 - .../pip-0.8.1-py2.6.egg/pip/commands/bundle.py | 33 - .../pip-0.8.1-py2.6.egg/pip/commands/completion.py | 60 - .../pip-0.8.1-py2.6.egg/pip/commands/freeze.py | 109 -- .../pip-0.8.1-py2.6.egg/pip/commands/help.py | 32 - .../pip-0.8.1-py2.6.egg/pip/commands/install.py | 247 ---- .../pip-0.8.1-py2.6.egg/pip/commands/search.py | 116 -- .../pip-0.8.1-py2.6.egg/pip/commands/uninstall.py | 42 - .../pip-0.8.1-py2.6.egg/pip/commands/unzip.py | 9 - .../pip-0.8.1-py2.6.egg/pip/commands/zip.py | 346 ----- .../pip-0.8.1-py2.6.egg/pip/download.py | 470 ------- .../pip-0.8.1-py2.6.egg/pip/exceptions.py | 17 - .../site-packages/pip-0.8.1-py2.6.egg/pip/index.py | 686 ---------- .../pip-0.8.1-py2.6.egg/pip/locations.py | 45 - .../site-packages/pip-0.8.1-py2.6.egg/pip/log.py | 181 --- .../site-packages/pip-0.8.1-py2.6.egg/pip/req.py | 1432 -------------------- .../pip-0.8.1-py2.6.egg/pip/runner.py | 18 - .../site-packages/pip-0.8.1-py2.6.egg/pip/util.py | 479 ------- .../pip-0.8.1-py2.6.egg/pip/vcs/__init__.py | 238 ---- .../pip-0.8.1-py2.6.egg/pip/vcs/bazaar.py | 138 -- .../pip-0.8.1-py2.6.egg/pip/vcs/git.py | 204 --- .../pip-0.8.1-py2.6.egg/pip/vcs/mercurial.py | 162 --- .../pip-0.8.1-py2.6.egg/pip/vcs/subversion.py | 260 ---- .../site-packages/pip-0.8.1-py2.6.egg/pip/venv.py | 53 - .../site-packages/setuptools-0.6c11-py2.6.egg | Bin 333447 -> 0 bytes test/lib/python2.6/site-packages/setuptools.pth | 1 - test/lib/python2.6/site.py | 713 ---------- test/lib/python2.6/sre.py | 1 - test/lib/python2.6/sre_compile.py | 1 - test/lib/python2.6/sre_constants.py | 1 - test/lib/python2.6/sre_parse.py | 1 - test/lib/python2.6/stat.py | 1 - test/lib/python2.6/types.py | 1 - test/lib/python2.6/warnings.py | 1 - 77 files changed, 8226 deletions(-) delete mode 120000 test/.Python delete mode 100644 test/bin/activate delete mode 100644 test/bin/activate.csh delete mode 100644 test/bin/activate.fish delete mode 100644 test/bin/activate_this.py delete mode 100755 test/bin/easy_install delete mode 100755 test/bin/easy_install-2.6 delete mode 100755 test/bin/pip delete mode 100755 test/bin/pip-2.6 delete mode 100755 test/bin/python delete mode 120000 test/bin/python2.6 delete mode 120000 test/include/python2.6 delete mode 120000 test/lib/python2.6/UserDict.py delete mode 120000 test/lib/python2.6/_abcoll.py delete mode 120000 test/lib/python2.6/abc.py delete mode 120000 test/lib/python2.6/codecs.py delete mode 120000 test/lib/python2.6/config delete mode 120000 test/lib/python2.6/copy_reg.py delete mode 100644 test/lib/python2.6/distutils/__init__.py delete mode 100644 test/lib/python2.6/distutils/distutils.cfg delete mode 120000 test/lib/python2.6/encodings delete mode 120000 test/lib/python2.6/fnmatch.py delete mode 120000 test/lib/python2.6/genericpath.py delete mode 120000 test/lib/python2.6/lib-dynload delete mode 120000 test/lib/python2.6/linecache.py delete mode 120000 test/lib/python2.6/locale.py delete mode 120000 test/lib/python2.6/ntpath.py delete mode 100644 test/lib/python2.6/orig-prefix.txt delete mode 120000 test/lib/python2.6/os.py delete mode 120000 test/lib/python2.6/posixpath.py delete mode 120000 test/lib/python2.6/re.py delete mode 100644 test/lib/python2.6/site-packages/easy-install.pth delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/PKG-INFO delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/SOURCES.txt delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/dependency_links.txt delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/entry_points.txt delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/not-zip-safe delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/top_level.txt delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/__init__.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/_pkgutil.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/backwardcompat.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/basecommand.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/baseparser.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/__init__.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/bundle.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/completion.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/freeze.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/help.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/install.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/search.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/uninstall.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/unzip.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/zip.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/download.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/exceptions.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/index.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/locations.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/log.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/req.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/runner.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/util.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/__init__.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/bazaar.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/git.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/mercurial.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/subversion.py delete mode 100644 test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/venv.py delete mode 100644 test/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg delete mode 100644 test/lib/python2.6/site-packages/setuptools.pth delete mode 100644 test/lib/python2.6/site.py delete mode 120000 test/lib/python2.6/sre.py delete mode 120000 test/lib/python2.6/sre_compile.py delete mode 120000 test/lib/python2.6/sre_constants.py delete mode 120000 test/lib/python2.6/sre_parse.py delete mode 120000 test/lib/python2.6/stat.py delete mode 120000 test/lib/python2.6/types.py delete mode 120000 test/lib/python2.6/warnings.py diff --git a/test/.Python b/test/.Python deleted file mode 120000 index 6cce156bd..000000000 --- a/test/.Python +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/Python \ No newline at end of file diff --git a/test/bin/activate b/test/bin/activate deleted file mode 100644 index 588b54105..000000000 --- a/test/bin/activate +++ /dev/null @@ -1,76 +0,0 @@ -# This file must be used with "source bin/activate" *from bash* -# you cannot run it directly - -deactivate () { - # reset old environment variables - if [ -n "$_OLD_VIRTUAL_PATH" ] ; then - PATH="$_OLD_VIRTUAL_PATH" - export PATH - unset _OLD_VIRTUAL_PATH - fi - if [ -n "$_OLD_VIRTUAL_PYTHONHOME" ] ; then - PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" - export PYTHONHOME - unset _OLD_VIRTUAL_PYTHONHOME - fi - - # This should detect bash and zsh, which have a hash command that must - # be called to get it to forget past commands. Without forgetting - # past commands the $PATH changes we made may not be respected - if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then - hash -r - fi - - if [ -n "$_OLD_VIRTUAL_PS1" ] ; then - PS1="$_OLD_VIRTUAL_PS1" - export PS1 - unset _OLD_VIRTUAL_PS1 - fi - - unset VIRTUAL_ENV - if [ ! "$1" = "nondestructive" ] ; then - # Self destruct! - unset -f deactivate - fi -} - -# unset irrelavent variables -deactivate nondestructive - -VIRTUAL_ENV="/Users/anne.gentle/src/nova/docslice/test" -export VIRTUAL_ENV - -_OLD_VIRTUAL_PATH="$PATH" -PATH="$VIRTUAL_ENV/bin:$PATH" -export PATH - -# unset PYTHONHOME if set -# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) -# could use `if (set -u; : $PYTHONHOME) ;` in bash -if [ -n "$PYTHONHOME" ] ; then - _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" - unset PYTHONHOME -fi - -if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then - _OLD_VIRTUAL_PS1="$PS1" - if [ "x" != x ] ; then - PS1="$PS1" - else - if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then - # special case for Aspen magic directories - # see http://www.zetadev.com/software/aspen/ - PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" - else - PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" - fi - fi - export PS1 -fi - -# This should detect bash and zsh, which have a hash command that must -# be called to get it to forget past commands. Without forgetting -# past commands the $PATH changes we made may not be respected -if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then - hash -r -fi diff --git a/test/bin/activate.csh b/test/bin/activate.csh deleted file mode 100644 index d29ac339a..000000000 --- a/test/bin/activate.csh +++ /dev/null @@ -1,32 +0,0 @@ -# This file must be used with "source bin/activate.csh" *from csh*. -# You cannot run it directly. -# Created by Davide Di Blasi <davidedb@gmail.com>. - -alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' - -# Unset irrelavent variables. -deactivate nondestructive - -setenv VIRTUAL_ENV "/Users/anne.gentle/src/nova/docslice/test" - -set _OLD_VIRTUAL_PATH="$PATH" -setenv PATH "$VIRTUAL_ENV/bin:$PATH" - -set _OLD_VIRTUAL_PROMPT="$prompt" - -if ("" != "") then - set env_name = "" -else - if (`basename "$VIRTUAL_ENV"` == "__") then - # special case for Aspen magic directories - # see http://www.zetadev.com/software/aspen/ - set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` - else - set env_name = `basename "$VIRTUAL_ENV"` - endif -endif -set prompt = "[$env_name] $prompt" -unset env_name - -rehash - diff --git a/test/bin/activate.fish b/test/bin/activate.fish deleted file mode 100644 index de3a8901e..000000000 --- a/test/bin/activate.fish +++ /dev/null @@ -1,79 +0,0 @@ -# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org) -# you cannot run it directly - -function deactivate -d "Exit virtualenv and return to normal shell environment" - # reset old environment variables - if test -n "$_OLD_VIRTUAL_PATH" - set -gx PATH $_OLD_VIRTUAL_PATH - set -e _OLD_VIRTUAL_PATH - end - if test -n "$_OLD_VIRTUAL_PYTHONHOME" - set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME - set -e _OLD_VIRTUAL_PYTHONHOME - end - - if test -n "$_OLD_FISH_PROMPT_OVERRIDE" - functions -e fish_prompt - set -e _OLD_FISH_PROMPT_OVERRIDE - end - - set -e VIRTUAL_ENV - if test "$argv[1]" != "nondestructive" - # Self destruct! - functions -e deactivate - end -end - -# unset irrelavent variables -deactivate nondestructive - -set -gx VIRTUAL_ENV "/Users/anne.gentle/src/nova/docslice/test" - -set -gx _OLD_VIRTUAL_PATH $PATH -set -gx PATH "$VIRTUAL_ENV/bin" $PATH - -# unset PYTHONHOME if set -if set -q PYTHONHOME - set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME - set -e PYTHONHOME -end - -if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" - # fish shell uses a function, instead of env vars, - # to produce the prompt. Overriding the existing function is easy. - # However, adding to the current prompt, instead of clobbering it, - # is a little more work. - set -l oldpromptfile (tempfile) - if test $status - # save the current fish_prompt function... - echo "function _old_fish_prompt" >> $oldpromptfile - echo -n \# >> $oldpromptfile - functions fish_prompt >> $oldpromptfile - # we've made the "_old_fish_prompt" file, source it. - . $oldpromptfile - rm -f $oldpromptfile - - if test -n "" - # We've been given us a prompt override. - # - # FIXME: Unsure how to handle this *safely*. We could just eval() - # whatever is given, but the risk is a bit much. - echo "activate.fish: Alternative prompt prefix is not supported under fish-shell." 1>&2 - echo "activate.fish: Alter the fish_prompt in this file as needed." 1>&2 - end - - # with the original prompt function renamed, we can override with our own. - function fish_prompt - set -l _checkbase (basename "$VIRTUAL_ENV") - if test $_checkbase = "__" - # special case for Aspen magic directories - # see http://www.zetadev.com/software/aspen/ - printf "%s[%s]%s %s" (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) (_old_fish_prompt) - else - printf "%s(%s)%s%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) (_old_fish_prompt) - end - end - set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" - end -end - diff --git a/test/bin/activate_this.py b/test/bin/activate_this.py deleted file mode 100644 index aff6927d6..000000000 --- a/test/bin/activate_this.py +++ /dev/null @@ -1,32 +0,0 @@ -"""By using execfile(this_file, dict(__file__=this_file)) you will -activate this virtualenv environment. - -This can be used when you must use an existing Python interpreter, not -the virtualenv bin/python -""" - -try: - __file__ -except NameError: - raise AssertionError( - "You must run this like execfile('path/to/activate_this.py', dict(__file__='path/to/activate_this.py'))") -import sys -import os - -base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -if sys.platform == 'win32': - site_packages = os.path.join(base, 'Lib', 'site-packages') -else: - site_packages = os.path.join(base, 'lib', 'python%s' % sys.version[:3], 'site-packages') -prev_sys_path = list(sys.path) -import site -site.addsitedir(site_packages) -sys.real_prefix = sys.prefix -sys.prefix = base -# Move the added items to the front of the path: -new_sys_path = [] -for item in list(sys.path): - if item not in prev_sys_path: - new_sys_path.append(item) - sys.path.remove(item) -sys.path[:0] = new_sys_path diff --git a/test/bin/easy_install b/test/bin/easy_install deleted file mode 100755 index 8544573fc..000000000 --- a/test/bin/easy_install +++ /dev/null @@ -1,9 +0,0 @@ -#!/Users/anne.gentle/src/nova/docslice/test/bin/python -# EASY-INSTALL-ENTRY-SCRIPT: 'setuptools==0.6c11','console_scripts','easy_install' -__requires__ = 'setuptools==0.6c11' -import sys -from pkg_resources import load_entry_point - -sys.exit( - load_entry_point('setuptools==0.6c11', 'console_scripts', 'easy_install')() -) diff --git a/test/bin/easy_install-2.6 b/test/bin/easy_install-2.6 deleted file mode 100755 index bad01d2d7..000000000 --- a/test/bin/easy_install-2.6 +++ /dev/null @@ -1,9 +0,0 @@ -#!/Users/anne.gentle/src/nova/docslice/test/bin/python -# EASY-INSTALL-ENTRY-SCRIPT: 'setuptools==0.6c11','console_scripts','easy_install-2.6' -__requires__ = 'setuptools==0.6c11' -import sys -from pkg_resources import load_entry_point - -sys.exit( - load_entry_point('setuptools==0.6c11', 'console_scripts', 'easy_install-2.6')() -) diff --git a/test/bin/pip b/test/bin/pip deleted file mode 100755 index a3d2ed311..000000000 --- a/test/bin/pip +++ /dev/null @@ -1,9 +0,0 @@ -#!/Users/anne.gentle/src/nova/docslice/test/bin/python -# EASY-INSTALL-ENTRY-SCRIPT: 'pip==0.8.1','console_scripts','pip' -__requires__ = 'pip==0.8.1' -import sys -from pkg_resources import load_entry_point - -sys.exit( - load_entry_point('pip==0.8.1', 'console_scripts', 'pip')() -) diff --git a/test/bin/pip-2.6 b/test/bin/pip-2.6 deleted file mode 100755 index 4ad06add1..000000000 --- a/test/bin/pip-2.6 +++ /dev/null @@ -1,9 +0,0 @@ -#!/Users/anne.gentle/src/nova/docslice/test/bin/python -# EASY-INSTALL-ENTRY-SCRIPT: 'pip==0.8.1','console_scripts','pip-2.6' -__requires__ = 'pip==0.8.1' -import sys -from pkg_resources import load_entry_point - -sys.exit( - load_entry_point('pip==0.8.1', 'console_scripts', 'pip-2.6')() -) diff --git a/test/bin/python b/test/bin/python deleted file mode 100755 index 271b6ca30..000000000 Binary files a/test/bin/python and /dev/null differ diff --git a/test/bin/python2.6 b/test/bin/python2.6 deleted file mode 120000 index d8654aa0e..000000000 --- a/test/bin/python2.6 +++ /dev/null @@ -1 +0,0 @@ -python \ No newline at end of file diff --git a/test/include/python2.6 b/test/include/python2.6 deleted file mode 120000 index 788ac55ba..000000000 --- a/test/include/python2.6 +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/include/python2.6 \ No newline at end of file diff --git a/test/lib/python2.6/UserDict.py b/test/lib/python2.6/UserDict.py deleted file mode 120000 index 93e0864b1..000000000 --- a/test/lib/python2.6/UserDict.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/UserDict.py \ No newline at end of file diff --git a/test/lib/python2.6/_abcoll.py b/test/lib/python2.6/_abcoll.py deleted file mode 120000 index ff1769246..000000000 --- a/test/lib/python2.6/_abcoll.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/_abcoll.py \ No newline at end of file diff --git a/test/lib/python2.6/abc.py b/test/lib/python2.6/abc.py deleted file mode 120000 index 79fa108b2..000000000 --- a/test/lib/python2.6/abc.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/abc.py \ No newline at end of file diff --git a/test/lib/python2.6/codecs.py b/test/lib/python2.6/codecs.py deleted file mode 120000 index 9e4bfb7fa..000000000 --- a/test/lib/python2.6/codecs.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/codecs.py \ No newline at end of file diff --git a/test/lib/python2.6/config b/test/lib/python2.6/config deleted file mode 120000 index eef498c62..000000000 --- a/test/lib/python2.6/config +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/config \ No newline at end of file diff --git a/test/lib/python2.6/copy_reg.py b/test/lib/python2.6/copy_reg.py deleted file mode 120000 index 7285fda0e..000000000 --- a/test/lib/python2.6/copy_reg.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/copy_reg.py \ No newline at end of file diff --git a/test/lib/python2.6/distutils/__init__.py b/test/lib/python2.6/distutils/__init__.py deleted file mode 100644 index 7ebb41c04..000000000 --- a/test/lib/python2.6/distutils/__init__.py +++ /dev/null @@ -1,91 +0,0 @@ -import os -import sys -import warnings -import opcode # opcode is not a virtualenv module, so we can use it to find the stdlib - # Important! To work on pypy, this must be a module that resides in the - # lib-python/modified-x.y.z directory - -dirname = os.path.dirname - -distutils_path = os.path.join(os.path.dirname(opcode.__file__), 'distutils') -if os.path.normpath(distutils_path) == os.path.dirname(os.path.normpath(__file__)): - warnings.warn( - "The virtualenv distutils package at %s appears to be in the same location as the system distutils?") -else: - __path__.insert(0, distutils_path) - exec open(os.path.join(distutils_path, '__init__.py')).read() - -import dist -import sysconfig - - -## patch build_ext (distutils doesn't know how to get the libs directory -## path on windows - it hardcodes the paths around the patched sys.prefix) - -if sys.platform == 'win32': - from distutils.command.build_ext import build_ext as old_build_ext - class build_ext(old_build_ext): - def finalize_options (self): - if self.library_dirs is None: - self.library_dirs = [] - elif isinstance(self.library_dirs, basestring): - self.library_dirs = self.library_dirs.split(os.pathsep) - - self.library_dirs.insert(0, os.path.join(sys.real_prefix, "Libs")) - old_build_ext.finalize_options(self) - - from distutils.command import build_ext as build_ext_module - build_ext_module.build_ext = build_ext - -## distutils.dist patches: - -old_find_config_files = dist.Distribution.find_config_files -def find_config_files(self): - found = old_find_config_files(self) - system_distutils = os.path.join(distutils_path, 'distutils.cfg') - #if os.path.exists(system_distutils): - # found.insert(0, system_distutils) - # What to call the per-user config file - if os.name == 'posix': - user_filename = ".pydistutils.cfg" - else: - user_filename = "pydistutils.cfg" - user_filename = os.path.join(sys.prefix, user_filename) - if os.path.isfile(user_filename): - for item in list(found): - if item.endswith('pydistutils.cfg'): - found.remove(item) - found.append(user_filename) - return found -dist.Distribution.find_config_files = find_config_files - -## distutils.sysconfig patches: - -old_get_python_inc = sysconfig.get_python_inc -def sysconfig_get_python_inc(plat_specific=0, prefix=None): - if prefix is None: - prefix = sys.real_prefix - return old_get_python_inc(plat_specific, prefix) -sysconfig_get_python_inc.__doc__ = old_get_python_inc.__doc__ -sysconfig.get_python_inc = sysconfig_get_python_inc - -old_get_python_lib = sysconfig.get_python_lib -def sysconfig_get_python_lib(plat_specific=0, standard_lib=0, prefix=None): - if standard_lib and prefix is None: - prefix = sys.real_prefix - return old_get_python_lib(plat_specific, standard_lib, prefix) -sysconfig_get_python_lib.__doc__ = old_get_python_lib.__doc__ -sysconfig.get_python_lib = sysconfig_get_python_lib - -old_get_config_vars = sysconfig.get_config_vars -def sysconfig_get_config_vars(*args): - real_vars = old_get_config_vars(*args) - if sys.platform == 'win32': - lib_dir = os.path.join(sys.real_prefix, "libs") - if isinstance(real_vars, dict) and 'LIBDIR' not in real_vars: - real_vars['LIBDIR'] = lib_dir # asked for all - elif isinstance(real_vars, list) and 'LIBDIR' in args: - real_vars = real_vars + [lib_dir] # asked for list - return real_vars -sysconfig_get_config_vars.__doc__ = old_get_config_vars.__doc__ -sysconfig.get_config_vars = sysconfig_get_config_vars diff --git a/test/lib/python2.6/distutils/distutils.cfg b/test/lib/python2.6/distutils/distutils.cfg deleted file mode 100644 index 1af230ec9..000000000 --- a/test/lib/python2.6/distutils/distutils.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# This is a config file local to this virtualenv installation -# You may include options that will be used by all distutils commands, -# and by easy_install. For instance: -# -# [easy_install] -# find_links = http://mylocalsite diff --git a/test/lib/python2.6/encodings b/test/lib/python2.6/encodings deleted file mode 120000 index 89f28f824..000000000 --- a/test/lib/python2.6/encodings +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/encodings \ No newline at end of file diff --git a/test/lib/python2.6/fnmatch.py b/test/lib/python2.6/fnmatch.py deleted file mode 120000 index cce0594f2..000000000 --- a/test/lib/python2.6/fnmatch.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/fnmatch.py \ No newline at end of file diff --git a/test/lib/python2.6/genericpath.py b/test/lib/python2.6/genericpath.py deleted file mode 120000 index b14e1bc26..000000000 --- a/test/lib/python2.6/genericpath.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/genericpath.py \ No newline at end of file diff --git a/test/lib/python2.6/lib-dynload b/test/lib/python2.6/lib-dynload deleted file mode 120000 index 4644b7072..000000000 --- a/test/lib/python2.6/lib-dynload +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-dynload \ No newline at end of file diff --git a/test/lib/python2.6/linecache.py b/test/lib/python2.6/linecache.py deleted file mode 120000 index 783624da8..000000000 --- a/test/lib/python2.6/linecache.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/linecache.py \ No newline at end of file diff --git a/test/lib/python2.6/locale.py b/test/lib/python2.6/locale.py deleted file mode 120000 index 4e674c7b6..000000000 --- a/test/lib/python2.6/locale.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/locale.py \ No newline at end of file diff --git a/test/lib/python2.6/ntpath.py b/test/lib/python2.6/ntpath.py deleted file mode 120000 index 9b6b40f48..000000000 --- a/test/lib/python2.6/ntpath.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ntpath.py \ No newline at end of file diff --git a/test/lib/python2.6/orig-prefix.txt b/test/lib/python2.6/orig-prefix.txt deleted file mode 100644 index 535eb0f0e..000000000 --- a/test/lib/python2.6/orig-prefix.txt +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6 \ No newline at end of file diff --git a/test/lib/python2.6/os.py b/test/lib/python2.6/os.py deleted file mode 120000 index 92e6e9a7c..000000000 --- a/test/lib/python2.6/os.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/os.py \ No newline at end of file diff --git a/test/lib/python2.6/posixpath.py b/test/lib/python2.6/posixpath.py deleted file mode 120000 index c095d16a1..000000000 --- a/test/lib/python2.6/posixpath.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/posixpath.py \ No newline at end of file diff --git a/test/lib/python2.6/re.py b/test/lib/python2.6/re.py deleted file mode 120000 index b4710c5f7..000000000 --- a/test/lib/python2.6/re.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/re.py \ No newline at end of file diff --git a/test/lib/python2.6/site-packages/easy-install.pth b/test/lib/python2.6/site-packages/easy-install.pth deleted file mode 100644 index 7a6ae2b6d..000000000 --- a/test/lib/python2.6/site-packages/easy-install.pth +++ /dev/null @@ -1,4 +0,0 @@ -import sys; sys.__plen = len(sys.path) -./setuptools-0.6c11-py2.6.egg -./pip-0.8.1-py2.6.egg -import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/PKG-INFO b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/PKG-INFO deleted file mode 100644 index 29c30cb8c..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/PKG-INFO +++ /dev/null @@ -1,348 +0,0 @@ -Metadata-Version: 1.0 -Name: pip -Version: 0.8.1 -Summary: pip installs packages. Python packages. An easy_install replacement -Home-page: http://pip.openplans.org -Author: Ian Bicking -Author-email: python-virtualenv@groups.google.com -License: MIT -Description: The main website for pip is `pip.openplans.org - <http://pip.openplans.org>`_. You can also install - the `in-development version <http://bitbucket.org/ianb/pip/get/tip.gz#egg=pip-dev>`_ - of pip with ``easy_install pip==dev``. - - - Introduction - ------------ - - pip installs packages. Python packages. - - If you use `virtualenv <http://virtualenv.openplans.org>`__ -- a tool - for installing libraries in a local and isolated manner -- you'll - automatically get a copy of pip. Free bonus! - - Once you have pip, you can use it like this:: - - $ pip install SomePackage - - SomePackage is some package you'll find on `PyPI - <http://pypi.python.org/pypi/>`_. This installs the package and all - its dependencies. - - pip does other stuff too, with packages, but install is the biggest - one. You can ``pip uninstall`` too. - - You can also install from a URL (that points to a tar or zip file), - install from some version control system (use URLs like - ``hg+http://domain/repo`` -- or prefix ``git+``, ``svn+`` etc). pip - knows a bunch of stuff about revisions and stuff, so if you need to do - things like install a very specific revision from a repository pip can - do that too. - - If you've ever used ``python setup.py develop``, you can do something - like that with ``pip install -e ./`` -- this works with packages that - use ``distutils`` too (usually this only works with Setuptools - projects). - - You can use ``pip install --upgrade SomePackage`` to upgrade to a - newer version, or ``pip install SomePackage==1.0.4`` to install a very - specific version. - - Pip Compared To easy_install - ---------------------------- - - pip is a replacement for `easy_install - <http://peak.telecommunity.com/DevCenter/EasyInstall>`_. It uses mostly the - same techniques for finding packages, so packages that were made - easy_installable should be pip-installable as well. - - pip is meant to improve on easy_install. Some of the improvements: - - * All packages are downloaded before installation. Partially-completed - installation doesn't occur as a result. - - * Care is taken to present useful output on the console. - - * The reasons for actions are kept track of. For instance, if a package is - being installed, pip keeps track of why that package was required. - - * Error messages should be useful. - - * The code is relatively concise and cohesive, making it easier to use - programmatically. - - * Packages don't have to be installed as egg archives, they can be installed - flat (while keeping the egg metadata). - - * Native support for other version control systems (Git, Mercurial and Bazaar) - - * Uninstallation of packages. - - * Simple to define fixed sets of requirements and reliably reproduce a - set of packages. - - pip doesn't do everything that easy_install does. Specifically: - - * It cannot install from eggs. It only installs from source. (In the - future it would be good if it could install binaries from Windows ``.exe`` - or ``.msi`` -- binary install on other platforms is not a priority.) - - * It doesn't understand Setuptools extras (like ``package[test]``). This should - be added eventually. - - * It is incompatible with some packages that extensively customize distutils - or setuptools in their ``setup.py`` files. - - pip is complementary with `virtualenv - <http://pypi.python.org/pypi/virtualenv>`__, and it is encouraged that you use - virtualenv to isolate your installation. - - Community - --------- - - The homepage for pip is temporarily located `on PyPI - <http://pypi.python.org/pypi/pip>`_ -- a more proper homepage will - follow. Bugs can go on the `pip issue tracker - <http://bitbucket.org/ianb/pip/issues/>`_. Discussion should happen on the - `virtualenv email group - <http://groups.google.com/group/python-virtualenv?hl=en>`_. - - Uninstall - --------- - - pip is able to uninstall most installed packages with ``pip uninstall - package-name``. - - Known exceptions include pure-distutils packages installed with - ``python setup.py install`` (such packages leave behind no metadata allowing - determination of what files were installed), and script wrappers installed - by develop-installs (``python setup.py develop``). - - pip also performs an automatic uninstall of an old version of a package - before upgrading to a newer version, so outdated files (and egg-info data) - from conflicting versions aren't left hanging around to cause trouble. The - old version of the package is automatically restored if the new version - fails to download or install. - - .. _`requirements file`: - - Requirements Files - ------------------ - - When installing software, and Python packages in particular, it's common that - you get a lot of libraries installed. You just did ``easy_install MyPackage`` - and you get a dozen packages. Each of these packages has its own version. - - Maybe you ran that installation and it works. Great! Will it keep working? - Did you have to provide special options to get it to find everything? Did you - have to install a bunch of other optional pieces? Most of all, will you be able - to do it again? Requirements files give you a way to create an *environment*: - a *set* of packages that work together. - - If you've ever tried to setup an application on a new system, or with slightly - updated pieces, and had it fail, pip requirements are for you. If you - haven't had this problem then you will eventually, so pip requirements are - for you too -- requirements make explicit, repeatable installation of packages. - - So what are requirements files? They are very simple: lists of packages to - install. Instead of running something like ``pip MyApp`` and getting - whatever libraries come along, you can create a requirements file something like:: - - MyApp - Framework==0.9.4 - Library>=0.2 - - Then, regardless of what MyApp lists in ``setup.py``, you'll get a - specific version of Framework (0.9.4) and at least the 0.2 version of - Library. (You might think you could list these specific versions in - MyApp's ``setup.py`` -- but if you do that you'll have to edit MyApp - if you want to try a new version of Framework, or release a new - version of MyApp if you determine that Library 0.3 doesn't work with - your application.) You can also add optional libraries and support - tools that MyApp doesn't strictly require, giving people a set of - recommended libraries. - - You can also include "editable" packages -- packages that are checked out from - Subversion, Git, Mercurial and Bazaar. These are just like using the ``-e`` - option to pip. They look like:: - - -e svn+http://myrepo/svn/MyApp#egg=MyApp - - You have to start the URL with ``svn+`` (``git+``, ``hg+`` or ``bzr+``), and - you have to include ``#egg=Package`` so pip knows what to expect at that URL. - You can also include ``@rev`` in the URL, e.g., ``@275`` to check out - revision 275. - - Requirement files are mostly *flat*. Maybe ``MyApp`` requires - ``Framework``, and ``Framework`` requires ``Library``. I encourage - you to still list all these in a single requirement file; it is the - nature of Python programs that there are implicit bindings *directly* - between MyApp and Library. For instance, Framework might expose one - of Library's objects, and so if Library is updated it might directly - break MyApp. If that happens you can update the requirements file to - force an earlier version of Library, and you can do that without - having to re-release MyApp at all. - - Read the `requirements file format <http://pip.openplans.org/requirement-format.html>`_ to - learn about other features. - - Freezing Requirements - --------------------- - - So you have a working set of packages, and you want to be able to install them - elsewhere. `Requirements files`_ let you install exact versions, but it won't - tell you what all the exact versions are. - - To create a new requirements file from a known working environment, use:: - - $ pip freeze > stable-req.txt - - This will write a listing of *all* installed libraries to ``stable-req.txt`` - with exact versions for every library. You may want to edit the file down after - generating (e.g., to eliminate unnecessary libraries), but it'll give you a - stable starting point for constructing your requirements file. - - You can also give it an existing requirements file, and it will use that as a - sort of template for the new file. So if you do:: - - $ pip freeze -r devel-req.txt > stable-req.txt - - it will keep the packages listed in ``devel-req.txt`` in order and preserve - comments. - - Bundles - ------- - - Another way to distribute a set of libraries is a bundle format (specific to - pip). This format is not stable at this time (there simply hasn't been - any feedback, nor a great deal of thought). A bundle file contains all the - source for your package, and you can have pip install them all together. - Once you have the bundle file further network access won't be necessary. To - build a bundle file, do:: - - $ pip bundle MyApp.pybundle MyApp - - (Using a `requirements file`_ would be wise.) Then someone else can get the - file ``MyApp.pybundle`` and run:: - - $ pip install MyApp.pybundle - - This is *not* a binary format. This only packages source. If you have binary - packages, then the person who installs the files will have to have a compiler, - any necessary headers installed, etc. Binary packages are hard, this is - relatively easy. - - Using pip with virtualenv - ------------------------- - - pip is most nutritious when used with `virtualenv - <http://pypi.python.org/pypi/virtualenv>`__. One of the reasons pip - doesn't install "multi-version" eggs is that virtualenv removes much of the need - for it. Because pip is installed by virtualenv, just use - ``path/to/my/environment/bin/pip`` to install things into that - specific environment. - - To tell pip to only run if there is a virtualenv currently activated, - and to bail if not, use:: - - export PIP_REQUIRE_VIRTUALENV=true - - To tell pip to automatically use the currently active virtualenv:: - - export PIP_RESPECT_VIRTUALENV=true - - Providing an environment with ``-E`` will be ignored. - - Using pip with virtualenvwrapper - --------------------------------- - - If you are using `virtualenvwrapper - <http://www.doughellmann.com/projects/virtualenvwrapper/>`_, you might - want pip to automatically create its virtualenvs in your - ``$WORKON_HOME``. - - You can tell pip to do so by defining ``PIP_VIRTUALENV_BASE`` in your - environment and setting it to the same value as that of - ``$WORKON_HOME``. - - Do so by adding the line:: - - export PIP_VIRTUALENV_BASE=$WORKON_HOME - - in your .bashrc under the line starting with ``export WORKON_HOME``. - - Using pip with buildout - ----------------------- - - If you are using `zc.buildout - <http://pypi.python.org/pypi/zc.buildout>`_ you should look at - `gp.recipe.pip <http://pypi.python.org/pypi/gp.recipe.pip>`_ as an - option to use pip and virtualenv in your buildouts. - - Command line completion - ----------------------- - - pip comes with support for command line completion in bash and zsh and - allows you tab complete commands and options. To enable it you simply - need copy the required shell script to the your shell startup file - (e.g. ``.profile`` or ``.zprofile``) by running the special ``completion`` - command, e.g. for bash:: - - $ pip completion --bash >> ~/.profile - - And for zsh:: - - $ pip completion --zsh >> ~/.zprofile - - Alternatively, you can use the result of the ``completion`` command - directly with the eval function of you shell, e.g. by adding:: - - eval "`pip completion --bash`" - - to your startup file. - - Searching for packages - ---------------------- - - pip can search the `Python Package Index <http://pypi.python.org/pypi>`_ (PyPI) - for packages using the ``pip search`` command. To search, run:: - - $ pip search "query" - - The query will be used to search the names and summaries of all packages - indexed. - - pip searches http://pypi.python.org/pypi by default but alternative indexes - can be searched by using the ``--index`` flag. - - Mirror support - -------------- - - The `PyPI mirroring infrastructure <http://pypi.python.org/mirrors>`_ as - described in `PEP 381 <http://www.python.org/dev/peps/pep-0381/>`_ can be - used by passing the ``--use-mirrors`` option to the install command. - Alternatively, you can use the other ways to configure pip, e.g.:: - - $ export PIP_USE_MIRRORS=true - - If enabled, pip will automatically query the DNS entry of the mirror index URL - to find the list of mirrors to use. In case you want to override this list, - please use the ``--mirrors`` option of the install command, or add to your pip - configuration file:: - - [install] - use-mirrors = true - mirrors = - http://d.pypi.python.org - http://b.pypi.python.org - -Keywords: easy_install distutils setuptools egg virtualenv -Platform: UNKNOWN -Classifier: Development Status :: 4 - Beta -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Topic :: Software Development :: Build Tools -Classifier: Programming Language :: Python :: 2.4 -Classifier: Programming Language :: Python :: 2.5 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/SOURCES.txt b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/SOURCES.txt deleted file mode 100644 index 3a068547e..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/SOURCES.txt +++ /dev/null @@ -1,57 +0,0 @@ -MANIFEST.in -setup.cfg -setup.py -docs/branches.txt -docs/ci-server-step-by-step.txt -docs/configuration.txt -docs/how-to-contribute.txt -docs/index.txt -docs/license.txt -docs/news.txt -docs/requirement-format.txt -docs/running-tests.txt -docs/_build/branches.html -docs/_build/ci-server-step-by-step.html -docs/_build/configuration.html -docs/_build/how-to-contribute.html -docs/_build/index.html -docs/_build/license.html -docs/_build/news.html -docs/_build/requirement-format.html -docs/_build/running-tests.html -docs/_build/search.html -pip/__init__.py -pip/_pkgutil.py -pip/backwardcompat.py -pip/basecommand.py -pip/baseparser.py -pip/download.py -pip/exceptions.py -pip/index.py -pip/locations.py -pip/log.py -pip/req.py -pip/runner.py -pip/util.py -pip/venv.py -pip.egg-info/PKG-INFO -pip.egg-info/SOURCES.txt -pip.egg-info/dependency_links.txt -pip.egg-info/entry_points.txt -pip.egg-info/not-zip-safe -pip.egg-info/top_level.txt -pip/commands/__init__.py -pip/commands/bundle.py -pip/commands/completion.py -pip/commands/freeze.py -pip/commands/help.py -pip/commands/install.py -pip/commands/search.py -pip/commands/uninstall.py -pip/commands/unzip.py -pip/commands/zip.py -pip/vcs/__init__.py -pip/vcs/bazaar.py -pip/vcs/git.py -pip/vcs/mercurial.py -pip/vcs/subversion.py \ No newline at end of file diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/dependency_links.txt b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/dependency_links.txt deleted file mode 100644 index 8b1378917..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/entry_points.txt b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/entry_points.txt deleted file mode 100644 index 2b0afba73..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/entry_points.txt +++ /dev/null @@ -1,4 +0,0 @@ -[console_scripts] -pip = pip:main -pip-2.6 = pip:main - diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/not-zip-safe b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/not-zip-safe deleted file mode 100644 index 8b1378917..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/not-zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/top_level.txt b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/top_level.txt deleted file mode 100644 index a1b589e38..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/EGG-INFO/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/__init__.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/__init__.py deleted file mode 100644 index c5de5c9a8..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/__init__.py +++ /dev/null @@ -1,261 +0,0 @@ -#!/usr/bin/env python -import os -import optparse - -import subprocess -import sys -import re -import difflib - -from pip.basecommand import command_dict, load_command, load_all_commands, command_names -from pip.baseparser import parser -from pip.exceptions import InstallationError -from pip.log import logger -from pip.util import get_installed_distributions -from pip.backwardcompat import walk_packages - - -def autocomplete(): - """Command and option completion for the main option parser (and options) - and its subcommands (and options). - - Enable by sourcing one of the completion shell scripts (bash or zsh). - """ - # Don't complete if user hasn't sourced bash_completion file. - if 'PIP_AUTO_COMPLETE' not in os.environ: - return - cwords = os.environ['COMP_WORDS'].split()[1:] - cword = int(os.environ['COMP_CWORD']) - try: - current = cwords[cword-1] - except IndexError: - current = '' - load_all_commands() - subcommands = [cmd for cmd, cls in command_dict.items() if not cls.hidden] - options = [] - # subcommand - try: - subcommand_name = [w for w in cwords if w in subcommands][0] - except IndexError: - subcommand_name = None - # subcommand options - if subcommand_name: - # special case: 'help' subcommand has no options - if subcommand_name == 'help': - sys.exit(1) - # special case: list locally installed dists for uninstall command - if subcommand_name == 'uninstall' and not current.startswith('-'): - installed = [] - lc = current.lower() - for dist in get_installed_distributions(local_only=True): - if dist.key.startswith(lc) and dist.key not in cwords[1:]: - installed.append(dist.key) - # if there are no dists installed, fall back to option completion - if installed: - for dist in installed: - print dist - sys.exit(1) - subcommand = command_dict.get(subcommand_name) - options += [(opt.get_opt_string(), opt.nargs) - for opt in subcommand.parser.option_list - if opt.help != optparse.SUPPRESS_HELP] - # filter out previously specified options from available options - prev_opts = [x.split('=')[0] for x in cwords[1:cword-1]] - options = filter(lambda (x, v): x not in prev_opts, options) - # filter options by current input - options = [(k, v) for k, v in options if k.startswith(current)] - for option in options: - opt_label = option[0] - # append '=' to options which require args - if option[1]: - opt_label += '=' - print opt_label - else: - # show options of main parser only when necessary - if current.startswith('-') or current.startswith('--'): - subcommands += [opt.get_opt_string() - for opt in parser.option_list - if opt.help != optparse.SUPPRESS_HELP] - print ' '.join(filter(lambda x: x.startswith(current), subcommands)) - sys.exit(1) - - -def version_control(): - # Import all the version control support modules: - from pip import vcs - for importer, modname, ispkg in \ - walk_packages(path=vcs.__path__, prefix=vcs.__name__+'.'): - __import__(modname) - - -def main(initial_args=None): - if initial_args is None: - initial_args = sys.argv[1:] - autocomplete() - version_control() - options, args = parser.parse_args(initial_args) - if options.help and not args: - args = ['help'] - if not args: - parser.error('You must give a command (use "pip help" to see a list of commands)') - command = args[0].lower() - load_command(command) - if command not in command_dict: - close_commands = difflib.get_close_matches(command, command_names()) - if close_commands: - guess = close_commands[0] - if args[1:]: - guess = "%s %s" % (guess, " ".join(args[1:])) - else: - guess = 'install %s' % command - error_dict = {'arg': command, 'guess': guess, - 'script': os.path.basename(sys.argv[0])} - parser.error('No command by the name %(script)s %(arg)s\n ' - '(maybe you meant "%(script)s %(guess)s")' % error_dict) - command = command_dict[command] - return command.main(initial_args, args[1:], options) - - -############################################################ -## Writing freeze files - - -class FrozenRequirement(object): - - def __init__(self, name, req, editable, comments=()): - self.name = name - self.req = req - self.editable = editable - self.comments = comments - - _rev_re = re.compile(r'-r(\d+)$') - _date_re = re.compile(r'-(20\d\d\d\d\d\d)$') - - @classmethod - def from_dist(cls, dist, dependency_links, find_tags=False): - location = os.path.normcase(os.path.abspath(dist.location)) - comments = [] - from pip.vcs import vcs, get_src_requirement - if vcs.get_backend_name(location): - editable = True - req = get_src_requirement(dist, location, find_tags) - if req is None: - logger.warn('Could not determine repository location of %s' % location) - comments.append('## !! Could not determine repository location') - req = dist.as_requirement() - editable = False - else: - editable = False - req = dist.as_requirement() - specs = req.specs - assert len(specs) == 1 and specs[0][0] == '==' - version = specs[0][1] - ver_match = cls._rev_re.search(version) - date_match = cls._date_re.search(version) - if ver_match or date_match: - svn_backend = vcs.get_backend('svn') - if svn_backend: - svn_location = svn_backend( - ).get_location(dist, dependency_links) - if not svn_location: - logger.warn( - 'Warning: cannot find svn location for %s' % req) - comments.append('## FIXME: could not find svn URL in dependency_links for this package:') - else: - comments.append('# Installing as editable to satisfy requirement %s:' % req) - if ver_match: - rev = ver_match.group(1) - else: - rev = '{%s}' % date_match.group(1) - editable = True - req = '%s@%s#egg=%s' % (svn_location, rev, cls.egg_name(dist)) - return cls(dist.project_name, req, editable, comments) - - @staticmethod - def egg_name(dist): - name = dist.egg_name() - match = re.search(r'-py\d\.\d$', name) - if match: - name = name[:match.start()] - return name - - def __str__(self): - req = self.req - if self.editable: - req = '-e %s' % req - return '\n'.join(list(self.comments)+[str(req)])+'\n' - -############################################################ -## Requirement files - - -def call_subprocess(cmd, show_stdout=True, - filter_stdout=None, cwd=None, - raise_on_returncode=True, - command_level=logger.DEBUG, command_desc=None, - extra_environ=None): - if command_desc is None: - cmd_parts = [] - for part in cmd: - if ' ' in part or '\n' in part or '"' in part or "'" in part: - part = '"%s"' % part.replace('"', '\\"') - cmd_parts.append(part) - command_desc = ' '.join(cmd_parts) - if show_stdout: - stdout = None - else: - stdout = subprocess.PIPE - logger.log(command_level, "Running command %s" % command_desc) - env = os.environ.copy() - if extra_environ: - env.update(extra_environ) - try: - proc = subprocess.Popen( - cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, - cwd=cwd, env=env) - except Exception, e: - logger.fatal( - "Error %s while executing command %s" % (e, command_desc)) - raise - all_output = [] - if stdout is not None: - stdout = proc.stdout - while 1: - line = stdout.readline() - if not line: - break - line = line.rstrip() - all_output.append(line + '\n') - if filter_stdout: - level = filter_stdout(line) - if isinstance(level, tuple): - level, line = level - logger.log(level, line) - if not logger.stdout_level_matches(level): - logger.show_progress() - else: - logger.info(line) - else: - returned_stdout, returned_stderr = proc.communicate() - all_output = [returned_stdout or ''] - proc.wait() - if proc.returncode: - if raise_on_returncode: - if all_output: - logger.notify('Complete output from command %s:' % command_desc) - logger.notify('\n'.join(all_output) + '\n----------------------------------------') - raise InstallationError( - "Command %s failed with error code %s" - % (command_desc, proc.returncode)) - else: - logger.warn( - "Command %s had error code %s" - % (command_desc, proc.returncode)) - if stdout is not None: - return ''.join(all_output) - - -if __name__ == '__main__': - exit = main() - if exit: - sys.exit(exit) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/_pkgutil.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/_pkgutil.py deleted file mode 100644 index f8fb8aa61..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/_pkgutil.py +++ /dev/null @@ -1,589 +0,0 @@ -"""Utilities to support packages.""" - -# NOTE: This module must remain compatible with Python 2.3, as it is shared -# by setuptools for distribution with Python 2.3 and up. - -import os -import sys -import imp -import os.path -from types import ModuleType - -__all__ = [ - 'get_importer', 'iter_importers', 'get_loader', 'find_loader', - 'walk_packages', 'iter_modules', - 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path', -] - - -def read_code(stream): - # This helper is needed in order for the PEP 302 emulation to - # correctly handle compiled files - import marshal - - magic = stream.read(4) - if magic != imp.get_magic(): - return None - - stream.read(4) # Skip timestamp - return marshal.load(stream) - - -def simplegeneric(func): - """Make a trivial single-dispatch generic function""" - registry = {} - - def wrapper(*args, **kw): - ob = args[0] - try: - cls = ob.__class__ - except AttributeError: - cls = type(ob) - try: - mro = cls.__mro__ - except AttributeError: - try: - - class cls(cls, object): - pass - - mro = cls.__mro__[1:] - except TypeError: - mro = object, # must be an ExtensionClass or some such :( - for t in mro: - if t in registry: - return registry[t](*args, **kw) - else: - return func(*args, **kw) - try: - wrapper.__name__ = func.__name__ - except (TypeError, AttributeError): - pass # Python 2.3 doesn't allow functions to be renamed - - def register(typ, func=None): - if func is None: - return lambda f: register(typ, f) - registry[typ] = func - return func - - wrapper.__dict__ = func.__dict__ - wrapper.__doc__ = func.__doc__ - wrapper.register = register - return wrapper - - -def walk_packages(path=None, prefix='', onerror=None): - """Yields (module_loader, name, ispkg) for all modules recursively - on path, or, if path is None, all accessible modules. - - 'path' should be either None or a list of paths to look for - modules in. - - 'prefix' is a string to output on the front of every module name - on output. - - Note that this function must import all *packages* (NOT all - modules!) on the given path, in order to access the __path__ - attribute to find submodules. - - 'onerror' is a function which gets called with one argument (the - name of the package which was being imported) if any exception - occurs while trying to import a package. If no onerror function is - supplied, ImportErrors are caught and ignored, while all other - exceptions are propagated, terminating the search. - - Examples: - - # list all modules python can access - walk_packages() - - # list all submodules of ctypes - walk_packages(ctypes.__path__, ctypes.__name__+'.') - """ - - def seen(p, m={}): - if p in m: - return True - m[p] = True - - for importer, name, ispkg in iter_modules(path, prefix): - yield importer, name, ispkg - - if ispkg: - try: - __import__(name) - except ImportError: - if onerror is not None: - onerror(name) - except Exception: - if onerror is not None: - onerror(name) - else: - raise - else: - path = getattr(sys.modules[name], '__path__', None) or [] - - # don't traverse path items we've seen before - path = [p for p in path if not seen(p)] - - for item in walk_packages(path, name+'.', onerror): - yield item - - -def iter_modules(path=None, prefix=''): - """Yields (module_loader, name, ispkg) for all submodules on path, - or, if path is None, all top-level modules on sys.path. - - 'path' should be either None or a list of paths to look for - modules in. - - 'prefix' is a string to output on the front of every module name - on output. - """ - - if path is None: - importers = iter_importers() - else: - importers = map(get_importer, path) - - yielded = {} - for i in importers: - for name, ispkg in iter_importer_modules(i, prefix): - if name not in yielded: - yielded[name] = 1 - yield i, name, ispkg - - -#@simplegeneric -def iter_importer_modules(importer, prefix=''): - if not hasattr(importer, 'iter_modules'): - return [] - return importer.iter_modules(prefix) - -iter_importer_modules = simplegeneric(iter_importer_modules) - - -class ImpImporter: - """PEP 302 Importer that wraps Python's "classic" import algorithm - - ImpImporter(dirname) produces a PEP 302 importer that searches that - directory. ImpImporter(None) produces a PEP 302 importer that searches - the current sys.path, plus any modules that are frozen or built-in. - - Note that ImpImporter does not currently support being used by placement - on sys.meta_path. - """ - - def __init__(self, path=None): - self.path = path - - def find_module(self, fullname, path=None): - # Note: we ignore 'path' argument since it is only used via meta_path - subname = fullname.split(".")[-1] - if subname != fullname and self.path is None: - return None - if self.path is None: - path = None - else: - path = [os.path.realpath(self.path)] - try: - file, filename, etc = imp.find_module(subname, path) - except ImportError: - return None - return ImpLoader(fullname, file, filename, etc) - - def iter_modules(self, prefix=''): - if self.path is None or not os.path.isdir(self.path): - return - - yielded = {} - import inspect - - filenames = os.listdir(self.path) - filenames.sort() # handle packages before same-named modules - - for fn in filenames: - modname = inspect.getmodulename(fn) - if modname=='__init__' or modname in yielded: - continue - - path = os.path.join(self.path, fn) - ispkg = False - - if not modname and os.path.isdir(path) and '.' not in fn: - modname = fn - for fn in os.listdir(path): - subname = inspect.getmodulename(fn) - if subname=='__init__': - ispkg = True - break - else: - continue # not a package - - if modname and '.' not in modname: - yielded[modname] = 1 - yield prefix + modname, ispkg - - -class ImpLoader: - """PEP 302 Loader that wraps Python's "classic" import algorithm - """ - code = source = None - - def __init__(self, fullname, file, filename, etc): - self.file = file - self.filename = filename - self.fullname = fullname - self.etc = etc - - def load_module(self, fullname): - self._reopen() - try: - mod = imp.load_module(fullname, self.file, self.filename, self.etc) - finally: - if self.file: - self.file.close() - # Note: we don't set __loader__ because we want the module to look - # normal; i.e. this is just a wrapper for standard import machinery - return mod - - def get_data(self, pathname): - return open(pathname, "rb").read() - - def _reopen(self): - if self.file and self.file.closed: - mod_type = self.etc[2] - if mod_type==imp.PY_SOURCE: - self.file = open(self.filename, 'rU') - elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION): - self.file = open(self.filename, 'rb') - - def _fix_name(self, fullname): - if fullname is None: - fullname = self.fullname - elif fullname != self.fullname: - raise ImportError("Loader for module %s cannot handle " - "module %s" % (self.fullname, fullname)) - return fullname - - def is_package(self, fullname): - fullname = self._fix_name(fullname) - return self.etc[2]==imp.PKG_DIRECTORY - - def get_code(self, fullname=None): - fullname = self._fix_name(fullname) - if self.code is None: - mod_type = self.etc[2] - if mod_type==imp.PY_SOURCE: - source = self.get_source(fullname) - self.code = compile(source, self.filename, 'exec') - elif mod_type==imp.PY_COMPILED: - self._reopen() - try: - self.code = read_code(self.file) - finally: - self.file.close() - elif mod_type==imp.PKG_DIRECTORY: - self.code = self._get_delegate().get_code() - return self.code - - def get_source(self, fullname=None): - fullname = self._fix_name(fullname) - if self.source is None: - mod_type = self.etc[2] - if mod_type==imp.PY_SOURCE: - self._reopen() - try: - self.source = self.file.read() - finally: - self.file.close() - elif mod_type==imp.PY_COMPILED: - if os.path.exists(self.filename[:-1]): - f = open(self.filename[:-1], 'rU') - self.source = f.read() - f.close() - elif mod_type==imp.PKG_DIRECTORY: - self.source = self._get_delegate().get_source() - return self.source - - def _get_delegate(self): - return ImpImporter(self.filename).find_module('__init__') - - def get_filename(self, fullname=None): - fullname = self._fix_name(fullname) - mod_type = self.etc[2] - if self.etc[2]==imp.PKG_DIRECTORY: - return self._get_delegate().get_filename() - elif self.etc[2] in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION): - return self.filename - return None - - -try: - import zipimport - from zipimport import zipimporter - - def iter_zipimport_modules(importer, prefix=''): - dirlist = zipimport._zip_directory_cache[importer.archive].keys() - dirlist.sort() - _prefix = importer.prefix - plen = len(_prefix) - yielded = {} - import inspect - for fn in dirlist: - if not fn.startswith(_prefix): - continue - - fn = fn[plen:].split(os.sep) - - if len(fn)==2 and fn[1].startswith('__init__.py'): - if fn[0] not in yielded: - yielded[fn[0]] = 1 - yield fn[0], True - - if len(fn)!=1: - continue - - modname = inspect.getmodulename(fn[0]) - if modname=='__init__': - continue - - if modname and '.' not in modname and modname not in yielded: - yielded[modname] = 1 - yield prefix + modname, False - - iter_importer_modules.register(zipimporter, iter_zipimport_modules) - -except ImportError: - pass - - -def get_importer(path_item): - """Retrieve a PEP 302 importer for the given path item - - The returned importer is cached in sys.path_importer_cache - if it was newly created by a path hook. - - If there is no importer, a wrapper around the basic import - machinery is returned. This wrapper is never inserted into - the importer cache (None is inserted instead). - - The cache (or part of it) can be cleared manually if a - rescan of sys.path_hooks is necessary. - """ - try: - importer = sys.path_importer_cache[path_item] - except KeyError: - for path_hook in sys.path_hooks: - try: - importer = path_hook(path_item) - break - except ImportError: - pass - else: - importer = None - sys.path_importer_cache.setdefault(path_item, importer) - - if importer is None: - try: - importer = ImpImporter(path_item) - except ImportError: - importer = None - return importer - - -def iter_importers(fullname=""): - """Yield PEP 302 importers for the given module name - - If fullname contains a '.', the importers will be for the package - containing fullname, otherwise they will be importers for sys.meta_path, - sys.path, and Python's "classic" import machinery, in that order. If - the named module is in a package, that package is imported as a side - effect of invoking this function. - - Non PEP 302 mechanisms (e.g. the Windows registry) used by the - standard import machinery to find files in alternative locations - are partially supported, but are searched AFTER sys.path. Normally, - these locations are searched BEFORE sys.path, preventing sys.path - entries from shadowing them. - - For this to cause a visible difference in behaviour, there must - be a module or package name that is accessible via both sys.path - and one of the non PEP 302 file system mechanisms. In this case, - the emulation will find the former version, while the builtin - import mechanism will find the latter. - - Items of the following types can be affected by this discrepancy: - imp.C_EXTENSION, imp.PY_SOURCE, imp.PY_COMPILED, imp.PKG_DIRECTORY - """ - if fullname.startswith('.'): - raise ImportError("Relative module names not supported") - if '.' in fullname: - # Get the containing package's __path__ - pkg = '.'.join(fullname.split('.')[:-1]) - if pkg not in sys.modules: - __import__(pkg) - path = getattr(sys.modules[pkg], '__path__', None) or [] - else: - for importer in sys.meta_path: - yield importer - path = sys.path - for item in path: - yield get_importer(item) - if '.' not in fullname: - yield ImpImporter() - - -def get_loader(module_or_name): - """Get a PEP 302 "loader" object for module_or_name - - If the module or package is accessible via the normal import - mechanism, a wrapper around the relevant part of that machinery - is returned. Returns None if the module cannot be found or imported. - If the named module is not already imported, its containing package - (if any) is imported, in order to establish the package __path__. - - This function uses iter_importers(), and is thus subject to the same - limitations regarding platform-specific special import locations such - as the Windows registry. - """ - if module_or_name in sys.modules: - module_or_name = sys.modules[module_or_name] - if isinstance(module_or_name, ModuleType): - module = module_or_name - loader = getattr(module, '__loader__', None) - if loader is not None: - return loader - fullname = module.__name__ - else: - fullname = module_or_name - return find_loader(fullname) - - -def find_loader(fullname): - """Find a PEP 302 "loader" object for fullname - - If fullname contains dots, path must be the containing package's __path__. - Returns None if the module cannot be found or imported. This function uses - iter_importers(), and is thus subject to the same limitations regarding - platform-specific special import locations such as the Windows registry. - """ - for importer in iter_importers(fullname): - loader = importer.find_module(fullname) - if loader is not None: - return loader - - return None - - -def extend_path(path, name): - """Extend a package's path. - - Intended use is to place the following code in a package's __init__.py: - - from pkgutil import extend_path - __path__ = extend_path(__path__, __name__) - - This will add to the package's __path__ all subdirectories of - directories on sys.path named after the package. This is useful - if one wants to distribute different parts of a single logical - package as multiple directories. - - It also looks for *.pkg files beginning where * matches the name - argument. This feature is similar to *.pth files (see site.py), - except that it doesn't special-case lines starting with 'import'. - A *.pkg file is trusted at face value: apart from checking for - duplicates, all entries found in a *.pkg file are added to the - path, regardless of whether they are exist the filesystem. (This - is a feature.) - - If the input path is not a list (as is the case for frozen - packages) it is returned unchanged. The input path is not - modified; an extended copy is returned. Items are only appended - to the copy at the end. - - It is assumed that sys.path is a sequence. Items of sys.path that - are not (unicode or 8-bit) strings referring to existing - directories are ignored. Unicode items of sys.path that cause - errors when used as filenames may cause this function to raise an - exception (in line with os.path.isdir() behavior). - """ - - if not isinstance(path, list): - # This could happen e.g. when this is called from inside a - # frozen package. Return the path unchanged in that case. - return path - - pname = os.path.join(*name.split('.')) # Reconstitute as relative path - # Just in case os.extsep != '.' - sname = os.extsep.join(name.split('.')) - sname_pkg = sname + os.extsep + "pkg" - init_py = "__init__" + os.extsep + "py" - - path = path[:] # Start with a copy of the existing path - - for dir in sys.path: - if not isinstance(dir, basestring) or not os.path.isdir(dir): - continue - subdir = os.path.join(dir, pname) - # XXX This may still add duplicate entries to path on - # case-insensitive filesystems - initfile = os.path.join(subdir, init_py) - if subdir not in path and os.path.isfile(initfile): - path.append(subdir) - # XXX Is this the right thing for subpackages like zope.app? - # It looks for a file named "zope.app.pkg" - pkgfile = os.path.join(dir, sname_pkg) - if os.path.isfile(pkgfile): - try: - f = open(pkgfile) - except IOError, msg: - sys.stderr.write("Can't open %s: %s\n" % - (pkgfile, msg)) - else: - for line in f: - line = line.rstrip('\n') - if not line or line.startswith('#'): - continue - path.append(line) # Don't check for existence! - f.close() - - return path - - -def get_data(package, resource): - """Get a resource from a package. - - This is a wrapper round the PEP 302 loader get_data API. The package - argument should be the name of a package, in standard module format - (foo.bar). The resource argument should be in the form of a relative - filename, using '/' as the path separator. The parent directory name '..' - is not allowed, and nor is a rooted name (starting with a '/'). - - The function returns a binary string, which is the contents of the - specified resource. - - For packages located in the filesystem, which have already been imported, - this is the rough equivalent of - - d = os.path.dirname(sys.modules[package].__file__) - data = open(os.path.join(d, resource), 'rb').read() - - If the package cannot be located or loaded, or it uses a PEP 302 loader - which does not support get_data(), then None is returned. - """ - - loader = get_loader(package) - if loader is None or not hasattr(loader, 'get_data'): - return None - mod = sys.modules.get(package) or loader.load_module(package) - if mod is None or not hasattr(mod, '__file__'): - return None - - # Modify the resource name to be compatible with the loader.get_data - # signature - an os.path format "filename" starting with the dirname of - # the package's __file__ - parts = resource.split('/') - parts.insert(0, os.path.dirname(mod.__file__)) - resource_name = os.path.join(*parts) - return loader.get_data(resource_name) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/backwardcompat.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/backwardcompat.py deleted file mode 100644 index e7c11f1d3..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/backwardcompat.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Stuff that isn't in some old versions of Python""" - -import sys -import os -import shutil - -__all__ = ['any', 'WindowsError', 'md5', 'copytree'] - -try: - WindowsError = WindowsError -except NameError: - WindowsError = None -try: - from hashlib import md5 -except ImportError: - import md5 as md5_module - md5 = md5_module.new - -try: - from pkgutil import walk_packages -except ImportError: - # let's fall back as long as we can - from _pkgutil import walk_packages - -try: - any = any -except NameError: - - def any(seq): - for item in seq: - if item: - return True - return False - - -def copytree(src, dst): - if sys.version_info < (2, 5): - before_last_dir = os.path.dirname(dst) - if not os.path.exists(before_last_dir): - os.makedirs(before_last_dir) - shutil.copytree(src, dst) - shutil.copymode(src, dst) - else: - shutil.copytree(src, dst) - - -def product(*args, **kwds): - # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy - # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111 - pools = map(tuple, args) * kwds.get('repeat', 1) - result = [[]] - for pool in pools: - result = [x+[y] for x in result for y in pool] - for prod in result: - yield tuple(prod) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/basecommand.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/basecommand.py deleted file mode 100644 index f450e8393..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/basecommand.py +++ /dev/null @@ -1,203 +0,0 @@ -"""Base Command class, and related routines""" - -from cStringIO import StringIO -import getpass -import os -import socket -import sys -import traceback -import time -import urllib -import urllib2 - -from pip import commands -from pip.log import logger -from pip.baseparser import parser, ConfigOptionParser, UpdatingDefaultsHelpFormatter -from pip.download import urlopen -from pip.exceptions import BadCommand, InstallationError, UninstallationError -from pip.venv import restart_in_venv -from pip.backwardcompat import walk_packages - -__all__ = ['command_dict', 'Command', 'load_all_commands', - 'load_command', 'command_names'] - -command_dict = {} - -# for backwards compatibiliy -get_proxy = urlopen.get_proxy - - -class Command(object): - name = None - usage = None - hidden = False - - def __init__(self): - assert self.name - self.parser = ConfigOptionParser( - usage=self.usage, - prog='%s %s' % (sys.argv[0], self.name), - version=parser.version, - formatter=UpdatingDefaultsHelpFormatter(), - name=self.name) - for option in parser.option_list: - if not option.dest or option.dest == 'help': - # -h, --version, etc - continue - self.parser.add_option(option) - command_dict[self.name] = self - - def merge_options(self, initial_options, options): - # Make sure we have all global options carried over - for attr in ['log', 'venv', 'proxy', 'venv_base', 'require_venv', - 'respect_venv', 'log_explicit_levels', 'log_file', - 'timeout', 'default_vcs', 'skip_requirements_regex', - 'no_input']: - setattr(options, attr, getattr(initial_options, attr) or getattr(options, attr)) - options.quiet += initial_options.quiet - options.verbose += initial_options.verbose - - def setup_logging(self): - pass - - def main(self, complete_args, args, initial_options): - options, args = self.parser.parse_args(args) - self.merge_options(initial_options, options) - - level = 1 # Notify - level += options.verbose - level -= options.quiet - level = logger.level_for_integer(4-level) - complete_log = [] - logger.consumers.extend( - [(level, sys.stdout), - (logger.DEBUG, complete_log.append)]) - if options.log_explicit_levels: - logger.explicit_levels = True - - self.setup_logging() - - if options.require_venv and not options.venv: - # If a venv is required check if it can really be found - if not os.environ.get('VIRTUAL_ENV'): - logger.fatal('Could not find an activated virtualenv (required).') - sys.exit(3) - # Automatically install in currently activated venv if required - options.respect_venv = True - - if args and args[-1] == '___VENV_RESTART___': - ## FIXME: We don't do anything this this value yet: - args = args[:-2] - options.venv = None - else: - # If given the option to respect the activated environment - # check if no venv is given as a command line parameter - if options.respect_venv and os.environ.get('VIRTUAL_ENV'): - if options.venv and os.path.exists(options.venv): - # Make sure command line venv and environmental are the same - if (os.path.realpath(os.path.expanduser(options.venv)) != - os.path.realpath(os.environ.get('VIRTUAL_ENV'))): - logger.fatal("Given virtualenv (%s) doesn't match " - "currently activated virtualenv (%s)." - % (options.venv, os.environ.get('VIRTUAL_ENV'))) - sys.exit(3) - else: - options.venv = os.environ.get('VIRTUAL_ENV') - logger.info('Using already activated environment %s' % options.venv) - if options.venv: - logger.info('Running in environment %s' % options.venv) - site_packages=False - if options.site_packages: - site_packages=True - restart_in_venv(options.venv, options.venv_base, site_packages, - complete_args) - # restart_in_venv should actually never return, but for clarity... - return - - ## FIXME: not sure if this sure come before or after venv restart - if options.log: - log_fp = open_logfile(options.log, 'a') - logger.consumers.append((logger.DEBUG, log_fp)) - else: - log_fp = None - - socket.setdefaulttimeout(options.timeout or None) - - urlopen.setup(proxystr=options.proxy, prompting=not options.no_input) - - exit = 0 - try: - self.run(options, args) - except (InstallationError, UninstallationError), e: - logger.fatal(str(e)) - logger.info('Exception information:\n%s' % format_exc()) - exit = 1 - except BadCommand, e: - logger.fatal(str(e)) - logger.info('Exception information:\n%s' % format_exc()) - exit = 1 - except: - logger.fatal('Exception:\n%s' % format_exc()) - exit = 2 - - if log_fp is not None: - log_fp.close() - if exit: - log_fn = options.log_file - text = '\n'.join(complete_log) - logger.fatal('Storing complete log in %s' % log_fn) - log_fp = open_logfile(log_fn, 'w') - log_fp.write(text) - log_fp.close() - return exit - - - - -def format_exc(exc_info=None): - if exc_info is None: - exc_info = sys.exc_info() - out = StringIO() - traceback.print_exception(*exc_info, **dict(file=out)) - return out.getvalue() - - -def open_logfile(filename, mode='a'): - """Open the named log file in append mode. - - If the file already exists, a separator will also be printed to - the file to separate past activity from current activity. - """ - filename = os.path.expanduser(filename) - filename = os.path.abspath(filename) - dirname = os.path.dirname(filename) - if not os.path.exists(dirname): - os.makedirs(dirname) - exists = os.path.exists(filename) - - log_fp = open(filename, mode) - if exists: - print >> log_fp, '-'*60 - print >> log_fp, '%s run on %s' % (sys.argv[0], time.strftime('%c')) - return log_fp - - -def load_command(name): - full_name = 'pip.commands.%s' % name - if full_name in sys.modules: - return - try: - __import__(full_name) - except ImportError: - pass - - -def load_all_commands(): - for name in command_names(): - load_command(name) - - -def command_names(): - names = set((pkg[1] for pkg in walk_packages(path=commands.__path__))) - return list(names) - diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/baseparser.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/baseparser.py deleted file mode 100644 index a8bd6ce4f..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/baseparser.py +++ /dev/null @@ -1,231 +0,0 @@ -"""Base option parser setup""" - -import sys -import optparse -import pkg_resources -import ConfigParser -import os -from distutils.util import strtobool -from pip.locations import default_config_file, default_log_file - - -class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter): - """Custom help formatter for use in ConfigOptionParser that updates - the defaults before expanding them, allowing them to show up correctly - in the help listing""" - - def expand_default(self, option): - if self.parser is not None: - self.parser.update_defaults(self.parser.defaults) - return optparse.IndentedHelpFormatter.expand_default(self, option) - - -class ConfigOptionParser(optparse.OptionParser): - """Custom option parser which updates its defaults by by checking the - configuration files and environmental variables""" - - def __init__(self, *args, **kwargs): - self.config = ConfigParser.RawConfigParser() - self.name = kwargs.pop('name') - self.files = self.get_config_files() - self.config.read(self.files) - assert self.name - optparse.OptionParser.__init__(self, *args, **kwargs) - - def get_config_files(self): - config_file = os.environ.get('PIP_CONFIG_FILE', False) - if config_file and os.path.exists(config_file): - return [config_file] - return [default_config_file] - - def update_defaults(self, defaults): - """Updates the given defaults with values from the config files and - the environ. Does a little special handling for certain types of - options (lists).""" - # Then go and look for the other sources of configuration: - config = {} - # 1. config files - for section in ('global', self.name): - config.update(dict(self.get_config_section(section))) - # 2. environmental variables - config.update(dict(self.get_environ_vars())) - # Then set the options with those values - for key, val in config.iteritems(): - key = key.replace('_', '-') - if not key.startswith('--'): - key = '--%s' % key # only prefer long opts - option = self.get_option(key) - if option is not None: - # ignore empty values - if not val: - continue - # handle multiline configs - if option.action == 'append': - val = val.split() - else: - option.nargs = 1 - if option.action in ('store_true', 'store_false', 'count'): - val = strtobool(val) - try: - val = option.convert_value(key, val) - except optparse.OptionValueError, e: - print ("An error occured during configuration: %s" % e) - sys.exit(3) - defaults[option.dest] = val - return defaults - - def get_config_section(self, name): - """Get a section of a configuration""" - if self.config.has_section(name): - return self.config.items(name) - return [] - - def get_environ_vars(self, prefix='PIP_'): - """Returns a generator with all environmental vars with prefix PIP_""" - for key, val in os.environ.iteritems(): - if key.startswith(prefix): - yield (key.replace(prefix, '').lower(), val) - - def get_default_values(self): - """Overridding to make updating the defaults after instantiation of - the option parser possible, update_defaults() does the dirty work.""" - if not self.process_default_values: - # Old, pre-Optik 1.5 behaviour. - return optparse.Values(self.defaults) - - defaults = self.update_defaults(self.defaults.copy()) # ours - for option in self._get_all_options(): - default = defaults.get(option.dest) - if isinstance(default, basestring): - opt_str = option.get_opt_string() - defaults[option.dest] = option.check_value(opt_str, default) - return optparse.Values(defaults) - -try: - pip_dist = pkg_resources.get_distribution('pip') - version = '%s from %s (python %s)' % ( - pip_dist, pip_dist.location, sys.version[:3]) -except pkg_resources.DistributionNotFound: - # when running pip.py without installing - version=None - -parser = ConfigOptionParser( - usage='%prog COMMAND [OPTIONS]', - version=version, - add_help_option=False, - formatter=UpdatingDefaultsHelpFormatter(), - name='global') - -parser.add_option( - '-h', '--help', - dest='help', - action='store_true', - help='Show help') -parser.add_option( - '-E', '--environment', - dest='venv', - metavar='DIR', - help='virtualenv environment to run pip in (either give the ' - 'interpreter or the environment base directory)') -parser.add_option( - '-s', '--enable-site-packages', - dest='site_packages', - action='store_true', - help='Include site-packages in virtualenv if one is to be ' - 'created. Ignored if --environment is not used or ' - 'the virtualenv already exists.') -parser.add_option( - # Defines a default root directory for virtualenvs, relative - # virtualenvs names/paths are considered relative to it. - '--virtualenv-base', - dest='venv_base', - type='str', - default='', - help=optparse.SUPPRESS_HELP) -parser.add_option( - # Run only if inside a virtualenv, bail if not. - '--require-virtualenv', '--require-venv', - dest='require_venv', - action='store_true', - default=False, - help=optparse.SUPPRESS_HELP) -parser.add_option( - # Use automatically an activated virtualenv instead of installing - # globally. -E will be ignored if used. - '--respect-virtualenv', '--respect-venv', - dest='respect_venv', - action='store_true', - default=False, - help=optparse.SUPPRESS_HELP) - -parser.add_option( - '-v', '--verbose', - dest='verbose', - action='count', - default=0, - help='Give more output') -parser.add_option( - '-q', '--quiet', - dest='quiet', - action='count', - default=0, - help='Give less output') -parser.add_option( - '--log', - dest='log', - metavar='FILENAME', - help='Log file where a complete (maximum verbosity) record will be kept') -parser.add_option( - # Writes the log levels explicitely to the log' - '--log-explicit-levels', - dest='log_explicit_levels', - action='store_true', - default=False, - help=optparse.SUPPRESS_HELP) -parser.add_option( - # The default log file - '--local-log', '--log-file', - dest='log_file', - metavar='FILENAME', - default=default_log_file, - help=optparse.SUPPRESS_HELP) -parser.add_option( - # Don't ask for input - '--no-input', - dest='no_input', - action='store_true', - default=False, - help=optparse.SUPPRESS_HELP) - -parser.add_option( - '--proxy', - dest='proxy', - type='str', - default='', - help="Specify a proxy in the form user:passwd@proxy.server:port. " - "Note that the user:password@ is optional and required only if you " - "are behind an authenticated proxy. If you provide " - "user@proxy.server:port then you will be prompted for a password.") -parser.add_option( - '--timeout', '--default-timeout', - metavar='SECONDS', - dest='timeout', - type='float', - default=15, - help='Set the socket timeout (default %default seconds)') -parser.add_option( - # The default version control system for editables, e.g. 'svn' - '--default-vcs', - dest='default_vcs', - type='str', - default='', - help=optparse.SUPPRESS_HELP) -parser.add_option( - # A regex to be used to skip requirements - '--skip-requirements-regex', - dest='skip_requirements_regex', - type='str', - default='', - help=optparse.SUPPRESS_HELP) - -parser.disable_interspersed_args() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/__init__.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/__init__.py deleted file mode 100644 index 792d60054..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/bundle.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/bundle.py deleted file mode 100644 index fb0f75704..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/bundle.py +++ /dev/null @@ -1,33 +0,0 @@ -from pip.locations import build_prefix, src_prefix -from pip.util import display_path, backup_dir -from pip.log import logger -from pip.exceptions import InstallationError -from pip.commands.install import InstallCommand - - -class BundleCommand(InstallCommand): - name = 'bundle' - usage = '%prog [OPTIONS] BUNDLE_NAME.pybundle PACKAGE_NAMES...' - summary = 'Create pybundles (archives containing multiple packages)' - bundle = True - - def __init__(self): - super(BundleCommand, self).__init__() - - def run(self, options, args): - if not args: - raise InstallationError('You must give a bundle filename') - if not options.build_dir: - options.build_dir = backup_dir(build_prefix, '-bundle') - if not options.src_dir: - options.src_dir = backup_dir(src_prefix, '-bundle') - # We have to get everything when creating a bundle: - options.ignore_installed = True - logger.notify('Putting temporary build files in %s and source/develop files in %s' - % (display_path(options.build_dir), display_path(options.src_dir))) - self.bundle_filename = args.pop(0) - requirement_set = super(BundleCommand, self).run(options, args) - return requirement_set - - -BundleCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/completion.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/completion.py deleted file mode 100644 index d003b9ae3..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/completion.py +++ /dev/null @@ -1,60 +0,0 @@ -import sys -from pip.basecommand import Command - -BASE_COMPLETION = """ -# pip %(shell)s completion start%(script)s# pip %(shell)s completion end -""" - -COMPLETION_SCRIPTS = { - 'bash': """ -_pip_completion() -{ - COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \\ - COMP_CWORD=$COMP_CWORD \\ - PIP_AUTO_COMPLETE=1 $1 ) ) -} -complete -o default -F _pip_completion pip -""", 'zsh': """ -function _pip_completion { - local words cword - read -Ac words - read -cn cword - reply=( $( COMP_WORDS="$words[*]" \\ - COMP_CWORD=$(( cword-1 )) \\ - PIP_AUTO_COMPLETE=1 $words[1] ) ) -} -compctl -K _pip_completion pip -"""} - - -class CompletionCommand(Command): - name = 'completion' - summary = 'A helper command to be used for command completion' - hidden = True - - def __init__(self): - super(CompletionCommand, self).__init__() - self.parser.add_option( - '--bash', '-b', - action='store_const', - const='bash', - dest='shell', - help='Emit completion code for bash') - self.parser.add_option( - '--zsh', '-z', - action='store_const', - const='zsh', - dest='shell', - help='Emit completion code for zsh') - - def run(self, options, args): - """Prints the completion code of the given shell""" - shells = COMPLETION_SCRIPTS.keys() - shell_options = ['--'+shell for shell in sorted(shells)] - if options.shell in shells: - script = COMPLETION_SCRIPTS.get(options.shell, '') - print BASE_COMPLETION % {'script': script, 'shell': options.shell} - else: - sys.stderr.write('ERROR: You must pass %s\n' % ' or '.join(shell_options)) - -CompletionCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/freeze.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/freeze.py deleted file mode 100644 index 01b5df934..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/freeze.py +++ /dev/null @@ -1,109 +0,0 @@ -import re -import sys -import pkg_resources -import pip -from pip.req import InstallRequirement -from pip.log import logger -from pip.basecommand import Command -from pip.util import get_installed_distributions - - -class FreezeCommand(Command): - name = 'freeze' - usage = '%prog [OPTIONS]' - summary = 'Output all currently installed packages (exact versions) to stdout' - - def __init__(self): - super(FreezeCommand, self).__init__() - self.parser.add_option( - '-r', '--requirement', - dest='requirement', - action='store', - default=None, - metavar='FILENAME', - help='Use the given requirements file as a hint about how to generate the new frozen requirements') - self.parser.add_option( - '-f', '--find-links', - dest='find_links', - action='append', - default=[], - metavar='URL', - help='URL for finding packages, which will be added to the frozen requirements file') - self.parser.add_option( - '-l', '--local', - dest='local', - action='store_true', - default=False, - help='If in a virtualenv, do not report globally-installed packages') - - def setup_logging(self): - logger.move_stdout_to_stderr() - - def run(self, options, args): - requirement = options.requirement - find_links = options.find_links or [] - local_only = options.local - ## FIXME: Obviously this should be settable: - find_tags = False - skip_match = None - - skip_regex = options.skip_requirements_regex - if skip_regex: - skip_match = re.compile(skip_regex) - - dependency_links = [] - - f = sys.stdout - - for dist in pkg_resources.working_set: - if dist.has_metadata('dependency_links.txt'): - dependency_links.extend(dist.get_metadata_lines('dependency_links.txt')) - for link in find_links: - if '#egg=' in link: - dependency_links.append(link) - for link in find_links: - f.write('-f %s\n' % link) - installations = {} - for dist in get_installed_distributions(local_only=local_only): - req = pip.FrozenRequirement.from_dist(dist, dependency_links, find_tags=find_tags) - installations[req.name] = req - if requirement: - req_f = open(requirement) - for line in req_f: - if not line.strip() or line.strip().startswith('#'): - f.write(line) - continue - if skip_match and skip_match.search(line): - f.write(line) - continue - elif line.startswith('-e') or line.startswith('--editable'): - if line.startswith('-e'): - line = line[2:].strip() - else: - line = line[len('--editable'):].strip().lstrip('=') - line_req = InstallRequirement.from_editable(line, default_vcs=options.default_vcs) - elif (line.startswith('-r') or line.startswith('--requirement') - or line.startswith('-Z') or line.startswith('--always-unzip') - or line.startswith('-f') or line.startswith('-i') - or line.startswith('--extra-index-url')): - f.write(line) - continue - else: - line_req = InstallRequirement.from_line(line) - if not line_req.name: - logger.notify("Skipping line because it's not clear what it would install: %s" - % line.strip()) - logger.notify(" (add #egg=PackageName to the URL to avoid this warning)") - continue - if line_req.name not in installations: - logger.warn("Requirement file contains %s, but that package is not installed" - % line.strip()) - continue - f.write(str(installations[line_req.name])) - del installations[line_req.name] - f.write('## The following requirements were added by pip --freeze:\n') - for installation in sorted(installations.values(), key=lambda x: x.name): - f.write(str(installation)) - - -FreezeCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/help.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/help.py deleted file mode 100644 index b0b366112..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/help.py +++ /dev/null @@ -1,32 +0,0 @@ -from pip.basecommand import Command, command_dict, load_all_commands -from pip.exceptions import InstallationError -from pip.baseparser import parser - - -class HelpCommand(Command): - name = 'help' - usage = '%prog' - summary = 'Show available commands' - - def run(self, options, args): - load_all_commands() - if args: - ## FIXME: handle errors better here - command = args[0] - if command not in command_dict: - raise InstallationError('No command with the name: %s' % command) - command = command_dict[command] - command.parser.print_help() - return - parser.print_help() - print - print 'Commands available:' - commands = list(set(command_dict.values())) - commands.sort(key=lambda x: x.name) - for command in commands: - if command.hidden: - continue - print ' %s: %s' % (command.name, command.summary) - - -HelpCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/install.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/install.py deleted file mode 100644 index 861c332bf..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/install.py +++ /dev/null @@ -1,247 +0,0 @@ -import os, sys -from pip.req import InstallRequirement, RequirementSet -from pip.req import parse_requirements -from pip.log import logger -from pip.locations import build_prefix, src_prefix -from pip.basecommand import Command -from pip.index import PackageFinder -from pip.exceptions import InstallationError - - -class InstallCommand(Command): - name = 'install' - usage = '%prog [OPTIONS] PACKAGE_NAMES...' - summary = 'Install packages' - bundle = False - - def __init__(self): - super(InstallCommand, self).__init__() - self.parser.add_option( - '-e', '--editable', - dest='editables', - action='append', - default=[], - metavar='VCS+REPOS_URL[@REV]#egg=PACKAGE', - help='Install a package directly from a checkout. Source will be checked ' - 'out into src/PACKAGE (lower-case) and installed in-place (using ' - 'setup.py develop). You can run this on an existing directory/checkout (like ' - 'pip install -e src/mycheckout). This option may be provided multiple times. ' - 'Possible values for VCS are: svn, git, hg and bzr.') - self.parser.add_option( - '-r', '--requirement', - dest='requirements', - action='append', - default=[], - metavar='FILENAME', - help='Install all the packages listed in the given requirements file. ' - 'This option can be used multiple times.') - self.parser.add_option( - '-f', '--find-links', - dest='find_links', - action='append', - default=[], - metavar='URL', - help='URL to look for packages at') - self.parser.add_option( - '-i', '--index-url', '--pypi-url', - dest='index_url', - metavar='URL', - default='http://pypi.python.org/simple/', - help='Base URL of Python Package Index (default %default)') - self.parser.add_option( - '--extra-index-url', - dest='extra_index_urls', - metavar='URL', - action='append', - default=[], - help='Extra URLs of package indexes to use in addition to --index-url') - self.parser.add_option( - '--no-index', - dest='no_index', - action='store_true', - default=False, - help='Ignore package index (only looking at --find-links URLs instead)') - self.parser.add_option( - '-M', '--use-mirrors', - dest='use_mirrors', - action='store_true', - default=False, - help='Use the PyPI mirrors as a fallback in case the main index is down.') - self.parser.add_option( - '--mirrors', - dest='mirrors', - metavar='URL', - action='append', - default=[], - help='Specific mirror URLs to query when --use-mirrors is used') - - self.parser.add_option( - '-b', '--build', '--build-dir', '--build-directory', - dest='build_dir', - metavar='DIR', - default=None, - help='Unpack packages into DIR (default %s) and build from there' % build_prefix) - self.parser.add_option( - '-d', '--download', '--download-dir', '--download-directory', - dest='download_dir', - metavar='DIR', - default=None, - help='Download packages into DIR instead of installing them') - self.parser.add_option( - '--download-cache', - dest='download_cache', - metavar='DIR', - default=None, - help='Cache downloaded packages in DIR') - self.parser.add_option( - '--src', '--source', '--source-dir', '--source-directory', - dest='src_dir', - metavar='DIR', - default=None, - help='Check out --editable packages into DIR (default %s)' % src_prefix) - - self.parser.add_option( - '-U', '--upgrade', - dest='upgrade', - action='store_true', - help='Upgrade all packages to the newest available version') - self.parser.add_option( - '-I', '--ignore-installed', - dest='ignore_installed', - action='store_true', - help='Ignore the installed packages (reinstalling instead)') - self.parser.add_option( - '--no-deps', '--no-dependencies', - dest='ignore_dependencies', - action='store_true', - default=False, - help='Ignore package dependencies') - self.parser.add_option( - '--no-install', - dest='no_install', - action='store_true', - help="Download and unpack all packages, but don't actually install them") - self.parser.add_option( - '--no-download', - dest='no_download', - action="store_true", - help="Don't download any packages, just install the ones already downloaded " - "(completes an install run with --no-install)") - - self.parser.add_option( - '--install-option', - dest='install_options', - action='append', - help="Extra arguments to be supplied to the setup.py install " - "command (use like --install-option=\"--install-scripts=/usr/local/bin\"). " - "Use multiple --install-option options to pass multiple options to setup.py install. " - "If you are using an option with a directory path, be sure to use absolute path.") - - self.parser.add_option( - '--global-option', - dest='global_options', - action='append', - help="Extra global options to be supplied to the setup.py" - "call before the install command") - - self.parser.add_option( - '--user', - dest='use_user_site', - action='store_true', - help='Install to user-site') - - def _build_package_finder(self, options, index_urls): - """ - Create a package finder appropriate to this install command. - This method is meant to be overridden by subclasses, not - called directly. - """ - return PackageFinder(find_links=options.find_links, - index_urls=index_urls, - use_mirrors=options.use_mirrors, - mirrors=options.mirrors) - - def run(self, options, args): - if not options.build_dir: - options.build_dir = build_prefix - if not options.src_dir: - options.src_dir = src_prefix - if options.download_dir: - options.no_install = True - options.ignore_installed = True - options.build_dir = os.path.abspath(options.build_dir) - options.src_dir = os.path.abspath(options.src_dir) - install_options = options.install_options or [] - if options.use_user_site: - install_options.append('--user') - global_options = options.global_options or [] - index_urls = [options.index_url] + options.extra_index_urls - if options.no_index: - logger.notify('Ignoring indexes: %s' % ','.join(index_urls)) - index_urls = [] - - finder = self._build_package_finder(options, index_urls) - - requirement_set = RequirementSet( - build_dir=options.build_dir, - src_dir=options.src_dir, - download_dir=options.download_dir, - download_cache=options.download_cache, - upgrade=options.upgrade, - ignore_installed=options.ignore_installed, - ignore_dependencies=options.ignore_dependencies) - for name in args: - requirement_set.add_requirement( - InstallRequirement.from_line(name, None)) - for name in options.editables: - requirement_set.add_requirement( - InstallRequirement.from_editable(name, default_vcs=options.default_vcs)) - for filename in options.requirements: - for req in parse_requirements(filename, finder=finder, options=options): - requirement_set.add_requirement(req) - - if not requirement_set.has_requirements: - if options.find_links: - raise InstallationError('You must give at least one ' - 'requirement to %s (maybe you meant "pip install %s"?)' - % (self.name, " ".join(options.find_links))) - raise InstallationError('You must give at least one requirement ' - 'to %(name)s (see "pip help %(name)s")' % dict(name=self.name)) - - if (options.use_user_site and - sys.version_info < (2, 6)): - raise InstallationError('--user is only supported in Python version 2.6 and newer') - - import setuptools - if (options.use_user_site and - requirement_set.has_editables and - not getattr(setuptools, '_distribute', False)): - - raise InstallationError('--user --editable not supported with setuptools, use distribute') - - if not options.no_download: - requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle) - else: - requirement_set.locate_files() - - if not options.no_install and not self.bundle: - requirement_set.install(install_options, global_options) - installed = ' '.join([req.name for req in - requirement_set.successfully_installed]) - if installed: - logger.notify('Successfully installed %s' % installed) - elif not self.bundle: - downloaded = ' '.join([req.name for req in - requirement_set.successfully_downloaded]) - if downloaded: - logger.notify('Successfully downloaded %s' % downloaded) - elif self.bundle: - requirement_set.create_bundle(self.bundle_filename) - logger.notify('Created bundle in %s' % self.bundle_filename) - # Clean up - if not options.no_install: - requirement_set.cleanup_files(bundle=self.bundle) - return requirement_set - - -InstallCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/search.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/search.py deleted file mode 100644 index 73da58ac6..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/search.py +++ /dev/null @@ -1,116 +0,0 @@ -import sys -import xmlrpclib -import textwrap -import pkg_resources -import pip.download -from pip.basecommand import Command -from pip.util import get_terminal_size -from pip.log import logger -from distutils.version import StrictVersion, LooseVersion - - -class SearchCommand(Command): - name = 'search' - usage = '%prog QUERY' - summary = 'Search PyPI' - - def __init__(self): - super(SearchCommand, self).__init__() - self.parser.add_option( - '--index', - dest='index', - metavar='URL', - default='http://pypi.python.org/pypi', - help='Base URL of Python Package Index (default %default)') - - def run(self, options, args): - if not args: - logger.warn('ERROR: Missing required argument (search query).') - return - query = ' '.join(args) - index_url = options.index - - pypi_hits = self.search(query, index_url) - hits = transform_hits(pypi_hits) - - terminal_width = None - if sys.stdout.isatty(): - terminal_width = get_terminal_size()[0] - - print_results(hits, terminal_width=terminal_width) - - def search(self, query, index_url): - pypi = xmlrpclib.ServerProxy(index_url, pip.download.xmlrpclib_transport) - hits = pypi.search({'name': query, 'summary': query}, 'or') - return hits - - -def transform_hits(hits): - """ - The list from pypi is really a list of versions. We want a list of - packages with the list of versions stored inline. This converts the - list from pypi into one we can use. - """ - packages = {} - for hit in hits: - name = hit['name'] - summary = hit['summary'] - version = hit['version'] - score = hit['_pypi_ordering'] - - if name not in packages.keys(): - packages[name] = {'name': name, 'summary': summary, 'versions': [version], 'score': score} - else: - packages[name]['versions'].append(version) - - # if this is the highest version, replace summary and score - if version == highest_version(packages[name]['versions']): - packages[name]['summary'] = summary - packages[name]['score'] = score - - # each record has a unique name now, so we will convert the dict into a list sorted by score - package_list = sorted(packages.values(), lambda x, y: cmp(y['score'], x['score'])) - return package_list - - -def print_results(hits, name_column_width=25, terminal_width=None): - installed_packages = [p.project_name for p in pkg_resources.working_set] - for hit in hits: - name = hit['name'] - summary = hit['summary'] or '' - if terminal_width is not None: - # wrap and indent summary to fit terminal - summary = textwrap.wrap(summary, terminal_width - name_column_width - 5) - summary = ('\n' + ' ' * (name_column_width + 3)).join(summary) - line = '%s - %s' % (name.ljust(name_column_width), summary) - try: - logger.notify(line) - if name in installed_packages: - dist = pkg_resources.get_distribution(name) - logger.indent += 2 - try: - latest = highest_version(hit['versions']) - if dist.version == latest: - logger.notify('INSTALLED: %s (latest)' % dist.version) - else: - logger.notify('INSTALLED: %s' % dist.version) - logger.notify('LATEST: %s' % latest) - finally: - logger.indent -= 2 - except UnicodeEncodeError: - pass - - -def compare_versions(version1, version2): - try: - return cmp(StrictVersion(version1), StrictVersion(version2)) - # in case of abnormal version number, fall back to LooseVersion - except ValueError: - return cmp(LooseVersion(version1), LooseVersion(version2)) - - -def highest_version(versions): - return reduce((lambda v1, v2: compare_versions(v1, v2) == 1 and v1 or v2), versions) - - -SearchCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/uninstall.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/uninstall.py deleted file mode 100644 index 7effd844e..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/uninstall.py +++ /dev/null @@ -1,42 +0,0 @@ -from pip.req import InstallRequirement, RequirementSet, parse_requirements -from pip.basecommand import Command -from pip.exceptions import InstallationError - -class UninstallCommand(Command): - name = 'uninstall' - usage = '%prog [OPTIONS] PACKAGE_NAMES ...' - summary = 'Uninstall packages' - - def __init__(self): - super(UninstallCommand, self).__init__() - self.parser.add_option( - '-r', '--requirement', - dest='requirements', - action='append', - default=[], - metavar='FILENAME', - help='Uninstall all the packages listed in the given requirements file. ' - 'This option can be used multiple times.') - self.parser.add_option( - '-y', '--yes', - dest='yes', - action='store_true', - help="Don't ask for confirmation of uninstall deletions.") - - def run(self, options, args): - requirement_set = RequirementSet( - build_dir=None, - src_dir=None, - download_dir=None) - for name in args: - requirement_set.add_requirement( - InstallRequirement.from_line(name)) - for filename in options.requirements: - for req in parse_requirements(filename, options=options): - requirement_set.add_requirement(req) - if not requirement_set.has_requirements: - raise InstallationError('You must give at least one requirement ' - 'to %(name)s (see "pip help %(name)s")' % dict(name=self.name)) - requirement_set.uninstall(auto_confirm=options.yes) - -UninstallCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/unzip.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/unzip.py deleted file mode 100644 index f83e18205..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/unzip.py +++ /dev/null @@ -1,9 +0,0 @@ -from pip.commands.zip import ZipCommand - - -class UnzipCommand(ZipCommand): - name = 'unzip' - summary = 'Unzip individual packages' - - -UnzipCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/zip.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/zip.py deleted file mode 100644 index 346fc0519..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/commands/zip.py +++ /dev/null @@ -1,346 +0,0 @@ -import sys -import re -import fnmatch -import os -import shutil -import zipfile -from pip.util import display_path, backup_dir -from pip.log import logger -from pip.exceptions import InstallationError -from pip.basecommand import Command - - -class ZipCommand(Command): - name = 'zip' - usage = '%prog [OPTIONS] PACKAGE_NAMES...' - summary = 'Zip individual packages' - - def __init__(self): - super(ZipCommand, self).__init__() - if self.name == 'zip': - self.parser.add_option( - '--unzip', - action='store_true', - dest='unzip', - help='Unzip (rather than zip) a package') - else: - self.parser.add_option( - '--zip', - action='store_false', - dest='unzip', - default=True, - help='Zip (rather than unzip) a package') - self.parser.add_option( - '--no-pyc', - action='store_true', - dest='no_pyc', - help='Do not include .pyc files in zip files (useful on Google App Engine)') - self.parser.add_option( - '-l', '--list', - action='store_true', - dest='list', - help='List the packages available, and their zip status') - self.parser.add_option( - '--sort-files', - action='store_true', - dest='sort_files', - help='With --list, sort packages according to how many files they contain') - self.parser.add_option( - '--path', - action='append', - dest='paths', - help='Restrict operations to the given paths (may include wildcards)') - self.parser.add_option( - '-n', '--simulate', - action='store_true', - help='Do not actually perform the zip/unzip operation') - - def paths(self): - """All the entries of sys.path, possibly restricted by --path""" - if not self.select_paths: - return sys.path - result = [] - match_any = set() - for path in sys.path: - path = os.path.normcase(os.path.abspath(path)) - for match in self.select_paths: - match = os.path.normcase(os.path.abspath(match)) - if '*' in match: - if re.search(fnmatch.translate(match+'*'), path): - result.append(path) - match_any.add(match) - break - else: - if path.startswith(match): - result.append(path) - match_any.add(match) - break - else: - logger.debug("Skipping path %s because it doesn't match %s" - % (path, ', '.join(self.select_paths))) - for match in self.select_paths: - if match not in match_any and '*' not in match: - result.append(match) - logger.debug("Adding path %s because it doesn't match anything already on sys.path" - % match) - return result - - def run(self, options, args): - self.select_paths = options.paths - self.simulate = options.simulate - if options.list: - return self.list(options, args) - if not args: - raise InstallationError( - 'You must give at least one package to zip or unzip') - packages = [] - for arg in args: - module_name, filename = self.find_package(arg) - if options.unzip and os.path.isdir(filename): - raise InstallationError( - 'The module %s (in %s) is not a zip file; cannot be unzipped' - % (module_name, filename)) - elif not options.unzip and not os.path.isdir(filename): - raise InstallationError( - 'The module %s (in %s) is not a directory; cannot be zipped' - % (module_name, filename)) - packages.append((module_name, filename)) - last_status = None - for module_name, filename in packages: - if options.unzip: - last_status = self.unzip_package(module_name, filename) - else: - last_status = self.zip_package(module_name, filename, options.no_pyc) - return last_status - - def unzip_package(self, module_name, filename): - zip_filename = os.path.dirname(filename) - if not os.path.isfile(zip_filename) and zipfile.is_zipfile(zip_filename): - raise InstallationError( - 'Module %s (in %s) isn\'t located in a zip file in %s' - % (module_name, filename, zip_filename)) - package_path = os.path.dirname(zip_filename) - if not package_path in self.paths(): - logger.warn( - 'Unpacking %s into %s, but %s is not on sys.path' - % (display_path(zip_filename), display_path(package_path), - display_path(package_path))) - logger.notify('Unzipping %s (in %s)' % (module_name, display_path(zip_filename))) - if self.simulate: - logger.notify('Skipping remaining operations because of --simulate') - return - logger.indent += 2 - try: - ## FIXME: this should be undoable: - zip = zipfile.ZipFile(zip_filename) - to_save = [] - for name in zip.namelist(): - if name.startswith(module_name + os.path.sep): - content = zip.read(name) - dest = os.path.join(package_path, name) - if not os.path.exists(os.path.dirname(dest)): - os.makedirs(os.path.dirname(dest)) - if not content and dest.endswith(os.path.sep): - if not os.path.exists(dest): - os.makedirs(dest) - else: - f = open(dest, 'wb') - f.write(content) - f.close() - else: - to_save.append((name, zip.read(name))) - zip.close() - if not to_save: - logger.info('Removing now-empty zip file %s' % display_path(zip_filename)) - os.unlink(zip_filename) - self.remove_filename_from_pth(zip_filename) - else: - logger.info('Removing entries in %s/ from zip file %s' % (module_name, display_path(zip_filename))) - zip = zipfile.ZipFile(zip_filename, 'w') - for name, content in to_save: - zip.writestr(name, content) - zip.close() - finally: - logger.indent -= 2 - - def zip_package(self, module_name, filename, no_pyc): - orig_filename = filename - logger.notify('Zip %s (in %s)' % (module_name, display_path(filename))) - logger.indent += 2 - if filename.endswith('.egg'): - dest_filename = filename - else: - dest_filename = filename + '.zip' - try: - ## FIXME: I think this needs to be undoable: - if filename == dest_filename: - filename = backup_dir(orig_filename) - logger.notify('Moving %s aside to %s' % (orig_filename, filename)) - if not self.simulate: - shutil.move(orig_filename, filename) - try: - logger.info('Creating zip file in %s' % display_path(dest_filename)) - if not self.simulate: - zip = zipfile.ZipFile(dest_filename, 'w') - zip.writestr(module_name + '/', '') - for dirpath, dirnames, filenames in os.walk(filename): - if no_pyc: - filenames = [f for f in filenames - if not f.lower().endswith('.pyc')] - for fns, is_dir in [(dirnames, True), (filenames, False)]: - for fn in fns: - full = os.path.join(dirpath, fn) - dest = os.path.join(module_name, dirpath[len(filename):].lstrip(os.path.sep), fn) - if is_dir: - zip.writestr(dest+'/', '') - else: - zip.write(full, dest) - zip.close() - logger.info('Removing old directory %s' % display_path(filename)) - if not self.simulate: - shutil.rmtree(filename) - except: - ## FIXME: need to do an undo here - raise - ## FIXME: should also be undone: - self.add_filename_to_pth(dest_filename) - finally: - logger.indent -= 2 - - def remove_filename_from_pth(self, filename): - for pth in self.pth_files(): - f = open(pth, 'r') - lines = f.readlines() - f.close() - new_lines = [ - l for l in lines if l.strip() != filename] - if lines != new_lines: - logger.info('Removing reference to %s from .pth file %s' - % (display_path(filename), display_path(pth))) - if not filter(None, new_lines): - logger.info('%s file would be empty: deleting' % display_path(pth)) - if not self.simulate: - os.unlink(pth) - else: - if not self.simulate: - f = open(pth, 'wb') - f.writelines(new_lines) - f.close() - return - logger.warn('Cannot find a reference to %s in any .pth file' % display_path(filename)) - - def add_filename_to_pth(self, filename): - path = os.path.dirname(filename) - dest = os.path.join(path, filename + '.pth') - if path not in self.paths(): - logger.warn('Adding .pth file %s, but it is not on sys.path' % display_path(dest)) - if not self.simulate: - if os.path.exists(dest): - f = open(dest) - lines = f.readlines() - f.close() - if lines and not lines[-1].endswith('\n'): - lines[-1] += '\n' - lines.append(filename+'\n') - else: - lines = [filename + '\n'] - f = open(dest, 'wb') - f.writelines(lines) - f.close() - - def pth_files(self): - for path in self.paths(): - if not os.path.exists(path) or not os.path.isdir(path): - continue - for filename in os.listdir(path): - if filename.endswith('.pth'): - yield os.path.join(path, filename) - - def find_package(self, package): - for path in self.paths(): - full = os.path.join(path, package) - if os.path.exists(full): - return package, full - if not os.path.isdir(path) and zipfile.is_zipfile(path): - zip = zipfile.ZipFile(path, 'r') - try: - zip.read(os.path.join(package, '__init__.py')) - except KeyError: - pass - else: - zip.close() - return package, full - zip.close() - ## FIXME: need special error for package.py case: - raise InstallationError( - 'No package with the name %s found' % package) - - def list(self, options, args): - if args: - raise InstallationError( - 'You cannot give an argument with --list') - for path in sorted(self.paths()): - if not os.path.exists(path): - continue - basename = os.path.basename(path.rstrip(os.path.sep)) - if os.path.isfile(path) and zipfile.is_zipfile(path): - if os.path.dirname(path) not in self.paths(): - logger.notify('Zipped egg: %s' % display_path(path)) - continue - if (basename != 'site-packages' and basename != 'dist-packages' - and not path.replace('\\', '/').endswith('lib/python')): - continue - logger.notify('In %s:' % display_path(path)) - logger.indent += 2 - zipped = [] - unzipped = [] - try: - for filename in sorted(os.listdir(path)): - ext = os.path.splitext(filename)[1].lower() - if ext in ('.pth', '.egg-info', '.egg-link'): - continue - if ext == '.py': - logger.info('Not displaying %s: not a package' % display_path(filename)) - continue - full = os.path.join(path, filename) - if os.path.isdir(full): - unzipped.append((filename, self.count_package(full))) - elif zipfile.is_zipfile(full): - zipped.append(filename) - else: - logger.info('Unknown file: %s' % display_path(filename)) - if zipped: - logger.notify('Zipped packages:') - logger.indent += 2 - try: - for filename in zipped: - logger.notify(filename) - finally: - logger.indent -= 2 - else: - logger.notify('No zipped packages.') - if unzipped: - if options.sort_files: - unzipped.sort(key=lambda x: -x[1]) - logger.notify('Unzipped packages:') - logger.indent += 2 - try: - for filename, count in unzipped: - logger.notify('%s (%i files)' % (filename, count)) - finally: - logger.indent -= 2 - else: - logger.notify('No unzipped packages.') - finally: - logger.indent -= 2 - - def count_package(self, path): - total = 0 - for dirpath, dirnames, filenames in os.walk(path): - filenames = [f for f in filenames - if not f.lower().endswith('.pyc')] - total += len(filenames) - return total - - -ZipCommand() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/download.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/download.py deleted file mode 100644 index f1b63936b..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/download.py +++ /dev/null @@ -1,470 +0,0 @@ -import xmlrpclib -import re -import getpass -import urllib -import urllib2 -import urlparse -import os -import mimetypes -import shutil -import tempfile -from pip.backwardcompat import md5, copytree -from pip.exceptions import InstallationError -from pip.util import (splitext, - format_size, display_path, backup_dir, ask, - unpack_file, create_download_cache_folder, cache_download) -from pip.vcs import vcs -from pip.log import logger - - -__all__ = ['xmlrpclib_transport', 'get_file_content', 'urlopen', - 'is_url', 'url_to_path', 'path_to_url', 'path_to_url2', - 'geturl', 'is_archive_file', 'unpack_vcs_link', - 'unpack_file_url', 'is_vcs_url', 'is_file_url', 'unpack_http_url'] - - -xmlrpclib_transport = xmlrpclib.Transport() - - -def get_file_content(url, comes_from=None): - """Gets the content of a file; it may be a filename, file: URL, or - http: URL. Returns (location, content)""" - match = _scheme_re.search(url) - if match: - scheme = match.group(1).lower() - if (scheme == 'file' and comes_from - and comes_from.startswith('http')): - raise InstallationError( - 'Requirements file %s references URL %s, which is local' - % (comes_from, url)) - if scheme == 'file': - path = url.split(':', 1)[1] - path = path.replace('\\', '/') - match = _url_slash_drive_re.match(path) - if match: - path = match.group(1) + ':' + path.split('|', 1)[1] - path = urllib.unquote(path) - if path.startswith('/'): - path = '/' + path.lstrip('/') - url = path - else: - ## FIXME: catch some errors - resp = urlopen(url) - return geturl(resp), resp.read() - try: - f = open(url) - content = f.read() - except IOError, e: - raise InstallationError('Could not open requirements file: %s' % str(e)) - else: - f.close() - return url, content - - -_scheme_re = re.compile(r'^(http|https|file):', re.I) -_url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I) - -class URLOpener(object): - """ - pip's own URL helper that adds HTTP auth and proxy support - """ - def __init__(self): - self.passman = urllib2.HTTPPasswordMgrWithDefaultRealm() - - def __call__(self, url): - """ - If the given url contains auth info or if a normal request gets a 401 - response, an attempt is made to fetch the resource using basic HTTP - auth. - - """ - url, username, password = self.extract_credentials(url) - if username is None: - try: - response = urllib2.urlopen(self.get_request(url)) - except urllib2.HTTPError, e: - if e.code != 401: - raise - response = self.get_response(url) - else: - response = self.get_response(url, username, password) - return response - - def get_request(self, url): - """ - Wraps the URL to retrieve to protects against "creative" - interpretation of the RFC: http://bugs.python.org/issue8732 - """ - if isinstance(url, basestring): - url = urllib2.Request(url, headers={'Accept-encoding': 'identity'}) - return url - - def get_response(self, url, username=None, password=None): - """ - does the dirty work of actually getting the rsponse object using urllib2 - and its HTTP auth builtins. - """ - scheme, netloc, path, query, frag = urlparse.urlsplit(url) - pass_url = urlparse.urlunsplit(('_none_', netloc, path, query, frag)).replace('_none_://', '', 1) - req = self.get_request(url) - - stored_username, stored_password = self.passman.find_user_password(None, netloc) - # see if we have a password stored - if stored_username is None: - if username is None and self.prompting: - username = urllib.quote(raw_input('User for %s: ' % netloc)) - password = urllib.quote(getpass.getpass('Password: ')) - if username and password: - self.passman.add_password(None, netloc, username, password) - stored_username, stored_password = self.passman.find_user_password(None, netloc) - authhandler = urllib2.HTTPBasicAuthHandler(self.passman) - opener = urllib2.build_opener(authhandler) - # FIXME: should catch a 401 and offer to let the user reenter credentials - return opener.open(req) - - def setup(self, proxystr='', prompting=True): - """ - Sets the proxy handler given the option passed on the command - line. If an empty string is passed it looks at the HTTP_PROXY - environment variable. - """ - self.prompting = prompting - proxy = self.get_proxy(proxystr) - if proxy: - proxy_support = urllib2.ProxyHandler({"http": proxy, "ftp": proxy}) - opener = urllib2.build_opener(proxy_support, urllib2.CacheFTPHandler) - urllib2.install_opener(opener) - - def parse_credentials(self, netloc): - if "@" in netloc: - userinfo = netloc.rsplit("@", 1)[0] - if ":" in userinfo: - return userinfo.split(":", 1) - return userinfo, None - return None, None - - def extract_credentials(self, url): - """ - Extracts user/password from a url. - - Returns a tuple: - (url-without-auth, username, password) - """ - if isinstance(url, urllib2.Request): - result = urlparse.urlsplit(url.get_full_url()) - else: - result = urlparse.urlsplit(url) - scheme, netloc, path, query, frag = result - - username, password = self.parse_credentials(netloc) - if username is None: - return url, None, None - elif password is None and self.prompting: - # remove the auth credentials from the url part - netloc = netloc.replace('%s@' % username, '', 1) - # prompt for the password - prompt = 'Password for %s@%s: ' % (username, netloc) - password = urllib.quote(getpass.getpass(prompt)) - else: - # remove the auth credentials from the url part - netloc = netloc.replace('%s:%s@' % (username, password), '', 1) - - target_url = urlparse.urlunsplit((scheme, netloc, path, query, frag)) - return target_url, username, password - - def get_proxy(self, proxystr=''): - """ - Get the proxy given the option passed on the command line. - If an empty string is passed it looks at the HTTP_PROXY - environment variable. - """ - if not proxystr: - proxystr = os.environ.get('HTTP_PROXY', '') - if proxystr: - if '@' in proxystr: - user_password, server_port = proxystr.split('@', 1) - if ':' in user_password: - user, password = user_password.split(':', 1) - else: - user = user_password - prompt = 'Password for %s@%s: ' % (user, server_port) - password = urllib.quote(getpass.getpass(prompt)) - return '%s:%s@%s' % (user, password, server_port) - else: - return proxystr - else: - return None - -urlopen = URLOpener() - - -def is_url(name): - """Returns true if the name looks like a URL""" - if ':' not in name: - return False - scheme = name.split(':', 1)[0].lower() - return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes - - -def url_to_path(url): - """ - Convert a file: URL to a path. - """ - assert url.startswith('file:'), ( - "You can only turn file: urls into filenames (not %r)" % url) - path = url[len('file:'):].lstrip('/') - path = urllib.unquote(path) - if _url_drive_re.match(path): - path = path[0] + ':' + path[2:] - else: - path = '/' + path - return path - - -_drive_re = re.compile('^([a-z]):', re.I) -_url_drive_re = re.compile('^([a-z])[:|]', re.I) - - -def path_to_url(path): - """ - Convert a path to a file: URL. The path will be made absolute. - """ - path = os.path.normcase(os.path.abspath(path)) - if _drive_re.match(path): - path = path[0] + '|' + path[2:] - url = urllib.quote(path) - url = url.replace(os.path.sep, '/') - url = url.lstrip('/') - return 'file:///' + url - - -def path_to_url2(path): - """ - Convert a path to a file: URL. The path will be made absolute and have - quoted path parts. - """ - path = os.path.normpath(os.path.abspath(path)) - drive, path = os.path.splitdrive(path) - filepath = path.split(os.path.sep) - url = '/'.join([urllib.quote(part) for part in filepath]) - if not drive: - url = url.lstrip('/') - return 'file:///' + drive + url - - -def geturl(urllib2_resp): - """ - Use instead of urllib.addinfourl.geturl(), which appears to have - some issues with dropping the double slash for certain schemes - (e.g. file://). This implementation is probably over-eager, as it - always restores '://' if it is missing, and it appears some url - schemata aren't always followed by '//' after the colon, but as - far as I know pip doesn't need any of those. - The URI RFC can be found at: http://tools.ietf.org/html/rfc1630 - - This function assumes that - scheme:/foo/bar - is the same as - scheme:///foo/bar - """ - url = urllib2_resp.geturl() - scheme, rest = url.split(':', 1) - if rest.startswith('//'): - return url - else: - # FIXME: write a good test to cover it - return '%s://%s' % (scheme, rest) - - -def is_archive_file(name): - """Return True if `name` is a considered as an archive file.""" - archives = ('.zip', '.tar.gz', '.tar.bz2', '.tgz', '.tar', '.pybundle') - ext = splitext(name)[1].lower() - if ext in archives: - return True - return False - - -def unpack_vcs_link(link, location, only_download=False): - vcs_backend = _get_used_vcs_backend(link) - if only_download: - vcs_backend.export(location) - else: - vcs_backend.unpack(location) - - -def unpack_file_url(link, location): - source = url_to_path(link.url) - content_type = mimetypes.guess_type(source)[0] - if os.path.isdir(source): - # delete the location since shutil will create it again :( - if os.path.isdir(location): - shutil.rmtree(location) - copytree(source, location) - else: - unpack_file(source, location, content_type, link) - - -def _get_used_vcs_backend(link): - for backend in vcs.backends: - if link.scheme in backend.schemes: - vcs_backend = backend(link.url) - return vcs_backend - - -def is_vcs_url(link): - return bool(_get_used_vcs_backend(link)) - - -def is_file_url(link): - return link.url.lower().startswith('file:') - - -def _check_md5(download_hash, link): - download_hash = download_hash.hexdigest() - if download_hash != link.md5_hash: - logger.fatal("MD5 hash of the package %s (%s) doesn't match the expected hash %s!" - % (link, download_hash, link.md5_hash)) - raise InstallationError('Bad MD5 hash for package %s' % link) - - -def _get_md5_from_file(target_file, link): - download_hash = md5() - fp = open(target_file, 'rb') - while 1: - chunk = fp.read(4096) - if not chunk: - break - download_hash.update(chunk) - fp.close() - return download_hash - - -def _download_url(resp, link, temp_location): - fp = open(temp_location, 'wb') - download_hash = None - if link.md5_hash: - download_hash = md5() - try: - total_length = int(resp.info()['content-length']) - except (ValueError, KeyError): - total_length = 0 - downloaded = 0 - show_progress = total_length > 40*1000 or not total_length - show_url = link.show_url - try: - if show_progress: - ## FIXME: the URL can get really long in this message: - if total_length: - logger.start_progress('Downloading %s (%s): ' % (show_url, format_size(total_length))) - else: - logger.start_progress('Downloading %s (unknown size): ' % show_url) - else: - logger.notify('Downloading %s' % show_url) - logger.debug('Downloading from URL %s' % link) - - while 1: - chunk = resp.read(4096) - if not chunk: - break - downloaded += len(chunk) - if show_progress: - if not total_length: - logger.show_progress('%s' % format_size(downloaded)) - else: - logger.show_progress('%3i%% %s' % (100*downloaded/total_length, format_size(downloaded))) - if link.md5_hash: - download_hash.update(chunk) - fp.write(chunk) - fp.close() - finally: - if show_progress: - logger.end_progress('%s downloaded' % format_size(downloaded)) - return download_hash - - -def _copy_file(filename, location, content_type, link): - copy = True - download_location = os.path.join(location, link.filename) - if os.path.exists(download_location): - response = ask('The file %s exists. (i)gnore, (w)ipe, (b)ackup ' - % display_path(download_location), ('i', 'w', 'b')) - if response == 'i': - copy = False - elif response == 'w': - logger.warn('Deleting %s' % display_path(download_location)) - os.remove(download_location) - elif response == 'b': - dest_file = backup_dir(download_location) - logger.warn('Backing up %s to %s' - % (display_path(download_location), display_path(dest_file))) - shutil.move(download_location, dest_file) - if copy: - shutil.copy(filename, download_location) - logger.indent -= 2 - logger.notify('Saved %s' % display_path(download_location)) - - -def unpack_http_url(link, location, download_cache, only_download): - temp_dir = tempfile.mkdtemp('-unpack', 'pip-') - target_url = link.url.split('#', 1)[0] - target_file = None - download_hash = None - if download_cache: - target_file = os.path.join(download_cache, - urllib.quote(target_url, '')) - if not os.path.isdir(download_cache): - create_download_cache_folder(download_cache) - if (target_file - and os.path.exists(target_file) - and os.path.exists(target_file+'.content-type')): - fp = open(target_file+'.content-type') - content_type = fp.read().strip() - fp.close() - if link.md5_hash: - download_hash = _get_md5_from_file(target_file, link) - temp_location = target_file - logger.notify('Using download cache from %s' % target_file) - else: - resp = _get_response_from_url(target_url, link) - content_type = resp.info()['content-type'] - filename = link.filename - ext = splitext(filename)[1] - if not ext: - ext = mimetypes.guess_extension(content_type) - if ext: - filename += ext - if not ext and link.url != geturl(resp): - ext = os.path.splitext(geturl(resp))[1] - if ext: - filename += ext - temp_location = os.path.join(temp_dir, filename) - download_hash = _download_url(resp, link, temp_location) - if link.md5_hash: - _check_md5(download_hash, link) - if only_download: - _copy_file(temp_location, location, content_type, link) - else: - unpack_file(temp_location, location, content_type, link) - if target_file and target_file != temp_location: - cache_download(target_file, temp_location, content_type) - if target_file is None: - os.unlink(temp_location) - os.rmdir(temp_dir) - - -def _get_response_from_url(target_url, link): - try: - resp = urlopen(target_url) - except urllib2.HTTPError, e: - logger.fatal("HTTP error %s while getting %s" % (e.code, link)) - raise - except IOError, e: - # Typically an FTP error - logger.fatal("Error %s while getting %s" % (e, link)) - raise - return resp - -class Urllib2HeadRequest(urllib2.Request): - def get_method(self): - return "HEAD" diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/exceptions.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/exceptions.py deleted file mode 100644 index 1ad1a616d..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/exceptions.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Exceptions used throughout package""" - - -class InstallationError(Exception): - """General exception during installation""" - - -class UninstallationError(Exception): - """General exception during uninstallation""" - - -class DistributionNotFound(InstallationError): - """Raised when a distribution cannot be found to satisfy a requirement""" - - -class BadCommand(Exception): - """Raised when virtualenv or a command is not found""" diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/index.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/index.py deleted file mode 100644 index e42d8c868..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/index.py +++ /dev/null @@ -1,686 +0,0 @@ -"""Routines related to PyPI, indexes""" - -import sys -import os -import re -import mimetypes -import threading -import posixpath -import pkg_resources -import urllib -import urllib2 -import urlparse -import httplib -import random -import socket -import string -from Queue import Queue -from Queue import Empty as QueueEmpty -from pip.log import logger -from pip.util import Inf -from pip.util import normalize_name, splitext -from pip.exceptions import DistributionNotFound -from pip.backwardcompat import WindowsError, product -from pip.download import urlopen, path_to_url2, url_to_path, geturl, Urllib2HeadRequest - -__all__ = ['PackageFinder'] - - -DEFAULT_MIRROR_URL = "last.pypi.python.org" - - -class PackageFinder(object): - """This finds packages. - - This is meant to match easy_install's technique for looking for - packages, by reading pages and looking for appropriate links - """ - - def __init__(self, find_links, index_urls, - use_mirrors=False, mirrors=None, main_mirror_url=None): - self.find_links = find_links - self.index_urls = index_urls - self.dependency_links = [] - self.cache = PageCache() - # These are boring links that have already been logged somehow: - self.logged_links = set() - if use_mirrors: - self.mirror_urls = self._get_mirror_urls(mirrors, main_mirror_url) - logger.info('Using PyPI mirrors: %s' % ', '.join(self.mirror_urls)) - else: - self.mirror_urls = [] - - def add_dependency_links(self, links): - ## FIXME: this shouldn't be global list this, it should only - ## apply to requirements of the package that specifies the - ## dependency_links value - ## FIXME: also, we should track comes_from (i.e., use Link) - self.dependency_links.extend(links) - - @staticmethod - def _sort_locations(locations): - """ - Sort locations into "files" (archives) and "urls", and return - a pair of lists (files,urls) - """ - files = [] - urls = [] - - # puts the url for the given file path into the appropriate - # list - def sort_path(path): - url = path_to_url2(path) - if mimetypes.guess_type(url, strict=False)[0] == 'text/html': - urls.append(url) - else: - files.append(url) - - for url in locations: - if url.startswith('file:'): - path = url_to_path(url) - if os.path.isdir(path): - path = os.path.realpath(path) - for item in os.listdir(path): - sort_path(os.path.join(path, item)) - elif os.path.isfile(path): - sort_path(path) - else: - urls.append(url) - return files, urls - - def find_requirement(self, req, upgrade): - url_name = req.url_name - # Only check main index if index URL is given: - main_index_url = None - if self.index_urls: - # Check that we have the url_name correctly spelled: - main_index_url = Link(posixpath.join(self.index_urls[0], url_name)) - # This will also cache the page, so it's okay that we get it again later: - page = self._get_page(main_index_url, req) - if page is None: - url_name = self._find_url_name(Link(self.index_urls[0]), url_name, req) or req.url_name - - # Combine index URLs with mirror URLs here to allow - # adding more index URLs from requirements files - all_index_urls = self.index_urls + self.mirror_urls - - def mkurl_pypi_url(url): - loc = posixpath.join(url, url_name) - # For maximum compatibility with easy_install, ensure the path - # ends in a trailing slash. Although this isn't in the spec - # (and PyPI can handle it without the slash) some other index - # implementations might break if they relied on easy_install's behavior. - if not loc.endswith('/'): - loc = loc + '/' - return loc - if url_name is not None: - locations = [ - mkurl_pypi_url(url) - for url in all_index_urls] + self.find_links - else: - locations = list(self.find_links) - locations.extend(self.dependency_links) - for version in req.absolute_versions: - if url_name is not None and main_index_url is not None: - locations = [ - posixpath.join(main_index_url.url, version)] + locations - - file_locations, url_locations = self._sort_locations(locations) - - locations = [Link(url) for url in url_locations] - logger.debug('URLs to search for versions for %s:' % req) - for location in locations: - logger.debug('* %s' % location) - found_versions = [] - found_versions.extend( - self._package_versions( - [Link(url, '-f') for url in self.find_links], req.name.lower())) - page_versions = [] - for page in self._get_pages(locations, req): - logger.debug('Analyzing links from page %s' % page.url) - logger.indent += 2 - try: - page_versions.extend(self._package_versions(page.links, req.name.lower())) - finally: - logger.indent -= 2 - dependency_versions = list(self._package_versions( - [Link(url) for url in self.dependency_links], req.name.lower())) - if dependency_versions: - logger.info('dependency_links found: %s' % ', '.join([link.url for parsed, link, version in dependency_versions])) - file_versions = list(self._package_versions( - [Link(url) for url in file_locations], req.name.lower())) - if not found_versions and not page_versions and not dependency_versions and not file_versions: - logger.fatal('Could not find any downloads that satisfy the requirement %s' % req) - raise DistributionNotFound('No distributions at all found for %s' % req) - if req.satisfied_by is not None: - found_versions.append((req.satisfied_by.parsed_version, Inf, req.satisfied_by.version)) - if file_versions: - file_versions.sort(reverse=True) - logger.info('Local files found: %s' % ', '.join([url_to_path(link.url) for parsed, link, version in file_versions])) - found_versions = file_versions + found_versions - all_versions = found_versions + page_versions + dependency_versions - applicable_versions = [] - for (parsed_version, link, version) in all_versions: - if version not in req.req: - logger.info("Ignoring link %s, version %s doesn't match %s" - % (link, version, ','.join([''.join(s) for s in req.req.specs]))) - continue - applicable_versions.append((link, version)) - applicable_versions = sorted(applicable_versions, key=lambda v: pkg_resources.parse_version(v[1]), reverse=True) - existing_applicable = bool([link for link, version in applicable_versions if link is Inf]) - if not upgrade and existing_applicable: - if applicable_versions[0][1] is Inf: - logger.info('Existing installed version (%s) is most up-to-date and satisfies requirement' - % req.satisfied_by.version) - else: - logger.info('Existing installed version (%s) satisfies requirement (most up-to-date version is %s)' - % (req.satisfied_by.version, applicable_versions[0][1])) - return None - if not applicable_versions: - logger.fatal('Could not find a version that satisfies the requirement %s (from versions: %s)' - % (req, ', '.join([version for parsed_version, link, version in found_versions]))) - raise DistributionNotFound('No distributions matching the version for %s' % req) - if applicable_versions[0][0] is Inf: - # We have an existing version, and its the best version - logger.info('Installed version (%s) is most up-to-date (past versions: %s)' - % (req.satisfied_by.version, ', '.join([version for link, version in applicable_versions[1:]]) or 'none')) - return None - if len(applicable_versions) > 1: - logger.info('Using version %s (newest of versions: %s)' % - (applicable_versions[0][1], ', '.join([version for link, version in applicable_versions]))) - return applicable_versions[0][0] - - def _find_url_name(self, index_url, url_name, req): - """Finds the true URL name of a package, when the given name isn't quite correct. - This is usually used to implement case-insensitivity.""" - if not index_url.url.endswith('/'): - # Vaguely part of the PyPI API... weird but true. - ## FIXME: bad to modify this? - index_url.url += '/' - page = self._get_page(index_url, req) - if page is None: - logger.fatal('Cannot fetch index base URL %s' % index_url) - return - norm_name = normalize_name(req.url_name) - for link in page.links: - base = posixpath.basename(link.path.rstrip('/')) - if norm_name == normalize_name(base): - logger.notify('Real name of requirement %s is %s' % (url_name, base)) - return base - return None - - def _get_pages(self, locations, req): - """Yields (page, page_url) from the given locations, skipping - locations that have errors, and adding download/homepage links""" - pending_queue = Queue() - for location in locations: - pending_queue.put(location) - done = [] - seen = set() - threads = [] - for i in range(min(10, len(locations))): - t = threading.Thread(target=self._get_queued_page, args=(req, pending_queue, done, seen)) - t.setDaemon(True) - threads.append(t) - t.start() - for t in threads: - t.join() - return done - - _log_lock = threading.Lock() - - def _get_queued_page(self, req, pending_queue, done, seen): - while 1: - try: - location = pending_queue.get(False) - except QueueEmpty: - return - if location in seen: - continue - seen.add(location) - page = self._get_page(location, req) - if page is None: - continue - done.append(page) - for link in page.rel_links(): - pending_queue.put(link) - - _egg_fragment_re = re.compile(r'#egg=([^&]*)') - _egg_info_re = re.compile(r'([a-z0-9_.]+)-([a-z0-9_.-]+)', re.I) - _py_version_re = re.compile(r'-py([123]\.[0-9])$') - - def _sort_links(self, links): - "Returns elements of links in order, non-egg links first, egg links second, while eliminating duplicates" - eggs, no_eggs = [], [] - seen = set() - for link in links: - if link not in seen: - seen.add(link) - if link.egg_fragment: - eggs.append(link) - else: - no_eggs.append(link) - return no_eggs + eggs - - def _package_versions(self, links, search_name): - for link in self._sort_links(links): - for v in self._link_package_versions(link, search_name): - yield v - - def _link_package_versions(self, link, search_name): - """ - Return an iterable of triples (pkg_resources_version_key, - link, python_version) that can be extracted from the given - link. - - Meant to be overridden by subclasses, not called by clients. - """ - if link.egg_fragment: - egg_info = link.egg_fragment - else: - egg_info, ext = link.splitext() - if not ext: - if link not in self.logged_links: - logger.debug('Skipping link %s; not a file' % link) - self.logged_links.add(link) - return [] - if egg_info.endswith('.tar'): - # Special double-extension case: - egg_info = egg_info[:-4] - ext = '.tar' + ext - if ext not in ('.tar.gz', '.tar.bz2', '.tar', '.tgz', '.zip'): - if link not in self.logged_links: - logger.debug('Skipping link %s; unknown archive format: %s' % (link, ext)) - self.logged_links.add(link) - return [] - version = self._egg_info_matches(egg_info, search_name, link) - if version is None: - logger.debug('Skipping link %s; wrong project name (not %s)' % (link, search_name)) - return [] - match = self._py_version_re.search(version) - if match: - version = version[:match.start()] - py_version = match.group(1) - if py_version != sys.version[:3]: - logger.debug('Skipping %s because Python version is incorrect' % link) - return [] - logger.debug('Found link %s, version: %s' % (link, version)) - return [(pkg_resources.parse_version(version), - link, - version)] - - def _egg_info_matches(self, egg_info, search_name, link): - match = self._egg_info_re.search(egg_info) - if not match: - logger.debug('Could not parse version from link: %s' % link) - return None - name = match.group(0).lower() - # To match the "safe" name that pkg_resources creates: - name = name.replace('_', '-') - if name.startswith(search_name.lower()): - return match.group(0)[len(search_name):].lstrip('-') - else: - return None - - def _get_page(self, link, req): - return HTMLPage.get_page(link, req, cache=self.cache) - - def _get_mirror_urls(self, mirrors=None, main_mirror_url=None): - """Retrieves a list of URLs from the main mirror DNS entry - unless a list of mirror URLs are passed. - """ - if not mirrors: - mirrors = get_mirrors(main_mirror_url) - # Should this be made "less random"? E.g. netselect like? - random.shuffle(mirrors) - - mirror_urls = set() - for mirror_url in mirrors: - # Make sure we have a valid URL - if not ("http://" or "https://" or "file://") in mirror_url: - mirror_url = "http://%s" % mirror_url - if not mirror_url.endswith("/simple"): - mirror_url = "%s/simple/" % mirror_url - mirror_urls.add(mirror_url) - - return list(mirror_urls) - - -class PageCache(object): - """Cache of HTML pages""" - - failure_limit = 3 - - def __init__(self): - self._failures = {} - self._pages = {} - self._archives = {} - - def too_many_failures(self, url): - return self._failures.get(url, 0) >= self.failure_limit - - def get_page(self, url): - return self._pages.get(url) - - def is_archive(self, url): - return self._archives.get(url, False) - - def set_is_archive(self, url, value=True): - self._archives[url] = value - - def add_page_failure(self, url, level): - self._failures[url] = self._failures.get(url, 0)+level - - def add_page(self, urls, page): - for url in urls: - self._pages[url] = page - - -class HTMLPage(object): - """Represents one page, along with its URL""" - - ## FIXME: these regexes are horrible hacks: - _homepage_re = re.compile(r'<th>\s*home\s*page', re.I) - _download_re = re.compile(r'<th>\s*download\s+url', re.I) - ## These aren't so aweful: - _rel_re = re.compile("""<[^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*>""", re.I) - _href_re = re.compile('href=(?:"([^"]*)"|\'([^\']*)\'|([^>\\s\\n]*))', re.I|re.S) - _base_re = re.compile(r"""<base\s+href\s*=\s*['"]?([^'">]+)""", re.I) - - def __init__(self, content, url, headers=None): - self.content = content - self.url = url - self.headers = headers - - def __str__(self): - return self.url - - @classmethod - def get_page(cls, link, req, cache=None, skip_archives=True): - url = link.url - url = url.split('#', 1)[0] - if cache.too_many_failures(url): - return None - - # Check for VCS schemes that do not support lookup as web pages. - from pip.vcs import VcsSupport - for scheme in VcsSupport.schemes: - if url.lower().startswith(scheme) and url[len(scheme)] in '+:': - logger.debug('Cannot look at %(scheme)s URL %(link)s' % locals()) - return None - - if cache is not None: - inst = cache.get_page(url) - if inst is not None: - return inst - try: - if skip_archives: - if cache is not None: - if cache.is_archive(url): - return None - filename = link.filename - for bad_ext in ['.tar', '.tar.gz', '.tar.bz2', '.tgz', '.zip']: - if filename.endswith(bad_ext): - content_type = cls._get_content_type(url) - if content_type.lower().startswith('text/html'): - break - else: - logger.debug('Skipping page %s because of Content-Type: %s' % (link, content_type)) - if cache is not None: - cache.set_is_archive(url) - return None - logger.debug('Getting page %s' % url) - - # Tack index.html onto file:// URLs that point to directories - (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url) - if scheme == 'file' and os.path.isdir(urllib.url2pathname(path)): - # add trailing slash if not present so urljoin doesn't trim final segment - if not url.endswith('/'): - url += '/' - url = urlparse.urljoin(url, 'index.html') - logger.debug(' file: URL is directory, getting %s' % url) - - resp = urlopen(url) - - real_url = geturl(resp) - headers = resp.info() - inst = cls(resp.read(), real_url, headers) - except (urllib2.HTTPError, urllib2.URLError, socket.timeout, socket.error, OSError, WindowsError), e: - desc = str(e) - if isinstance(e, socket.timeout): - log_meth = logger.info - level =1 - desc = 'timed out' - elif isinstance(e, urllib2.URLError): - log_meth = logger.info - if hasattr(e, 'reason') and isinstance(e.reason, socket.timeout): - desc = 'timed out' - level = 1 - else: - level = 2 - elif isinstance(e, urllib2.HTTPError) and e.code == 404: - ## FIXME: notify? - log_meth = logger.info - level = 2 - else: - log_meth = logger.info - level = 1 - log_meth('Could not fetch URL %s: %s' % (link, desc)) - log_meth('Will skip URL %s when looking for download links for %s' % (link.url, req)) - if cache is not None: - cache.add_page_failure(url, level) - return None - if cache is not None: - cache.add_page([url, real_url], inst) - return inst - - @staticmethod - def _get_content_type(url): - """Get the Content-Type of the given url, using a HEAD request""" - scheme, netloc, path, query, fragment = urlparse.urlsplit(url) - if not scheme in ('http', 'https', 'ftp', 'ftps'): - ## FIXME: some warning or something? - ## assertion error? - return '' - req = Urllib2HeadRequest(url, headers={'Host': netloc}) - resp = urlopen(req) - try: - if hasattr(resp, 'code') and resp.code != 200 and scheme not in ('ftp', 'ftps'): - ## FIXME: doesn't handle redirects - return '' - return resp.info().get('content-type', '') - finally: - resp.close() - - @property - def base_url(self): - if not hasattr(self, "_base_url"): - match = self._base_re.search(self.content) - if match: - self._base_url = match.group(1) - else: - self._base_url = self.url - return self._base_url - - @property - def links(self): - """Yields all links in the page""" - for match in self._href_re.finditer(self.content): - url = match.group(1) or match.group(2) or match.group(3) - url = self.clean_link(urlparse.urljoin(self.base_url, url)) - yield Link(url, self) - - def rel_links(self): - for url in self.explicit_rel_links(): - yield url - for url in self.scraped_rel_links(): - yield url - - def explicit_rel_links(self, rels=('homepage', 'download')): - """Yields all links with the given relations""" - for match in self._rel_re.finditer(self.content): - found_rels = match.group(1).lower().split() - for rel in rels: - if rel in found_rels: - break - else: - continue - match = self._href_re.search(match.group(0)) - if not match: - continue - url = match.group(1) or match.group(2) or match.group(3) - url = self.clean_link(urlparse.urljoin(self.base_url, url)) - yield Link(url, self) - - def scraped_rel_links(self): - for regex in (self._homepage_re, self._download_re): - match = regex.search(self.content) - if not match: - continue - href_match = self._href_re.search(self.content, pos=match.end()) - if not href_match: - continue - url = match.group(1) or match.group(2) or match.group(3) - if not url: - continue - url = self.clean_link(urlparse.urljoin(self.base_url, url)) - yield Link(url, self) - - _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) - - def clean_link(self, url): - """Makes sure a link is fully encoded. That is, if a ' ' shows up in - the link, it will be rewritten to %20 (while not over-quoting - % or other characters).""" - return self._clean_re.sub( - lambda match: '%%%2x' % ord(match.group(0)), url) - - -class Link(object): - - def __init__(self, url, comes_from=None): - self.url = url - self.comes_from = comes_from - - def __str__(self): - if self.comes_from: - return '%s (from %s)' % (self.url, self.comes_from) - else: - return self.url - - def __repr__(self): - return '<Link %s>' % self - - def __eq__(self, other): - return self.url == other.url - - def __hash__(self): - return hash(self.url) - - @property - def filename(self): - url = self.url - url = url.split('#', 1)[0] - url = url.split('?', 1)[0] - url = url.rstrip('/') - name = posixpath.basename(url) - assert name, ( - 'URL %r produced no filename' % url) - return name - - @property - def scheme(self): - return urlparse.urlsplit(self.url)[0] - - @property - def path(self): - return urlparse.urlsplit(self.url)[2] - - def splitext(self): - return splitext(posixpath.basename(self.path.rstrip('/'))) - - _egg_fragment_re = re.compile(r'#egg=([^&]*)') - - @property - def egg_fragment(self): - match = self._egg_fragment_re.search(self.url) - if not match: - return None - return match.group(1) - - _md5_re = re.compile(r'md5=([a-f0-9]+)') - - @property - def md5_hash(self): - match = self._md5_re.search(self.url) - if match: - return match.group(1) - return None - - @property - def show_url(self): - return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0]) - - -def get_requirement_from_url(url): - """Get a requirement from the URL, if possible. This looks for #egg - in the URL""" - link = Link(url) - egg_info = link.egg_fragment - if not egg_info: - egg_info = splitext(link.filename)[0] - return package_to_requirement(egg_info) - - -def package_to_requirement(package_name): - """Translate a name like Foo-1.2 to Foo==1.3""" - match = re.search(r'^(.*?)(-dev|-\d.*)', package_name) - if match: - name = match.group(1) - version = match.group(2) - else: - name = package_name - version = '' - if version: - return '%s==%s' % (name, version) - else: - return name - - -def get_mirrors(hostname=None): - """Return the list of mirrors from the last record found on the DNS - entry:: - - >>> from pip.index import get_mirrors - >>> get_mirrors() - ['a.pypi.python.org', 'b.pypi.python.org', 'c.pypi.python.org', - 'd.pypi.python.org'] - - Originally written for the distutils2 project by Alexis Metaireau. - """ - if hostname is None: - hostname = DEFAULT_MIRROR_URL - - # return the last mirror registered on PyPI. - try: - hostname = socket.gethostbyname_ex(hostname)[0] - except socket.gaierror: - return [] - end_letter = hostname.split(".", 1) - - # determine the list from the last one. - return ["%s.%s" % (s, end_letter[1]) for s in string_range(end_letter[0])] - - -def string_range(last): - """Compute the range of string between "a" and last. - - This works for simple "a to z" lists, but also for "a to zz" lists. - """ - for k in range(len(last)): - for x in product(string.ascii_lowercase, repeat=k+1): - result = ''.join(x) - yield result - if result == last: - return - diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/locations.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/locations.py deleted file mode 100644 index 4254ef2fb..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/locations.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Locations where we look for configs, install stuff, etc""" - -import sys -import os -from distutils import sysconfig - - -def running_under_virtualenv(): - """ - Return True if we're running inside a virtualenv, False otherwise. - - """ - return hasattr(sys, 'real_prefix') - - -if running_under_virtualenv(): - ## FIXME: is build/ a good name? - build_prefix = os.path.join(sys.prefix, 'build') - src_prefix = os.path.join(sys.prefix, 'src') -else: - ## FIXME: this isn't a very good default - build_prefix = os.path.join(os.getcwd(), 'build') - src_prefix = os.path.join(os.getcwd(), 'src') - -# FIXME doesn't account for venv linked to global site-packages - -site_packages = sysconfig.get_python_lib() -user_dir = os.path.expanduser('~') -if sys.platform == 'win32': - bin_py = os.path.join(sys.prefix, 'Scripts') - # buildout uses 'bin' on Windows too? - if not os.path.exists(bin_py): - bin_py = os.path.join(sys.prefix, 'bin') - user_dir = os.environ.get('APPDATA', user_dir) # Use %APPDATA% for roaming - default_storage_dir = os.path.join(user_dir, 'pip') - default_config_file = os.path.join(default_storage_dir, 'pip.ini') - default_log_file = os.path.join(default_storage_dir, 'pip.log') -else: - bin_py = os.path.join(sys.prefix, 'bin') - default_storage_dir = os.path.join(user_dir, '.pip') - default_config_file = os.path.join(default_storage_dir, 'pip.conf') - default_log_file = os.path.join(default_storage_dir, 'pip.log') - # Forcing to use /usr/local/bin for standard Mac OS X framework installs - if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': - bin_py = '/usr/local/bin' diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/log.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/log.py deleted file mode 100644 index 0218ab1ae..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/log.py +++ /dev/null @@ -1,181 +0,0 @@ -"""Logging -""" - -import sys -import logging - - -class Logger(object): - - """ - Logging object for use in command-line script. Allows ranges of - levels, to avoid some redundancy of displayed information. - """ - - VERBOSE_DEBUG = logging.DEBUG-1 - DEBUG = logging.DEBUG - INFO = logging.INFO - NOTIFY = (logging.INFO+logging.WARN)/2 - WARN = WARNING = logging.WARN - ERROR = logging.ERROR - FATAL = logging.FATAL - - LEVELS = [VERBOSE_DEBUG, DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL] - - def __init__(self): - self.consumers = [] - self.indent = 0 - self.explicit_levels = False - self.in_progress = None - self.in_progress_hanging = False - - def debug(self, msg, *args, **kw): - self.log(self.DEBUG, msg, *args, **kw) - - def info(self, msg, *args, **kw): - self.log(self.INFO, msg, *args, **kw) - - def notify(self, msg, *args, **kw): - self.log(self.NOTIFY, msg, *args, **kw) - - def warn(self, msg, *args, **kw): - self.log(self.WARN, msg, *args, **kw) - - def error(self, msg, *args, **kw): - self.log(self.WARN, msg, *args, **kw) - - def fatal(self, msg, *args, **kw): - self.log(self.FATAL, msg, *args, **kw) - - def log(self, level, msg, *args, **kw): - if args: - if kw: - raise TypeError( - "You may give positional or keyword arguments, not both") - args = args or kw - rendered = None - for consumer_level, consumer in self.consumers: - if self.level_matches(level, consumer_level): - if (self.in_progress_hanging - and consumer in (sys.stdout, sys.stderr)): - self.in_progress_hanging = False - sys.stdout.write('\n') - sys.stdout.flush() - if rendered is None: - if args: - rendered = msg % args - else: - rendered = msg - rendered = ' '*self.indent + rendered - if self.explicit_levels: - ## FIXME: should this be a name, not a level number? - rendered = '%02i %s' % (level, rendered) - if hasattr(consumer, 'write'): - consumer.write(rendered+'\n') - else: - consumer(rendered) - - def start_progress(self, msg): - assert not self.in_progress, ( - "Tried to start_progress(%r) while in_progress %r" - % (msg, self.in_progress)) - if self.level_matches(self.NOTIFY, self._stdout_level()): - sys.stdout.write(' '*self.indent + msg) - sys.stdout.flush() - self.in_progress_hanging = True - else: - self.in_progress_hanging = False - self.in_progress = msg - self.last_message = None - - def end_progress(self, msg='done.'): - assert self.in_progress, ( - "Tried to end_progress without start_progress") - if self.stdout_level_matches(self.NOTIFY): - if not self.in_progress_hanging: - # Some message has been printed out since start_progress - sys.stdout.write('...' + self.in_progress + msg + '\n') - sys.stdout.flush() - else: - # These erase any messages shown with show_progress (besides .'s) - logger.show_progress('') - logger.show_progress('') - sys.stdout.write(msg + '\n') - sys.stdout.flush() - self.in_progress = None - self.in_progress_hanging = False - - def show_progress(self, message=None): - """If we are in a progress scope, and no log messages have been - shown, write out another '.'""" - if self.in_progress_hanging: - if message is None: - sys.stdout.write('.') - sys.stdout.flush() - else: - if self.last_message: - padding = ' ' * max(0, len(self.last_message)-len(message)) - else: - padding = '' - sys.stdout.write('\r%s%s%s%s' % (' '*self.indent, self.in_progress, message, padding)) - sys.stdout.flush() - self.last_message = message - - def stdout_level_matches(self, level): - """Returns true if a message at this level will go to stdout""" - return self.level_matches(level, self._stdout_level()) - - def _stdout_level(self): - """Returns the level that stdout runs at""" - for level, consumer in self.consumers: - if consumer is sys.stdout: - return level - return self.FATAL - - def level_matches(self, level, consumer_level): - """ - >>> l = Logger() - >>> l.level_matches(3, 4) - False - >>> l.level_matches(3, 2) - True - >>> l.level_matches(slice(None, 3), 3) - False - >>> l.level_matches(slice(None, 3), 2) - True - >>> l.level_matches(slice(1, 3), 1) - True - >>> l.level_matches(slice(2, 3), 1) - False - """ - if isinstance(level, slice): - start, stop = level.start, level.stop - if start is not None and start > consumer_level: - return False - if stop is not None or stop <= consumer_level: - return False - return True - else: - return level >= consumer_level - - @classmethod - def level_for_integer(cls, level): - levels = cls.LEVELS - if level < 0: - return levels[0] - if level >= len(levels): - return levels[-1] - return levels[level] - - def move_stdout_to_stderr(self): - to_remove = [] - to_add = [] - for consumer_level, consumer in self.consumers: - if consumer == sys.stdout: - to_remove.append((consumer_level, consumer)) - to_add.append((consumer_level, sys.stderr)) - for item in to_remove: - self.consumers.remove(item) - self.consumers.extend(to_add) - -logger = Logger() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/req.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/req.py deleted file mode 100644 index 444e7252b..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/req.py +++ /dev/null @@ -1,1432 +0,0 @@ -import sys -import os -import shutil -import re -import zipfile -import pkg_resources -import tempfile -import urlparse -import urllib2 -import urllib -import ConfigParser -from distutils.sysconfig import get_python_version -from email.FeedParser import FeedParser -from pip.locations import bin_py, running_under_virtualenv -from pip.exceptions import InstallationError, UninstallationError -from pip.vcs import vcs -from pip.log import logger -from pip.util import display_path, rmtree -from pip.util import ask, backup_dir -from pip.util import is_installable_dir, is_local, dist_is_local -from pip.util import renames, normalize_path, egg_link_path -from pip.util import make_path_relative -from pip import call_subprocess -from pip.backwardcompat import any, copytree -from pip.index import Link -from pip.locations import build_prefix -from pip.download import (get_file_content, is_url, url_to_path, - path_to_url, is_archive_file, - unpack_vcs_link, is_vcs_url, is_file_url, - unpack_file_url, unpack_http_url) - - -PIP_DELETE_MARKER_FILENAME = 'pip-delete-this-directory.txt' - - -class InstallRequirement(object): - - def __init__(self, req, comes_from, source_dir=None, editable=False, - url=None, update=True): - if isinstance(req, basestring): - req = pkg_resources.Requirement.parse(req) - self.req = req - self.comes_from = comes_from - self.source_dir = source_dir - self.editable = editable - self.url = url - self._egg_info_path = None - # This holds the pkg_resources.Distribution object if this requirement - # is already available: - self.satisfied_by = None - # This hold the pkg_resources.Distribution object if this requirement - # conflicts with another installed distribution: - self.conflicts_with = None - self._temp_build_dir = None - self._is_bundle = None - # True if the editable should be updated: - self.update = update - # Set to True after successful installation - self.install_succeeded = None - # UninstallPathSet of uninstalled distribution (for possible rollback) - self.uninstalled = None - - @classmethod - def from_editable(cls, editable_req, comes_from=None, default_vcs=None): - name, url = parse_editable(editable_req, default_vcs) - if url.startswith('file:'): - source_dir = url_to_path(url) - else: - source_dir = None - return cls(name, comes_from, source_dir=source_dir, editable=True, url=url) - - @classmethod - def from_line(cls, name, comes_from=None): - """Creates an InstallRequirement from a name, which might be a - requirement, directory containing 'setup.py', filename, or URL. - """ - url = None - name = name.strip() - req = name - path = os.path.normpath(os.path.abspath(name)) - - if is_url(name): - url = name - ## FIXME: I think getting the requirement here is a bad idea: - #req = get_requirement_from_url(url) - req = None - elif os.path.isdir(path) and (os.path.sep in name or name.startswith('.')): - if not is_installable_dir(path): - raise InstallationError("Directory %r is not installable. File 'setup.py' not found." - % name) - url = path_to_url(name) - #req = get_requirement_from_url(url) - req = None - elif is_archive_file(path): - if not os.path.isfile(path): - logger.warn('Requirement %r looks like a filename, but the file does not exist' - % name) - url = path_to_url(name) - #req = get_requirement_from_url(url) - req = None - return cls(req, comes_from, url=url) - - def __str__(self): - if self.req: - s = str(self.req) - if self.url: - s += ' from %s' % self.url - else: - s = self.url - if self.satisfied_by is not None: - s += ' in %s' % display_path(self.satisfied_by.location) - if self.comes_from: - if isinstance(self.comes_from, basestring): - comes_from = self.comes_from - else: - comes_from = self.comes_from.from_path() - if comes_from: - s += ' (from %s)' % comes_from - return s - - def from_path(self): - if self.req is None: - return None - s = str(self.req) - if self.comes_from: - if isinstance(self.comes_from, basestring): - comes_from = self.comes_from - else: - comes_from = self.comes_from.from_path() - if comes_from: - s += '->' + comes_from - return s - - def build_location(self, build_dir, unpack=True): - if self._temp_build_dir is not None: - return self._temp_build_dir - if self.req is None: - self._temp_build_dir = tempfile.mkdtemp('-build', 'pip-') - self._ideal_build_dir = build_dir - return self._temp_build_dir - if self.editable: - name = self.name.lower() - else: - name = self.name - # FIXME: Is there a better place to create the build_dir? (hg and bzr need this) - if not os.path.exists(build_dir): - _make_build_dir(build_dir) - return os.path.join(build_dir, name) - - def correct_build_location(self): - """If the build location was a temporary directory, this will move it - to a new more permanent location""" - if self.source_dir is not None: - return - assert self.req is not None - assert self._temp_build_dir - old_location = self._temp_build_dir - new_build_dir = self._ideal_build_dir - del self._ideal_build_dir - if self.editable: - name = self.name.lower() - else: - name = self.name - new_location = os.path.join(new_build_dir, name) - if not os.path.exists(new_build_dir): - logger.debug('Creating directory %s' % new_build_dir) - _make_build_dir(new_build_dir) - if os.path.exists(new_location): - raise InstallationError( - 'A package already exists in %s; please remove it to continue' - % display_path(new_location)) - logger.debug('Moving package %s from %s to new location %s' - % (self, display_path(old_location), display_path(new_location))) - shutil.move(old_location, new_location) - self._temp_build_dir = new_location - self.source_dir = new_location - self._egg_info_path = None - - @property - def name(self): - if self.req is None: - return None - return self.req.project_name - - @property - def url_name(self): - if self.req is None: - return None - return urllib.quote(self.req.unsafe_name) - - @property - def setup_py(self): - return os.path.join(self.source_dir, 'setup.py') - - def run_egg_info(self, force_root_egg_info=False): - assert self.source_dir - if self.name: - logger.notify('Running setup.py egg_info for package %s' % self.name) - else: - logger.notify('Running setup.py egg_info for package from %s' % self.url) - logger.indent += 2 - try: - script = self._run_setup_py - script = script.replace('__SETUP_PY__', repr(self.setup_py)) - script = script.replace('__PKG_NAME__', repr(self.name)) - # We can't put the .egg-info files at the root, because then the source code will be mistaken - # for an installed egg, causing problems - if self.editable or force_root_egg_info: - egg_base_option = [] - else: - egg_info_dir = os.path.join(self.source_dir, 'pip-egg-info') - if not os.path.exists(egg_info_dir): - os.makedirs(egg_info_dir) - egg_base_option = ['--egg-base', 'pip-egg-info'] - call_subprocess( - [sys.executable, '-c', script, 'egg_info'] + egg_base_option, - cwd=self.source_dir, filter_stdout=self._filter_install, show_stdout=False, - command_level=logger.VERBOSE_DEBUG, - command_desc='python setup.py egg_info') - finally: - logger.indent -= 2 - if not self.req: - self.req = pkg_resources.Requirement.parse(self.pkg_info()['Name']) - self.correct_build_location() - - ## FIXME: This is a lame hack, entirely for PasteScript which has - ## a self-provided entry point that causes this awkwardness - _run_setup_py = """ -__file__ = __SETUP_PY__ -from setuptools.command import egg_info -def replacement_run(self): - self.mkpath(self.egg_info) - installer = self.distribution.fetch_build_egg - for ep in egg_info.iter_entry_points('egg_info.writers'): - # require=False is the change we're making: - writer = ep.load(require=False) - if writer: - writer(self, ep.name, egg_info.os.path.join(self.egg_info,ep.name)) - self.find_sources() -egg_info.egg_info.run = replacement_run -execfile(__file__) -""" - - def egg_info_data(self, filename): - if self.satisfied_by is not None: - if not self.satisfied_by.has_metadata(filename): - return None - return self.satisfied_by.get_metadata(filename) - assert self.source_dir - filename = self.egg_info_path(filename) - if not os.path.exists(filename): - return None - fp = open(filename, 'r') - data = fp.read() - fp.close() - return data - - def egg_info_path(self, filename): - if self._egg_info_path is None: - if self.editable: - base = self.source_dir - else: - base = os.path.join(self.source_dir, 'pip-egg-info') - filenames = os.listdir(base) - if self.editable: - filenames = [] - for root, dirs, files in os.walk(base): - for dir in vcs.dirnames: - if dir in dirs: - dirs.remove(dir) - for dir in dirs: - # Don't search in anything that looks like a virtualenv environment - if (os.path.exists(os.path.join(root, dir, 'bin', 'python')) - or os.path.exists(os.path.join(root, dir, 'Scripts', 'Python.exe'))): - dirs.remove(dir) - # Also don't search through tests - if dir == 'test' or dir == 'tests': - dirs.remove(dir) - filenames.extend([os.path.join(root, dir) - for dir in dirs]) - filenames = [f for f in filenames if f.endswith('.egg-info')] - - if not filenames: - raise InstallationError('No files/directores in %s (from %s)' % (base, filename)) - assert filenames, "No files/directories in %s (from %s)" % (base, filename) - - # if we have more than one match, we pick the toplevel one. This can - # easily be the case if there is a dist folder which contains an - # extracted tarball for testing purposes. - if len(filenames) > 1: - filenames.sort(key=lambda x: x.count(os.path.sep) + - (os.path.altsep and - x.count(os.path.altsep) or 0)) - self._egg_info_path = os.path.join(base, filenames[0]) - return os.path.join(self._egg_info_path, filename) - - def egg_info_lines(self, filename): - data = self.egg_info_data(filename) - if not data: - return [] - result = [] - for line in data.splitlines(): - line = line.strip() - if not line or line.startswith('#'): - continue - result.append(line) - return result - - def pkg_info(self): - p = FeedParser() - data = self.egg_info_data('PKG-INFO') - if not data: - logger.warn('No PKG-INFO file found in %s' % display_path(self.egg_info_path('PKG-INFO'))) - p.feed(data or '') - return p.close() - - @property - def dependency_links(self): - return self.egg_info_lines('dependency_links.txt') - - _requirements_section_re = re.compile(r'\[(.*?)\]') - - def requirements(self, extras=()): - in_extra = None - for line in self.egg_info_lines('requires.txt'): - match = self._requirements_section_re.match(line) - if match: - in_extra = match.group(1) - continue - if in_extra and in_extra not in extras: - # Skip requirement for an extra we aren't requiring - continue - yield line - - @property - def absolute_versions(self): - for qualifier, version in self.req.specs: - if qualifier == '==': - yield version - - @property - def installed_version(self): - return self.pkg_info()['version'] - - def assert_source_matches_version(self): - assert self.source_dir - if self.comes_from is None: - # We don't check the versions of things explicitly installed. - # This makes, e.g., "pip Package==dev" possible - return - version = self.installed_version - if version not in self.req: - logger.fatal( - 'Source in %s has the version %s, which does not match the requirement %s' - % (display_path(self.source_dir), version, self)) - raise InstallationError( - 'Source in %s has version %s that conflicts with %s' - % (display_path(self.source_dir), version, self)) - else: - logger.debug('Source in %s has version %s, which satisfies requirement %s' - % (display_path(self.source_dir), version, self)) - - def update_editable(self, obtain=True): - if not self.url: - logger.info("Cannot update repository at %s; repository location is unknown" % self.source_dir) - return - assert self.editable - assert self.source_dir - if self.url.startswith('file:'): - # Static paths don't get updated - return - assert '+' in self.url, "bad url: %r" % self.url - if not self.update: - return - vc_type, url = self.url.split('+', 1) - backend = vcs.get_backend(vc_type) - if backend: - vcs_backend = backend(self.url) - if obtain: - vcs_backend.obtain(self.source_dir) - else: - vcs_backend.export(self.source_dir) - else: - assert 0, ( - 'Unexpected version control type (in %s): %s' - % (self.url, vc_type)) - - def uninstall(self, auto_confirm=False): - """ - Uninstall the distribution currently satisfying this requirement. - - Prompts before removing or modifying files unless - ``auto_confirm`` is True. - - Refuses to delete or modify files outside of ``sys.prefix`` - - thus uninstallation within a virtual environment can only - modify that virtual environment, even if the virtualenv is - linked to global site-packages. - - """ - if not self.check_if_exists(): - raise UninstallationError("Cannot uninstall requirement %s, not installed" % (self.name,)) - dist = self.satisfied_by or self.conflicts_with - - paths_to_remove = UninstallPathSet(dist) - - pip_egg_info_path = os.path.join(dist.location, - dist.egg_name()) + '.egg-info' - easy_install_egg = dist.egg_name() + '.egg' - develop_egg_link = egg_link_path(dist) - if os.path.exists(pip_egg_info_path): - # package installed by pip - paths_to_remove.add(pip_egg_info_path) - if dist.has_metadata('installed-files.txt'): - for installed_file in dist.get_metadata('installed-files.txt').splitlines(): - path = os.path.normpath(os.path.join(pip_egg_info_path, installed_file)) - paths_to_remove.add(path) - if dist.has_metadata('top_level.txt'): - if dist.has_metadata('namespace_packages.txt'): - namespaces = dist.get_metadata('namespace_packages.txt') - else: - namespaces = [] - for top_level_pkg in [p for p - in dist.get_metadata('top_level.txt').splitlines() - if p and p not in namespaces]: - path = os.path.join(dist.location, top_level_pkg) - paths_to_remove.add(path) - paths_to_remove.add(path + '.py') - paths_to_remove.add(path + '.pyc') - - elif dist.location.endswith(easy_install_egg): - # package installed by easy_install - paths_to_remove.add(dist.location) - easy_install_pth = os.path.join(os.path.dirname(dist.location), - 'easy-install.pth') - paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) - - elif os.path.isfile(develop_egg_link): - # develop egg - fh = open(develop_egg_link, 'r') - link_pointer = os.path.normcase(fh.readline().strip()) - fh.close() - assert (link_pointer == dist.location), 'Egg-link %s does not match installed location of %s (at %s)' % (link_pointer, self.name, dist.location) - paths_to_remove.add(develop_egg_link) - easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), - 'easy-install.pth') - paths_to_remove.add_pth(easy_install_pth, dist.location) - - # find distutils scripts= scripts - if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): - for script in dist.metadata_listdir('scripts'): - paths_to_remove.add(os.path.join(bin_py, script)) - if sys.platform == 'win32': - paths_to_remove.add(os.path.join(bin_py, script) + '.bat') - - # find console_scripts - if dist.has_metadata('entry_points.txt'): - config = ConfigParser.SafeConfigParser() - config.readfp(FakeFile(dist.get_metadata_lines('entry_points.txt'))) - if config.has_section('console_scripts'): - for name, value in config.items('console_scripts'): - paths_to_remove.add(os.path.join(bin_py, name)) - if sys.platform == 'win32': - paths_to_remove.add(os.path.join(bin_py, name) + '.exe') - paths_to_remove.add(os.path.join(bin_py, name) + '.exe.manifest') - paths_to_remove.add(os.path.join(bin_py, name) + '-script.py') - - paths_to_remove.remove(auto_confirm) - self.uninstalled = paths_to_remove - - def rollback_uninstall(self): - if self.uninstalled: - self.uninstalled.rollback() - else: - logger.error("Can't rollback %s, nothing uninstalled." - % (self.project_name,)) - - def commit_uninstall(self): - if self.uninstalled: - self.uninstalled.commit() - else: - logger.error("Can't commit %s, nothing uninstalled." - % (self.project_name,)) - - def archive(self, build_dir): - assert self.source_dir - create_archive = True - archive_name = '%s-%s.zip' % (self.name, self.installed_version) - archive_path = os.path.join(build_dir, archive_name) - if os.path.exists(archive_path): - response = ask('The file %s exists. (i)gnore, (w)ipe, (b)ackup ' - % display_path(archive_path), ('i', 'w', 'b')) - if response == 'i': - create_archive = False - elif response == 'w': - logger.warn('Deleting %s' % display_path(archive_path)) - os.remove(archive_path) - elif response == 'b': - dest_file = backup_dir(archive_path) - logger.warn('Backing up %s to %s' - % (display_path(archive_path), display_path(dest_file))) - shutil.move(archive_path, dest_file) - if create_archive: - zip = zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) - dir = os.path.normcase(os.path.abspath(self.source_dir)) - for dirpath, dirnames, filenames in os.walk(dir): - if 'pip-egg-info' in dirnames: - dirnames.remove('pip-egg-info') - for dirname in dirnames: - dirname = os.path.join(dirpath, dirname) - name = self._clean_zip_name(dirname, dir) - zipdir = zipfile.ZipInfo(self.name + '/' + name + '/') - zipdir.external_attr = 0755 << 16L - zip.writestr(zipdir, '') - for filename in filenames: - if filename == PIP_DELETE_MARKER_FILENAME: - continue - filename = os.path.join(dirpath, filename) - name = self._clean_zip_name(filename, dir) - zip.write(filename, self.name + '/' + name) - zip.close() - logger.indent -= 2 - logger.notify('Saved %s' % display_path(archive_path)) - - def _clean_zip_name(self, name, prefix): - assert name.startswith(prefix+os.path.sep), ( - "name %r doesn't start with prefix %r" % (name, prefix)) - name = name[len(prefix)+1:] - name = name.replace(os.path.sep, '/') - return name - - def install(self, install_options, global_options=()): - if self.editable: - self.install_editable(install_options, global_options) - return - temp_location = tempfile.mkdtemp('-record', 'pip-') - record_filename = os.path.join(temp_location, 'install-record.txt') - try: - - install_args = [ - sys.executable, '-c', - "import setuptools;__file__=%r;"\ - "execfile(__file__)" % self.setup_py] +\ - list(global_options) + [ - 'install', - '--single-version-externally-managed', - '--record', record_filename] - - if running_under_virtualenv(): - ## FIXME: I'm not sure if this is a reasonable location; probably not - ## but we can't put it in the default location, as that is a virtualenv symlink that isn't writable - install_args += ['--install-headers', - os.path.join(sys.prefix, 'include', 'site', - 'python' + get_python_version())] - logger.notify('Running setup.py install for %s' % self.name) - logger.indent += 2 - try: - call_subprocess(install_args + install_options, - cwd=self.source_dir, filter_stdout=self._filter_install, show_stdout=False) - finally: - logger.indent -= 2 - if not os.path.exists(record_filename): - logger.notify('Record file %s not found' % record_filename) - return - self.install_succeeded = True - f = open(record_filename) - for line in f: - line = line.strip() - if line.endswith('.egg-info'): - egg_info_dir = line - break - else: - logger.warn('Could not find .egg-info directory in install record for %s' % self) - ## FIXME: put the record somewhere - ## FIXME: should this be an error? - return - f.close() - new_lines = [] - f = open(record_filename) - for line in f: - filename = line.strip() - if os.path.isdir(filename): - filename += os.path.sep - new_lines.append(make_path_relative(filename, egg_info_dir)) - f.close() - f = open(os.path.join(egg_info_dir, 'installed-files.txt'), 'w') - f.write('\n'.join(new_lines)+'\n') - f.close() - finally: - if os.path.exists(record_filename): - os.remove(record_filename) - os.rmdir(temp_location) - - def remove_temporary_source(self): - """Remove the source files from this requirement, if they are marked - for deletion""" - if self.is_bundle or os.path.exists(self.delete_marker_filename): - logger.info('Removing source in %s' % self.source_dir) - if self.source_dir: - rmtree(self.source_dir) - self.source_dir = None - if self._temp_build_dir and os.path.exists(self._temp_build_dir): - rmtree(self._temp_build_dir) - self._temp_build_dir = None - - def install_editable(self, install_options, global_options=()): - logger.notify('Running setup.py develop for %s' % self.name) - logger.indent += 2 - try: - ## FIXME: should we do --install-headers here too? - call_subprocess( - [sys.executable, '-c', - "import setuptools; __file__=%r; execfile(%r)" % (self.setup_py, self.setup_py)] - + list(global_options) + ['develop', '--no-deps'] + list(install_options), - - cwd=self.source_dir, filter_stdout=self._filter_install, - show_stdout=False) - finally: - logger.indent -= 2 - self.install_succeeded = True - - def _filter_install(self, line): - level = logger.NOTIFY - for regex in [r'^running .*', r'^writing .*', '^creating .*', '^[Cc]opying .*', - r'^reading .*', r"^removing .*\.egg-info' \(and everything under it\)$", - r'^byte-compiling ', - # Not sure what this warning is, but it seems harmless: - r"^warning: manifest_maker: standard file '-c' not found$"]: - if re.search(regex, line.strip()): - level = logger.INFO - break - return (level, line) - - def check_if_exists(self): - """Find an installed distribution that satisfies or conflicts - with this requirement, and set self.satisfied_by or - self.conflicts_with appropriately.""" - if self.req is None: - return False - try: - self.satisfied_by = pkg_resources.get_distribution(self.req) - except pkg_resources.DistributionNotFound: - return False - except pkg_resources.VersionConflict: - self.conflicts_with = pkg_resources.get_distribution(self.req.project_name) - return True - - @property - def is_bundle(self): - if self._is_bundle is not None: - return self._is_bundle - base = self._temp_build_dir - if not base: - ## FIXME: this doesn't seem right: - return False - self._is_bundle = (os.path.exists(os.path.join(base, 'pip-manifest.txt')) - or os.path.exists(os.path.join(base, 'pyinstall-manifest.txt'))) - return self._is_bundle - - def bundle_requirements(self): - for dest_dir in self._bundle_editable_dirs: - package = os.path.basename(dest_dir) - ## FIXME: svnism: - for vcs_backend in vcs.backends: - url = rev = None - vcs_bundle_file = os.path.join( - dest_dir, vcs_backend.bundle_file) - if os.path.exists(vcs_bundle_file): - vc_type = vcs_backend.name - fp = open(vcs_bundle_file) - content = fp.read() - fp.close() - url, rev = vcs_backend().parse_vcs_bundle_file(content) - break - if url: - url = '%s+%s@%s' % (vc_type, url, rev) - else: - url = None - yield InstallRequirement( - package, self, editable=True, url=url, - update=False, source_dir=dest_dir) - for dest_dir in self._bundle_build_dirs: - package = os.path.basename(dest_dir) - yield InstallRequirement( - package, self, - source_dir=dest_dir) - - def move_bundle_files(self, dest_build_dir, dest_src_dir): - base = self._temp_build_dir - assert base - src_dir = os.path.join(base, 'src') - build_dir = os.path.join(base, 'build') - bundle_build_dirs = [] - bundle_editable_dirs = [] - for source_dir, dest_dir, dir_collection in [ - (src_dir, dest_src_dir, bundle_editable_dirs), - (build_dir, dest_build_dir, bundle_build_dirs)]: - if os.path.exists(source_dir): - for dirname in os.listdir(source_dir): - dest = os.path.join(dest_dir, dirname) - dir_collection.append(dest) - if os.path.exists(dest): - logger.warn('The directory %s (containing package %s) already exists; cannot move source from bundle %s' - % (dest, dirname, self)) - continue - if not os.path.exists(dest_dir): - logger.info('Creating directory %s' % dest_dir) - os.makedirs(dest_dir) - shutil.move(os.path.join(source_dir, dirname), dest) - if not os.listdir(source_dir): - os.rmdir(source_dir) - self._temp_build_dir = None - self._bundle_build_dirs = bundle_build_dirs - self._bundle_editable_dirs = bundle_editable_dirs - - @property - def delete_marker_filename(self): - assert self.source_dir - return os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME) - - -DELETE_MARKER_MESSAGE = '''\ -This file is placed here by pip to indicate the source was put -here by pip. - -Once this package is successfully installed this source code will be -deleted (unless you remove this file). -''' - - -class RequirementSet(object): - - def __init__(self, build_dir, src_dir, download_dir, download_cache=None, - upgrade=False, ignore_installed=False, - ignore_dependencies=False): - self.build_dir = build_dir - self.src_dir = src_dir - self.download_dir = download_dir - self.download_cache = download_cache - self.upgrade = upgrade - self.ignore_installed = ignore_installed - self.requirements = {} - # Mapping of alias: real_name - self.requirement_aliases = {} - self.unnamed_requirements = [] - self.ignore_dependencies = ignore_dependencies - self.successfully_downloaded = [] - self.successfully_installed = [] - self.reqs_to_cleanup = [] - - def __str__(self): - reqs = [req for req in self.requirements.values() - if not req.comes_from] - reqs.sort(key=lambda req: req.name.lower()) - return ' '.join([str(req.req) for req in reqs]) - - def add_requirement(self, install_req): - name = install_req.name - if not name: - self.unnamed_requirements.append(install_req) - else: - if self.has_requirement(name): - raise InstallationError( - 'Double requirement given: %s (aready in %s, name=%r)' - % (install_req, self.get_requirement(name), name)) - self.requirements[name] = install_req - ## FIXME: what about other normalizations? E.g., _ vs. -? - if name.lower() != name: - self.requirement_aliases[name.lower()] = name - - def has_requirement(self, project_name): - for name in project_name, project_name.lower(): - if name in self.requirements or name in self.requirement_aliases: - return True - return False - - @property - def has_requirements(self): - return self.requirements.values() or self.unnamed_requirements - - @property - def has_editables(self): - if any(req.editable for req in self.requirements.values()): - return True - if any(req.editable for req in self.unnamed_requirements): - return True - return False - - @property - def is_download(self): - if self.download_dir: - self.download_dir = os.path.expanduser(self.download_dir) - if os.path.exists(self.download_dir): - return True - else: - logger.fatal('Could not find download directory') - raise InstallationError( - "Could not find or access download directory '%s'" - % display_path(self.download_dir)) - return False - - def get_requirement(self, project_name): - for name in project_name, project_name.lower(): - if name in self.requirements: - return self.requirements[name] - if name in self.requirement_aliases: - return self.requirements[self.requirement_aliases[name]] - raise KeyError("No project with the name %r" % project_name) - - def uninstall(self, auto_confirm=False): - for req in self.requirements.values(): - req.uninstall(auto_confirm=auto_confirm) - req.commit_uninstall() - - def locate_files(self): - ## FIXME: duplicates code from install_files; relevant code should - ## probably be factored out into a separate method - unnamed = list(self.unnamed_requirements) - reqs = self.requirements.values() - while reqs or unnamed: - if unnamed: - req_to_install = unnamed.pop(0) - else: - req_to_install = reqs.pop(0) - install_needed = True - if not self.ignore_installed and not req_to_install.editable: - req_to_install.check_if_exists() - if req_to_install.satisfied_by: - if self.upgrade: - req_to_install.conflicts_with = req_to_install.satisfied_by - req_to_install.satisfied_by = None - else: - install_needed = False - if req_to_install.satisfied_by: - logger.notify('Requirement already satisfied ' - '(use --upgrade to upgrade): %s' - % req_to_install) - - if req_to_install.editable: - if req_to_install.source_dir is None: - req_to_install.source_dir = req_to_install.build_location(self.src_dir) - elif install_needed: - req_to_install.source_dir = req_to_install.build_location(self.build_dir, not self.is_download) - - if req_to_install.source_dir is not None and not os.path.isdir(req_to_install.source_dir): - raise InstallationError('Could not install requirement %s ' - 'because source folder %s does not exist ' - '(perhaps --no-download was used without first running ' - 'an equivalent install with --no-install?)' - % (req_to_install, req_to_install.source_dir)) - - def prepare_files(self, finder, force_root_egg_info=False, bundle=False): - """Prepare process. Create temp directories, download and/or unpack files.""" - unnamed = list(self.unnamed_requirements) - reqs = self.requirements.values() - while reqs or unnamed: - if unnamed: - req_to_install = unnamed.pop(0) - else: - req_to_install = reqs.pop(0) - install = True - if not self.ignore_installed and not req_to_install.editable: - req_to_install.check_if_exists() - if req_to_install.satisfied_by: - if self.upgrade: - req_to_install.conflicts_with = req_to_install.satisfied_by - req_to_install.satisfied_by = None - else: - install = False - if req_to_install.satisfied_by: - logger.notify('Requirement already satisfied ' - '(use --upgrade to upgrade): %s' - % req_to_install) - if req_to_install.editable: - logger.notify('Obtaining %s' % req_to_install) - elif install: - if req_to_install.url and req_to_install.url.lower().startswith('file:'): - logger.notify('Unpacking %s' % display_path(url_to_path(req_to_install.url))) - else: - logger.notify('Downloading/unpacking %s' % req_to_install) - logger.indent += 2 - try: - is_bundle = False - if req_to_install.editable: - if req_to_install.source_dir is None: - location = req_to_install.build_location(self.src_dir) - req_to_install.source_dir = location - else: - location = req_to_install.source_dir - if not os.path.exists(self.build_dir): - _make_build_dir(self.build_dir) - req_to_install.update_editable(not self.is_download) - if self.is_download: - req_to_install.run_egg_info() - req_to_install.archive(self.download_dir) - else: - req_to_install.run_egg_info() - elif install: - ##@@ if filesystem packages are not marked - ##editable in a req, a non deterministic error - ##occurs when the script attempts to unpack the - ##build directory - - location = req_to_install.build_location(self.build_dir, not self.is_download) - ## FIXME: is the existance of the checkout good enough to use it? I don't think so. - unpack = True - if not os.path.exists(os.path.join(location, 'setup.py')): - ## FIXME: this won't upgrade when there's an existing package unpacked in `location` - if req_to_install.url is None: - url = finder.find_requirement(req_to_install, upgrade=self.upgrade) - else: - ## FIXME: should req_to_install.url already be a link? - url = Link(req_to_install.url) - assert url - if url: - try: - self.unpack_url(url, location, self.is_download) - except urllib2.HTTPError, e: - logger.fatal('Could not install requirement %s because of error %s' - % (req_to_install, e)) - raise InstallationError( - 'Could not install requirement %s because of HTTP error %s for URL %s' - % (req_to_install, e, url)) - else: - unpack = False - if unpack: - is_bundle = req_to_install.is_bundle - url = None - if is_bundle: - req_to_install.move_bundle_files(self.build_dir, self.src_dir) - for subreq in req_to_install.bundle_requirements(): - reqs.append(subreq) - self.add_requirement(subreq) - elif self.is_download: - req_to_install.source_dir = location - if url and url.scheme in vcs.all_schemes: - req_to_install.run_egg_info() - req_to_install.archive(self.download_dir) - else: - req_to_install.source_dir = location - req_to_install.run_egg_info() - if force_root_egg_info: - # We need to run this to make sure that the .egg-info/ - # directory is created for packing in the bundle - req_to_install.run_egg_info(force_root_egg_info=True) - req_to_install.assert_source_matches_version() - #@@ sketchy way of identifying packages not grabbed from an index - if bundle and req_to_install.url: - self.copy_to_build_dir(req_to_install) - if not is_bundle and not self.is_download: - ## FIXME: shouldn't be globally added: - finder.add_dependency_links(req_to_install.dependency_links) - ## FIXME: add extras in here: - if not self.ignore_dependencies: - for req in req_to_install.requirements(): - try: - name = pkg_resources.Requirement.parse(req).project_name - except ValueError, e: - ## FIXME: proper warning - logger.error('Invalid requirement: %r (%s) in requirement %s' % (req, e, req_to_install)) - continue - if self.has_requirement(name): - ## FIXME: check for conflict - continue - subreq = InstallRequirement(req, req_to_install) - reqs.append(subreq) - self.add_requirement(subreq) - if req_to_install.name not in self.requirements: - self.requirements[req_to_install.name] = req_to_install - else: - self.reqs_to_cleanup.append(req_to_install) - if install: - self.successfully_downloaded.append(req_to_install) - if bundle and (req_to_install.url and req_to_install.url.startswith('file:///')): - self.copy_to_build_dir(req_to_install) - finally: - logger.indent -= 2 - - def cleanup_files(self, bundle=False): - """Clean up files, remove builds.""" - logger.notify('Cleaning up...') - logger.indent += 2 - for req in self.reqs_to_cleanup: - req.remove_temporary_source() - - remove_dir = [] - if self._pip_has_created_build_dir(): - remove_dir.append(self.build_dir) - - # The source dir of a bundle can always be removed. - if bundle: - remove_dir.append(self.src_dir) - - for dir in remove_dir: - if os.path.exists(dir): - logger.info('Removing temporary dir %s...' % dir) - rmtree(dir) - - logger.indent -= 2 - - def _pip_has_created_build_dir(self): - return (self.build_dir == build_prefix and - os.path.exists(os.path.join(self.build_dir, PIP_DELETE_MARKER_FILENAME))) - - def copy_to_build_dir(self, req_to_install): - target_dir = req_to_install.editable and self.src_dir or self.build_dir - logger.info("Copying %s to %s" %(req_to_install.name, target_dir)) - dest = os.path.join(target_dir, req_to_install.name) - copytree(req_to_install.source_dir, dest) - call_subprocess(["python", "%s/setup.py"%dest, "clean"]) - - def unpack_url(self, link, location, only_download=False): - if only_download: - location = self.download_dir - if is_vcs_url(link): - return unpack_vcs_link(link, location, only_download) - elif is_file_url(link): - return unpack_file_url(link, location) - else: - if self.download_cache: - self.download_cache = os.path.expanduser(self.download_cache) - return unpack_http_url(link, location, self.download_cache, only_download) - - def install(self, install_options, global_options=()): - """Install everything in this set (after having downloaded and unpacked the packages)""" - to_install = sorted([r for r in self.requirements.values() - if self.upgrade or not r.satisfied_by], - key=lambda p: p.name.lower()) - if to_install: - logger.notify('Installing collected packages: %s' % (', '.join([req.name for req in to_install]))) - logger.indent += 2 - try: - for requirement in to_install: - if requirement.conflicts_with: - logger.notify('Found existing installation: %s' - % requirement.conflicts_with) - logger.indent += 2 - try: - requirement.uninstall(auto_confirm=True) - finally: - logger.indent -= 2 - try: - requirement.install(install_options, global_options) - except: - # if install did not succeed, rollback previous uninstall - if requirement.conflicts_with and not requirement.install_succeeded: - requirement.rollback_uninstall() - raise - else: - if requirement.conflicts_with and requirement.install_succeeded: - requirement.commit_uninstall() - requirement.remove_temporary_source() - finally: - logger.indent -= 2 - self.successfully_installed = to_install - - def create_bundle(self, bundle_filename): - ## FIXME: can't decide which is better; zip is easier to read - ## random files from, but tar.bz2 is smaller and not as lame a - ## format. - - ## FIXME: this file should really include a manifest of the - ## packages, maybe some other metadata files. It would make - ## it easier to detect as well. - zip = zipfile.ZipFile(bundle_filename, 'w', zipfile.ZIP_DEFLATED) - vcs_dirs = [] - for dir, basename in (self.build_dir, 'build'), (self.src_dir, 'src'): - dir = os.path.normcase(os.path.abspath(dir)) - for dirpath, dirnames, filenames in os.walk(dir): - for backend in vcs.backends: - vcs_backend = backend() - vcs_url = vcs_rev = None - if vcs_backend.dirname in dirnames: - for vcs_dir in vcs_dirs: - if dirpath.startswith(vcs_dir): - # vcs bundle file already in parent directory - break - else: - vcs_url, vcs_rev = vcs_backend.get_info( - os.path.join(dir, dirpath)) - vcs_dirs.append(dirpath) - vcs_bundle_file = vcs_backend.bundle_file - vcs_guide = vcs_backend.guide % {'url': vcs_url, - 'rev': vcs_rev} - dirnames.remove(vcs_backend.dirname) - break - if 'pip-egg-info' in dirnames: - dirnames.remove('pip-egg-info') - for dirname in dirnames: - dirname = os.path.join(dirpath, dirname) - name = self._clean_zip_name(dirname, dir) - zip.writestr(basename + '/' + name + '/', '') - for filename in filenames: - if filename == PIP_DELETE_MARKER_FILENAME: - continue - filename = os.path.join(dirpath, filename) - name = self._clean_zip_name(filename, dir) - zip.write(filename, basename + '/' + name) - if vcs_url: - name = os.path.join(dirpath, vcs_bundle_file) - name = self._clean_zip_name(name, dir) - zip.writestr(basename + '/' + name, vcs_guide) - - zip.writestr('pip-manifest.txt', self.bundle_requirements()) - zip.close() - - BUNDLE_HEADER = '''\ -# This is a pip bundle file, that contains many source packages -# that can be installed as a group. You can install this like: -# pip this_file.zip -# The rest of the file contains a list of all the packages included: -''' - - def bundle_requirements(self): - parts = [self.BUNDLE_HEADER] - for req in sorted( - [req for req in self.requirements.values() - if not req.comes_from], - key=lambda x: x.name): - parts.append('%s==%s\n' % (req.name, req.installed_version)) - parts.append('# These packages were installed to satisfy the above requirements:\n') - for req in sorted( - [req for req in self.requirements.values() - if req.comes_from], - key=lambda x: x.name): - parts.append('%s==%s\n' % (req.name, req.installed_version)) - ## FIXME: should we do something with self.unnamed_requirements? - return ''.join(parts) - - def _clean_zip_name(self, name, prefix): - assert name.startswith(prefix+os.path.sep), ( - "name %r doesn't start with prefix %r" % (name, prefix)) - name = name[len(prefix)+1:] - name = name.replace(os.path.sep, '/') - return name - - -def _make_build_dir(build_dir): - os.makedirs(build_dir) - _write_delete_marker_message(os.path.join(build_dir, PIP_DELETE_MARKER_FILENAME)) - - -def _write_delete_marker_message(filepath): - marker_fp = open(filepath, 'w') - marker_fp.write(DELETE_MARKER_MESSAGE) - marker_fp.close() - - -_scheme_re = re.compile(r'^(http|https|file):', re.I) - - -def parse_requirements(filename, finder=None, comes_from=None, options=None): - skip_match = None - skip_regex = options.skip_requirements_regex - if skip_regex: - skip_match = re.compile(skip_regex) - filename, content = get_file_content(filename, comes_from=comes_from) - for line_number, line in enumerate(content.splitlines()): - line_number += 1 - line = line.strip() - if not line or line.startswith('#'): - continue - if skip_match and skip_match.search(line): - continue - if line.startswith('-r') or line.startswith('--requirement'): - if line.startswith('-r'): - req_url = line[2:].strip() - else: - req_url = line[len('--requirement'):].strip().strip('=') - if _scheme_re.search(filename): - # Relative to a URL - req_url = urlparse.urljoin(req_url, filename) - elif not _scheme_re.search(req_url): - req_url = os.path.join(os.path.dirname(filename), req_url) - for item in parse_requirements(req_url, finder, comes_from=filename, options=options): - yield item - elif line.startswith('-Z') or line.startswith('--always-unzip'): - # No longer used, but previously these were used in - # requirement files, so we'll ignore. - pass - elif line.startswith('-f') or line.startswith('--find-links'): - if line.startswith('-f'): - line = line[2:].strip() - else: - line = line[len('--find-links'):].strip().lstrip('=') - ## FIXME: it would be nice to keep track of the source of - ## the find_links: - if finder: - finder.find_links.append(line) - elif line.startswith('-i') or line.startswith('--index-url'): - if line.startswith('-i'): - line = line[2:].strip() - else: - line = line[len('--index-url'):].strip().lstrip('=') - if finder: - finder.index_urls = [line] - elif line.startswith('--extra-index-url'): - line = line[len('--extra-index-url'):].strip().lstrip('=') - if finder: - finder.index_urls.append(line) - else: - comes_from = '-r %s (line %s)' % (filename, line_number) - if line.startswith('-e') or line.startswith('--editable'): - if line.startswith('-e'): - line = line[2:].strip() - else: - line = line[len('--editable'):].strip() - req = InstallRequirement.from_editable( - line, comes_from=comes_from, default_vcs=options.default_vcs) - else: - req = InstallRequirement.from_line(line, comes_from) - yield req - - -def parse_editable(editable_req, default_vcs=None): - """Parses svn+http://blahblah@rev#egg=Foobar into a requirement - (Foobar) and a URL""" - url = editable_req - if os.path.isdir(url) and os.path.exists(os.path.join(url, 'setup.py')): - # Treating it as code that has already been checked out - url = path_to_url(url) - if url.lower().startswith('file:'): - return None, url - for version_control in vcs: - if url.lower().startswith('%s:' % version_control): - url = '%s+%s' % (version_control, url) - if '+' not in url: - if default_vcs: - url = default_vcs + '+' + url - else: - raise InstallationError( - '--editable=%s should be formatted with svn+URL, git+URL, hg+URL or bzr+URL' % editable_req) - vc_type = url.split('+', 1)[0].lower() - if not vcs.get_backend(vc_type): - raise InstallationError( - 'For --editable=%s only svn (svn+URL), Git (git+URL), Mercurial (hg+URL) and Bazaar (bzr+URL) is currently supported' % editable_req) - match = re.search(r'(?:#|#.*?&)egg=([^&]*)', editable_req) - if (not match or not match.group(1)) and vcs.get_backend(vc_type): - parts = [p for p in editable_req.split('#', 1)[0].split('/') if p] - if parts[-2] in ('tags', 'branches', 'tag', 'branch'): - req = parts[-3] - elif parts[-1] == 'trunk': - req = parts[-2] - else: - raise InstallationError( - '--editable=%s is not the right format; it must have #egg=Package' - % editable_req) - else: - req = match.group(1) - ## FIXME: use package_to_requirement? - match = re.search(r'^(.*?)(?:-dev|-\d.*)', req) - if match: - # Strip off -dev, -0.2, etc. - req = match.group(1) - return req, url - - -class UninstallPathSet(object): - """A set of file paths to be removed in the uninstallation of a - requirement.""" - def __init__(self, dist): - self.paths = set() - self._refuse = set() - self.pth = {} - self.dist = dist - self.save_dir = None - self._moved_paths = [] - - def _permitted(self, path): - """ - Return True if the given path is one we are permitted to - remove/modify, False otherwise. - - """ - return is_local(path) - - def _can_uninstall(self): - if not dist_is_local(self.dist): - logger.notify("Not uninstalling %s at %s, outside environment %s" - % (self.dist.project_name, normalize_path(self.dist.location), sys.prefix)) - return False - return True - - def add(self, path): - path = normalize_path(path) - if not os.path.exists(path): - return - if self._permitted(path): - self.paths.add(path) - else: - self._refuse.add(path) - - def add_pth(self, pth_file, entry): - pth_file = normalize_path(pth_file) - if self._permitted(pth_file): - if pth_file not in self.pth: - self.pth[pth_file] = UninstallPthEntries(pth_file) - self.pth[pth_file].add(entry) - else: - self._refuse.add(pth_file) - - def compact(self, paths): - """Compact a path set to contain the minimal number of paths - necessary to contain all paths in the set. If /a/path/ and - /a/path/to/a/file.txt are both in the set, leave only the - shorter path.""" - short_paths = set() - for path in sorted(paths, key=len): - if not any([(path.startswith(shortpath) and - path[len(shortpath.rstrip(os.path.sep))] == os.path.sep) - for shortpath in short_paths]): - short_paths.add(path) - return short_paths - - def _stash(self, path): - return os.path.join( - self.save_dir, os.path.splitdrive(path)[1].lstrip(os.path.sep)) - - def remove(self, auto_confirm=False): - """Remove paths in ``self.paths`` with confirmation (unless - ``auto_confirm`` is True).""" - if not self._can_uninstall(): - return - logger.notify('Uninstalling %s:' % self.dist.project_name) - logger.indent += 2 - paths = sorted(self.compact(self.paths)) - try: - if auto_confirm: - response = 'y' - else: - for path in paths: - logger.notify(path) - response = ask('Proceed (y/n)? ', ('y', 'n')) - if self._refuse: - logger.notify('Not removing or modifying (outside of prefix):') - for path in self.compact(self._refuse): - logger.notify(path) - if response == 'y': - self.save_dir = tempfile.mkdtemp(suffix='-uninstall', - prefix='pip-') - for path in paths: - new_path = self._stash(path) - logger.info('Removing file or directory %s' % path) - self._moved_paths.append(path) - renames(path, new_path) - for pth in self.pth.values(): - pth.remove() - logger.notify('Successfully uninstalled %s' % self.dist.project_name) - - finally: - logger.indent -= 2 - - def rollback(self): - """Rollback the changes previously made by remove().""" - if self.save_dir is None: - logger.error("Can't roll back %s; was not uninstalled" % self.dist.project_name) - return False - logger.notify('Rolling back uninstall of %s' % self.dist.project_name) - for path in self._moved_paths: - tmp_path = self._stash(path) - logger.info('Replacing %s' % path) - renames(tmp_path, path) - for pth in self.pth: - pth.rollback() - - def commit(self): - """Remove temporary save dir: rollback will no longer be possible.""" - if self.save_dir is not None: - shutil.rmtree(self.save_dir) - self.save_dir = None - self._moved_paths = [] - - -class UninstallPthEntries(object): - def __init__(self, pth_file): - if not os.path.isfile(pth_file): - raise UninstallationError("Cannot remove entries from nonexistent file %s" % pth_file) - self.file = pth_file - self.entries = set() - self._saved_lines = None - - def add(self, entry): - entry = os.path.normcase(entry) - # On Windows, os.path.normcase converts the entry to use - # backslashes. This is correct for entries that describe absolute - # paths outside of site-packages, but all the others use forward - # slashes. - if sys.platform == 'win32' and not os.path.splitdrive(entry)[0]: - entry = entry.replace('\\', '/') - self.entries.add(entry) - - def remove(self): - logger.info('Removing pth entries from %s:' % self.file) - fh = open(self.file, 'r') - lines = fh.readlines() - self._saved_lines = lines - fh.close() - try: - for entry in self.entries: - logger.info('Removing entry: %s' % entry) - try: - lines.remove(entry + '\n') - except ValueError: - pass - finally: - pass - fh = open(self.file, 'wb') - fh.writelines(lines) - fh.close() - - def rollback(self): - if self._saved_lines is None: - logger.error('Cannot roll back changes to %s, none were made' % self.file) - return False - logger.info('Rolling %s back to previous state' % self.file) - fh = open(self.file, 'wb') - fh.writelines(self._saved_lines) - fh.close() - return True - - -class FakeFile(object): - """Wrap a list of lines in an object with readline() to make - ConfigParser happy.""" - def __init__(self, lines): - self._gen = (l for l in lines) - - def readline(self): - try: - return self._gen.next() - except StopIteration: - return '' diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/runner.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/runner.py deleted file mode 100644 index be830ad9a..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/runner.py +++ /dev/null @@ -1,18 +0,0 @@ -import sys -import os - - -def run(): - base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - ## FIXME: this is kind of crude; if we could create a fake pip - ## module, then exec into it and update pip.__path__ properly, we - ## wouldn't have to update sys.path: - sys.path.insert(0, base) - import pip - return pip.main() - - -if __name__ == '__main__': - exit = run() - if exit: - sys.exit(exit) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/util.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/util.py deleted file mode 100644 index 1eab34c06..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/util.py +++ /dev/null @@ -1,479 +0,0 @@ -import sys -import shutil -import os -import stat -import re -import posixpath -import pkg_resources -import zipfile -import tarfile -from pip.exceptions import InstallationError -from pip.backwardcompat import WindowsError -from pip.locations import site_packages, running_under_virtualenv -from pip.log import logger - -__all__ = ['rmtree', 'display_path', 'backup_dir', - 'find_command', 'ask', 'Inf', - 'normalize_name', 'splitext', - 'format_size', 'is_installable_dir', - 'is_svn_page', 'file_contents', - 'split_leading_dir', 'has_leading_dir', - 'make_path_relative', 'normalize_path', - 'renames', 'get_terminal_size', - 'unzip_file', 'untar_file', 'create_download_cache_folder', - 'cache_download', 'unpack_file'] - - -def rmtree(dir): - shutil.rmtree(dir, ignore_errors=True, - onerror=rmtree_errorhandler) - - -def rmtree_errorhandler(func, path, exc_info): - """On Windows, the files in .svn are read-only, so when rmtree() tries to - remove them, an exception is thrown. We catch that here, remove the - read-only attribute, and hopefully continue without problems.""" - exctype, value = exc_info[:2] - # lookin for a windows error - if exctype is not WindowsError or 'Access is denied' not in str(value): - raise - # file type should currently be read only - if ((os.stat(path).st_mode & stat.S_IREAD) != stat.S_IREAD): - raise - # convert to read/write - os.chmod(path, stat.S_IWRITE) - # use the original function to repeat the operation - func(path) - - -def display_path(path): - """Gives the display value for a given path, making it relative to cwd - if possible.""" - path = os.path.normcase(os.path.abspath(path)) - if path.startswith(os.getcwd() + os.path.sep): - path = '.' + path[len(os.getcwd()):] - return path - - -def backup_dir(dir, ext='.bak'): - """Figure out the name of a directory to back up the given dir to - (adding .bak, .bak2, etc)""" - n = 1 - extension = ext - while os.path.exists(dir + extension): - n += 1 - extension = ext + str(n) - return dir + extension - - -def find_command(cmd, paths=None, pathext=None): - """Searches the PATH for the given command and returns its path""" - if paths is None: - paths = os.environ.get('PATH', []).split(os.pathsep) - if isinstance(paths, basestring): - paths = [paths] - # check if there are funny path extensions for executables, e.g. Windows - if pathext is None: - pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD') - pathext = [ext for ext in pathext.lower().split(os.pathsep)] - # don't use extensions if the command ends with one of them - if os.path.splitext(cmd)[1].lower() in pathext: - pathext = [''] - # check if we find the command on PATH - for path in paths: - # try without extension first - cmd_path = os.path.join(path, cmd) - for ext in pathext: - # then including the extension - cmd_path_ext = cmd_path + ext - if os.path.exists(cmd_path_ext): - return cmd_path_ext - if os.path.exists(cmd_path): - return cmd_path - return None - - -def ask(message, options): - """Ask the message interactively, with the given possible responses""" - while 1: - if os.environ.get('PIP_NO_INPUT'): - raise Exception('No input was expected ($PIP_NO_INPUT set); question: %s' % message) - response = raw_input(message) - response = response.strip().lower() - if response not in options: - print 'Your response (%r) was not one of the expected responses: %s' % ( - response, ', '.join(options)) - else: - return response - - -class _Inf(object): - """I am bigger than everything!""" - def __cmp__(self, a): - if self is a: - return 0 - return 1 - - def __repr__(self): - return 'Inf' - -Inf = _Inf() -del _Inf - - -_normalize_re = re.compile(r'[^a-z]', re.I) - - -def normalize_name(name): - return _normalize_re.sub('-', name.lower()) - - -def format_size(bytes): - if bytes > 1000*1000: - return '%.1fMb' % (bytes/1000.0/1000) - elif bytes > 10*1000: - return '%iKb' % (bytes/1000) - elif bytes > 1000: - return '%.1fKb' % (bytes/1000.0) - else: - return '%ibytes' % bytes - - -def is_installable_dir(path): - """Return True if `path` is a directory containing a setup.py file.""" - if not os.path.isdir(path): - return False - setup_py = os.path.join(path, 'setup.py') - if os.path.isfile(setup_py): - return True - return False - - -def is_svn_page(html): - """Returns true if the page appears to be the index page of an svn repository""" - return (re.search(r'<title>[^<]*Revision \d+:', html) - and re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I)) - - -def file_contents(filename): - fp = open(filename, 'rb') - try: - return fp.read() - finally: - fp.close() - - -def split_leading_dir(path): - path = str(path) - path = path.lstrip('/').lstrip('\\') - if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) - or '\\' not in path): - return path.split('/', 1) - elif '\\' in path: - return path.split('\\', 1) - else: - return path, '' - - -def has_leading_dir(paths): - """Returns true if all the paths have the same leading path name - (i.e., everything is in one subdirectory in an archive)""" - common_prefix = None - for path in paths: - prefix, rest = split_leading_dir(path) - if not prefix: - return False - elif common_prefix is None: - common_prefix = prefix - elif prefix != common_prefix: - return False - return True - - -def make_path_relative(path, rel_to): - """ - Make a filename relative, where the filename path, and it is - relative to rel_to - - >>> make_relative_path('/usr/share/something/a-file.pth', - ... '/usr/share/another-place/src/Directory') - '../../../something/a-file.pth' - >>> make_relative_path('/usr/share/something/a-file.pth', - ... '/home/user/src/Directory') - '../../../usr/share/something/a-file.pth' - >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/') - 'a-file.pth' - """ - path_filename = os.path.basename(path) - path = os.path.dirname(path) - path = os.path.normpath(os.path.abspath(path)) - rel_to = os.path.normpath(os.path.abspath(rel_to)) - path_parts = path.strip(os.path.sep).split(os.path.sep) - rel_to_parts = rel_to.strip(os.path.sep).split(os.path.sep) - while path_parts and rel_to_parts and path_parts[0] == rel_to_parts[0]: - path_parts.pop(0) - rel_to_parts.pop(0) - full_parts = ['..']*len(rel_to_parts) + path_parts + [path_filename] - if full_parts == ['']: - return '.' + os.path.sep - return os.path.sep.join(full_parts) - - -def normalize_path(path): - """ - Convert a path to its canonical, case-normalized, absolute version. - - """ - return os.path.normcase(os.path.realpath(path)) - - -def splitext(path): - """Like os.path.splitext, but take off .tar too""" - base, ext = posixpath.splitext(path) - if base.lower().endswith('.tar'): - ext = base[-4:] + ext - base = base[:-4] - return base, ext - - -def renames(old, new): - """Like os.renames(), but handles renaming across devices.""" - # Implementation borrowed from os.renames(). - head, tail = os.path.split(new) - if head and tail and not os.path.exists(head): - os.makedirs(head) - - shutil.move(old, new) - - head, tail = os.path.split(old) - if head and tail: - try: - os.removedirs(head) - except OSError: - pass - - -def is_local(path): - """ - Return True if path is within sys.prefix, if we're running in a virtualenv. - - If we're not in a virtualenv, all paths are considered "local." - - """ - if not running_under_virtualenv(): - return True - return normalize_path(path).startswith(normalize_path(sys.prefix)) - - -def dist_is_local(dist): - """ - Return True if given Distribution object is installed locally - (i.e. within current virtualenv). - - Always True if we're not in a virtualenv. - - """ - return is_local(dist_location(dist)) - - -def get_installed_distributions(local_only=True, skip=('setuptools', 'pip', 'python')): - """ - Return a list of installed Distribution objects. - - If ``local_only`` is True (default), only return installations - local to the current virtualenv, if in a virtualenv. - - ``skip`` argument is an iterable of lower-case project names to - ignore; defaults to ('setuptools', 'pip', 'python'). [FIXME also - skip virtualenv?] - - """ - if local_only: - local_test = dist_is_local - else: - local_test = lambda d: True - return [d for d in pkg_resources.working_set if local_test(d) and d.key not in skip] - - -def egg_link_path(dist): - """ - Return the path where we'd expect to find a .egg-link file for - this distribution. (There doesn't seem to be any metadata in the - Distribution object for a develop egg that points back to its - .egg-link and easy-install.pth files). - - This won't find a globally-installed develop egg if we're in a - virtualenv. - - """ - return os.path.join(site_packages, dist.project_name) + '.egg-link' - - -def dist_location(dist): - """ - Get the site-packages location of this distribution. Generally - this is dist.location, except in the case of develop-installed - packages, where dist.location is the source code location, and we - want to know where the egg-link file is. - - """ - egg_link = egg_link_path(dist) - if os.path.exists(egg_link): - return egg_link - return dist.location - - -def get_terminal_size(): - """Returns a tuple (x, y) representing the width(x) and the height(x) - in characters of the terminal window.""" - def ioctl_GWINSZ(fd): - try: - import fcntl - import termios - import struct - cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, - '1234')) - except: - return None - if cr == (0, 0): - return None - if cr == (0, 0): - return None - return cr - cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) - if not cr: - try: - fd = os.open(os.ctermid(), os.O_RDONLY) - cr = ioctl_GWINSZ(fd) - os.close(fd) - except: - pass - if not cr: - cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) - return int(cr[1]), int(cr[0]) - - -def unzip_file(filename, location, flatten=True): - """Unzip the file (zip file located at filename) to the destination - location""" - if not os.path.exists(location): - os.makedirs(location) - zipfp = open(filename, 'rb') - try: - zip = zipfile.ZipFile(zipfp) - leading = has_leading_dir(zip.namelist()) and flatten - for name in zip.namelist(): - data = zip.read(name) - fn = name - if leading: - fn = split_leading_dir(name)[1] - fn = os.path.join(location, fn) - dir = os.path.dirname(fn) - if not os.path.exists(dir): - os.makedirs(dir) - if fn.endswith('/') or fn.endswith('\\'): - # A directory - if not os.path.exists(fn): - os.makedirs(fn) - else: - fp = open(fn, 'wb') - try: - fp.write(data) - finally: - fp.close() - finally: - zipfp.close() - - -def untar_file(filename, location): - """Untar the file (tar file located at filename) to the destination location""" - if not os.path.exists(location): - os.makedirs(location) - if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): - mode = 'r:gz' - elif filename.lower().endswith('.bz2') or filename.lower().endswith('.tbz'): - mode = 'r:bz2' - elif filename.lower().endswith('.tar'): - mode = 'r' - else: - logger.warn('Cannot determine compression type for file %s' % filename) - mode = 'r:*' - tar = tarfile.open(filename, mode) - try: - # note: python<=2.5 doesnt seem to know about pax headers, filter them - leading = has_leading_dir([ - member.name for member in tar.getmembers() - if member.name != 'pax_global_header' - ]) - for member in tar.getmembers(): - fn = member.name - if fn == 'pax_global_header': - continue - if leading: - fn = split_leading_dir(fn)[1] - path = os.path.join(location, fn) - if member.isdir(): - if not os.path.exists(path): - os.makedirs(path) - else: - try: - fp = tar.extractfile(member) - except (KeyError, AttributeError), e: - # Some corrupt tar files seem to produce this - # (specifically bad symlinks) - logger.warn( - 'In the tar file %s the member %s is invalid: %s' - % (filename, member.name, e)) - continue - if not os.path.exists(os.path.dirname(path)): - os.makedirs(os.path.dirname(path)) - destfp = open(path, 'wb') - try: - shutil.copyfileobj(fp, destfp) - finally: - destfp.close() - fp.close() - finally: - tar.close() - - -def create_download_cache_folder(folder): - logger.indent -= 2 - logger.notify('Creating supposed download cache at %s' % folder) - logger.indent += 2 - os.makedirs(folder) - - -def cache_download(target_file, temp_location, content_type): - logger.notify('Storing download in cache at %s' % display_path(target_file)) - shutil.copyfile(temp_location, target_file) - fp = open(target_file+'.content-type', 'w') - fp.write(content_type) - fp.close() - os.unlink(temp_location) - - -def unpack_file(filename, location, content_type, link): - if (content_type == 'application/zip' - or filename.endswith('.zip') - or filename.endswith('.pybundle') - or zipfile.is_zipfile(filename)): - unzip_file(filename, location, flatten=not filename.endswith('.pybundle')) - elif (content_type == 'application/x-gzip' - or tarfile.is_tarfile(filename) - or splitext(filename)[1].lower() in ('.tar', '.tar.gz', '.tar.bz2', '.tgz', '.tbz')): - untar_file(filename, location) - elif (content_type and content_type.startswith('text/html') - and is_svn_page(file_contents(filename))): - # We don't really care about this - from pip.vcs.subversion import Subversion - Subversion('svn+' + link.url).unpack(location) - else: - ## FIXME: handle? - ## FIXME: magic signatures? - logger.fatal('Cannot unpack file %s (downloaded from %s, content-type: %s); cannot detect archive format' - % (filename, location, content_type)) - raise InstallationError('Cannot determine archive format of %s' % location) - - - diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/__init__.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/__init__.py deleted file mode 100644 index e110440c7..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/__init__.py +++ /dev/null @@ -1,238 +0,0 @@ -"""Handles all VCS (version control) support""" - -import os -import shutil -import urlparse -import urllib - -from pip.exceptions import BadCommand -from pip.log import logger -from pip.util import display_path, backup_dir, find_command, ask - - -__all__ = ['vcs', 'get_src_requirement', 'import_vcs_support'] - - -class VcsSupport(object): - _registry = {} - schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp'] - - def __init__(self): - # Register more schemes with urlparse for various version control systems - urlparse.uses_netloc.extend(self.schemes) - urlparse.uses_fragment.extend(self.schemes) - super(VcsSupport, self).__init__() - - def __iter__(self): - return self._registry.__iter__() - - @property - def backends(self): - return self._registry.values() - - @property - def dirnames(self): - return [backend.dirname for backend in self.backends] - - @property - def all_schemes(self): - schemes = [] - for backend in self.backends: - schemes.extend(backend.schemes) - return schemes - - def register(self, cls): - if not hasattr(cls, 'name'): - logger.warn('Cannot register VCS %s' % cls.__name__) - return - if cls.name not in self._registry: - self._registry[cls.name] = cls - - def unregister(self, cls=None, name=None): - if name in self._registry: - del self._registry[name] - elif cls in self._registry.values(): - del self._registry[cls.name] - else: - logger.warn('Cannot unregister because no class or name given') - - def get_backend_name(self, location): - """ - Return the name of the version control backend if found at given - location, e.g. vcs.get_backend_name('/path/to/vcs/checkout') - """ - for vc_type in self._registry.values(): - path = os.path.join(location, vc_type.dirname) - if os.path.exists(path): - return vc_type.name - return None - - def get_backend(self, name): - name = name.lower() - if name in self._registry: - return self._registry[name] - - def get_backend_from_location(self, location): - vc_type = self.get_backend_name(location) - if vc_type: - return self.get_backend(vc_type) - return None - - -vcs = VcsSupport() - - -class VersionControl(object): - name = '' - dirname = '' - - def __init__(self, url=None, *args, **kwargs): - self.url = url - self._cmd = None - super(VersionControl, self).__init__(*args, **kwargs) - - def _filter(self, line): - return (logger.INFO, line) - - def _is_local_repository(self, repo): - """ - posix absolute paths start with os.path.sep, - win32 ones ones start with drive (like c:\\folder) - """ - drive, tail = os.path.splitdrive(repo) - return repo.startswith(os.path.sep) or drive - - @property - def cmd(self): - if self._cmd is not None: - return self._cmd - command = find_command(self.name) - if command is None: - raise BadCommand('Cannot find command %r' % self.name) - logger.info('Found command %r at %r' % (self.name, command)) - self._cmd = command - return command - - def get_url_rev(self): - """ - Returns the correct repository URL and revision by parsing the given - repository URL - """ - url = self.url.split('+', 1)[1] - scheme, netloc, path, query, frag = urlparse.urlsplit(url) - rev = None - if '@' in path: - path, rev = path.rsplit('@', 1) - url = urlparse.urlunsplit((scheme, netloc, path, query, '')) - return url, rev - - def get_info(self, location): - """ - Returns (url, revision), where both are strings - """ - assert not location.rstrip('/').endswith(self.dirname), 'Bad directory: %s' % location - return self.get_url(location), self.get_revision(location) - - def normalize_url(self, url): - """ - Normalize a URL for comparison by unquoting it and removing any trailing slash. - """ - return urllib.unquote(url).rstrip('/') - - def compare_urls(self, url1, url2): - """ - Compare two repo URLs for identity, ignoring incidental differences. - """ - return (self.normalize_url(url1) == self.normalize_url(url2)) - - def parse_vcs_bundle_file(self, content): - """ - Takes the contents of the bundled text file that explains how to revert - the stripped off version control data of the given package and returns - the URL and revision of it. - """ - raise NotImplementedError - - def obtain(self, dest): - """ - Called when installing or updating an editable package, takes the - source path of the checkout. - """ - raise NotImplementedError - - def switch(self, dest, url, rev_options): - """ - Switch the repo at ``dest`` to point to ``URL``. - """ - raise NotImplemented - - def update(self, dest, rev_options): - """ - Update an already-existing repo to the given ``rev_options``. - """ - raise NotImplementedError - - def check_destination(self, dest, url, rev_options, rev_display): - """ - Prepare a location to receive a checkout/clone. - - Return True if the location is ready for (and requires) a - checkout/clone, False otherwise. - """ - checkout = True - prompt = False - if os.path.exists(dest): - checkout = False - if os.path.exists(os.path.join(dest, self.dirname)): - existing_url = self.get_url(dest) - if self.compare_urls(existing_url, url): - logger.info('%s in %s exists, and has correct URL (%s)' - % (self.repo_name.title(), display_path(dest), url)) - logger.notify('Updating %s %s%s' - % (display_path(dest), self.repo_name, rev_display)) - self.update(dest, rev_options) - else: - logger.warn('%s %s in %s exists with URL %s' - % (self.name, self.repo_name, display_path(dest), existing_url)) - prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', ('s', 'i', 'w', 'b')) - else: - logger.warn('Directory %s already exists, and is not a %s %s.' - % (dest, self.name, self.repo_name)) - prompt = ('(i)gnore, (w)ipe, (b)ackup ', ('i', 'w', 'b')) - if prompt: - logger.warn('The plan is to install the %s repository %s' - % (self.name, url)) - response = ask('What to do? %s' % prompt[0], prompt[1]) - - if response == 's': - logger.notify('Switching %s %s to %s%s' - % (self.repo_name, display_path(dest), url, rev_display)) - self.switch(dest, url, rev_options) - elif response == 'i': - # do nothing - pass - elif response == 'w': - logger.warn('Deleting %s' % display_path(dest)) - shutil.rmtree(dest) - checkout = True - elif response == 'b': - dest_dir = backup_dir(dest) - logger.warn('Backing up %s to %s' - % (display_path(dest), dest_dir)) - shutil.move(dest, dest_dir) - checkout = True - return checkout - - def unpack(self, location): - raise NotImplementedError - - def get_src_requirement(self, dist, location, find_tags=False): - raise NotImplementedError - - -def get_src_requirement(dist, location, find_tags): - version_control = vcs.get_backend_from_location(location) - if version_control: - return version_control().get_src_requirement(dist, location, find_tags) - logger.warn('cannot determine version of editable source in %s (is not SVN checkout, Git clone, Mercurial clone or Bazaar branch)' % location) - return dist.as_requirement() diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/bazaar.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/bazaar.py deleted file mode 100644 index 3b6ea8f04..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/bazaar.py +++ /dev/null @@ -1,138 +0,0 @@ -import os -import shutil -import tempfile -import re -from pip import call_subprocess -from pip.log import logger -from pip.util import rmtree, display_path -from pip.vcs import vcs, VersionControl -from pip.download import path_to_url2 - - -class Bazaar(VersionControl): - name = 'bzr' - dirname = '.bzr' - repo_name = 'branch' - bundle_file = 'bzr-branch.txt' - schemes = ('bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp') - guide = ('# This was a Bazaar branch; to make it a branch again run:\n' - 'bzr branch -r %(rev)s %(url)s .\n') - - def parse_vcs_bundle_file(self, content): - url = rev = None - for line in content.splitlines(): - if not line.strip() or line.strip().startswith('#'): - continue - match = re.search(r'^bzr\s*branch\s*-r\s*(\d*)', line) - if match: - rev = match.group(1).strip() - url = line[match.end():].strip().split(None, 1)[0] - if url and rev: - return url, rev - return None, None - - def unpack(self, location): - """Get the bzr branch at the url to the destination location""" - url, rev = self.get_url_rev() - logger.notify('Checking out bzr repository %s to %s' % (url, location)) - logger.indent += 2 - try: - if os.path.exists(location): - os.rmdir(location) - call_subprocess( - [self.cmd, 'branch', url, location], - filter_stdout=self._filter, show_stdout=False) - finally: - logger.indent -= 2 - - def export(self, location): - """Export the Bazaar repository at the url to the destination location""" - temp_dir = tempfile.mkdtemp('-export', 'pip-') - self.unpack(temp_dir) - if os.path.exists(location): - # Remove the location to make sure Bazaar can export it correctly - rmtree(location) - try: - call_subprocess([self.cmd, 'export', location], cwd=temp_dir, - filter_stdout=self._filter, show_stdout=False) - finally: - shutil.rmtree(temp_dir) - - def switch(self, dest, url, rev_options): - call_subprocess([self.cmd, 'switch', url], cwd=dest) - - def update(self, dest, rev_options): - call_subprocess( - [self.cmd, 'pull', '-q'] + rev_options, cwd=dest) - - def obtain(self, dest): - url, rev = self.get_url_rev() - if rev: - rev_options = ['-r', rev] - rev_display = ' (to revision %s)' % rev - else: - rev_options = [] - rev_display = '' - if self.check_destination(dest, url, rev_options, rev_display): - logger.notify('Checking out %s%s to %s' - % (url, rev_display, display_path(dest))) - call_subprocess( - [self.cmd, 'branch', '-q'] + rev_options + [url, dest]) - - def get_url_rev(self): - # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it - url, rev = super(Bazaar, self).get_url_rev() - if url.startswith('ssh://'): - url = 'bzr+' + url - return url, rev - - def get_url(self, location): - urls = call_subprocess( - [self.cmd, 'info'], show_stdout=False, cwd=location) - for line in urls.splitlines(): - line = line.strip() - for x in ('checkout of branch: ', - 'parent branch: '): - if line.startswith(x): - repo = line.split(x)[1] - if self._is_local_repository(repo): - return path_to_url2(repo) - return repo - return None - - def get_revision(self, location): - revision = call_subprocess( - [self.cmd, 'revno'], show_stdout=False, cwd=location) - return revision.splitlines()[-1] - - def get_tag_revs(self, location): - tags = call_subprocess( - [self.cmd, 'tags'], show_stdout=False, cwd=location) - tag_revs = [] - for line in tags.splitlines(): - tags_match = re.search(r'([.\w-]+)\s*(.*)$', line) - if tags_match: - tag = tags_match.group(1) - rev = tags_match.group(2) - tag_revs.append((rev.strip(), tag.strip())) - return dict(tag_revs) - - def get_src_requirement(self, dist, location, find_tags): - repo = self.get_url(location) - if not repo.lower().startswith('bzr:'): - repo = 'bzr+' + repo - egg_project_name = dist.egg_name().split('-', 1)[0] - if not repo: - return None - current_rev = self.get_revision(location) - tag_revs = self.get_tag_revs(location) - - if current_rev in tag_revs: - # It's a tag - full_egg_name = '%s-%s' % (egg_project_name, tag_revs[current_rev]) - else: - full_egg_name = '%s-dev_r%s' % (dist.egg_name(), current_rev) - return '%s@%s#egg=%s' % (repo, current_rev, full_egg_name) - - -vcs.register(Bazaar) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/git.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/git.py deleted file mode 100644 index 0701e49ec..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/git.py +++ /dev/null @@ -1,204 +0,0 @@ -import os -import shutil -import tempfile -import re -from pip import call_subprocess -from pip.util import display_path -from pip.vcs import vcs, VersionControl -from pip.log import logger -from urllib import url2pathname -from urlparse import urlsplit, urlunsplit - - -class Git(VersionControl): - name = 'git' - dirname = '.git' - repo_name = 'clone' - schemes = ('git', 'git+http', 'git+ssh', 'git+git', 'git+file') - bundle_file = 'git-clone.txt' - guide = ('# This was a Git repo; to make it a repo again run:\n' - 'git init\ngit remote add origin %(url)s -f\ngit checkout %(rev)s\n') - - def __init__(self, url=None, *args, **kwargs): - - # Works around an apparent Git bug - # (see http://article.gmane.org/gmane.comp.version-control.git/146500) - if url: - scheme, netloc, path, query, fragment = urlsplit(url) - if scheme.endswith('file'): - initial_slashes = path[:-len(path.lstrip('/'))] - newpath = initial_slashes + url2pathname(path).replace('\\', '/').lstrip('/') - url = urlunsplit((scheme, netloc, newpath, query, fragment)) - after_plus = scheme.find('+')+1 - url = scheme[:after_plus]+ urlunsplit((scheme[after_plus:], netloc, newpath, query, fragment)) - - super(Git, self).__init__(url, *args, **kwargs) - - def parse_vcs_bundle_file(self, content): - url = rev = None - for line in content.splitlines(): - if not line.strip() or line.strip().startswith('#'): - continue - url_match = re.search(r'git\s*remote\s*add\s*origin(.*)\s*-f', line) - if url_match: - url = url_match.group(1).strip() - rev_match = re.search(r'^git\s*checkout\s*-q\s*(.*)\s*', line) - if rev_match: - rev = rev_match.group(1).strip() - if url and rev: - return url, rev - return None, None - - def unpack(self, location): - """Clone the Git repository at the url to the destination location""" - url, rev = self.get_url_rev() - logger.notify('Cloning Git repository %s to %s' % (url, location)) - logger.indent += 2 - try: - if os.path.exists(location): - os.rmdir(location) - call_subprocess( - [self.cmd, 'clone', url, location], - filter_stdout=self._filter, show_stdout=False) - finally: - logger.indent -= 2 - - def export(self, location): - """Export the Git repository at the url to the destination location""" - temp_dir = tempfile.mkdtemp('-export', 'pip-') - self.unpack(temp_dir) - try: - if not location.endswith('/'): - location = location + '/' - call_subprocess( - [self.cmd, 'checkout-index', '-a', '-f', '--prefix', location], - filter_stdout=self._filter, show_stdout=False, cwd=temp_dir) - finally: - shutil.rmtree(temp_dir) - - def check_rev_options(self, rev, dest, rev_options): - """Check the revision options before checkout to compensate that tags - and branches may need origin/ as a prefix. - Returns the SHA1 of the branch or tag if found. - """ - revisions = self.get_tag_revs(dest) - revisions.update(self.get_branch_revs(dest)) - inverse_revisions = dict((v, k) for k, v in revisions.iteritems()) - # Check if rev is a branch name - origin_rev = 'origin/%s' % rev - if origin_rev in inverse_revisions: - return [inverse_revisions[origin_rev]] - elif rev in inverse_revisions: - return [inverse_revisions[rev]] - else: - logger.warn("Could not find a tag or branch '%s', assuming commit." % rev) - return rev_options - - def switch(self, dest, url, rev_options): - call_subprocess( - [self.cmd, 'config', 'remote.origin.url', url], cwd=dest) - call_subprocess( - [self.cmd, 'checkout', '-q'] + rev_options, cwd=dest) - - def update(self, dest, rev_options): - call_subprocess([self.cmd, 'pull', '-q'], cwd=dest) - call_subprocess( - [self.cmd, 'checkout', '-q', '-f'] + rev_options, cwd=dest) - - def obtain(self, dest): - url, rev = self.get_url_rev() - if rev: - rev_options = [rev] - rev_display = ' (to %s)' % rev - else: - rev_options = ['master'] - rev_display = '' - if self.check_destination(dest, url, rev_options, rev_display): - logger.notify('Cloning %s%s to %s' % (url, rev_display, display_path(dest))) - call_subprocess([self.cmd, 'clone', '-q', url, dest]) - if rev: - rev_options = self.check_rev_options(rev, dest, rev_options) - # Only do a checkout if rev_options differs from HEAD - if not self.get_revision(dest).startswith(rev_options[0]): - call_subprocess([self.cmd, 'checkout', '-q'] + rev_options, cwd=dest) - - def get_url(self, location): - url = call_subprocess( - [self.cmd, 'config', 'remote.origin.url'], - show_stdout=False, cwd=location) - return url.strip() - - def get_revision(self, location): - current_rev = call_subprocess( - [self.cmd, 'rev-parse', 'HEAD'], show_stdout=False, cwd=location) - return current_rev.strip() - - def get_tag_revs(self, location): - tags = call_subprocess( - [self.cmd, 'tag', '-l'], - show_stdout=False, raise_on_returncode=False, cwd=location) - tag_revs = [] - for line in tags.splitlines(): - tag = line.strip() - rev = call_subprocess( - [self.cmd, 'rev-parse', tag], show_stdout=False, cwd=location) - tag_revs.append((rev.strip(), tag)) - tag_revs = dict(tag_revs) - return tag_revs - - def get_branch_revs(self, location): - branches = call_subprocess( - [self.cmd, 'branch', '-r'], show_stdout=False, cwd=location) - branch_revs = [] - for line in branches.splitlines(): - line = line.split('->')[0].strip() - branch = "".join([b for b in line.split() if b != '*']) - rev = call_subprocess( - [self.cmd, 'rev-parse', branch], show_stdout=False, cwd=location) - branch_revs.append((rev.strip(), branch)) - branch_revs = dict(branch_revs) - return branch_revs - - def get_src_requirement(self, dist, location, find_tags): - repo = self.get_url(location) - if not repo.lower().startswith('git:'): - repo = 'git+' + repo - egg_project_name = dist.egg_name().split('-', 1)[0] - if not repo: - return None - current_rev = self.get_revision(location) - tag_revs = self.get_tag_revs(location) - branch_revs = self.get_branch_revs(location) - - if current_rev in tag_revs: - # It's a tag - full_egg_name = '%s-%s' % (egg_project_name, tag_revs[current_rev]) - elif (current_rev in branch_revs and - branch_revs[current_rev] != 'origin/master'): - # It's the head of a branch - full_egg_name = '%s-%s' % (dist.egg_name(), - branch_revs[current_rev].replace('origin/', '')) - else: - full_egg_name = '%s-dev' % dist.egg_name() - - return '%s@%s#egg=%s' % (repo, current_rev, full_egg_name) - - def get_url_rev(self): - """ - Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. - That's required because although they use SSH they sometimes doesn't - work with a ssh:// scheme (e.g. Github). But we need a scheme for - parsing. Hence we remove it again afterwards and return it as a stub. - """ - if not '://' in self.url: - assert not 'file:' in self.url - self.url = self.url.replace('git+', 'git+ssh://') - url, rev = super(Git, self).get_url_rev() - url = url.replace('ssh://', '') - else: - url, rev = super(Git, self).get_url_rev() - - return url, rev - - -vcs.register(Git) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/mercurial.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/mercurial.py deleted file mode 100644 index 70c8c833d..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/mercurial.py +++ /dev/null @@ -1,162 +0,0 @@ -import os -import shutil -import tempfile -import re -import ConfigParser -from pip import call_subprocess -from pip.util import display_path -from pip.log import logger -from pip.vcs import vcs, VersionControl -from pip.download import path_to_url2 - - -class Mercurial(VersionControl): - name = 'hg' - dirname = '.hg' - repo_name = 'clone' - schemes = ('hg', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http') - bundle_file = 'hg-clone.txt' - guide = ('# This was a Mercurial repo; to make it a repo again run:\n' - 'hg init\nhg pull %(url)s\nhg update -r %(rev)s\n') - - def parse_vcs_bundle_file(self, content): - url = rev = None - for line in content.splitlines(): - if not line.strip() or line.strip().startswith('#'): - continue - url_match = re.search(r'hg\s*pull\s*(.*)\s*', line) - if url_match: - url = url_match.group(1).strip() - rev_match = re.search(r'^hg\s*update\s*-r\s*(.*)\s*', line) - if rev_match: - rev = rev_match.group(1).strip() - if url and rev: - return url, rev - return None, None - - def unpack(self, location): - """Clone the Hg repository at the url to the destination location""" - url, rev = self.get_url_rev() - logger.notify('Cloning Mercurial repository %s to %s' % (url, location)) - logger.indent += 2 - try: - if os.path.exists(location): - os.rmdir(location) - call_subprocess( - [self.cmd, 'clone', url, location], - filter_stdout=self._filter, show_stdout=False) - finally: - logger.indent -= 2 - - def export(self, location): - """Export the Hg repository at the url to the destination location""" - temp_dir = tempfile.mkdtemp('-export', 'pip-') - self.unpack(temp_dir) - try: - call_subprocess( - [self.cmd, 'archive', location], - filter_stdout=self._filter, show_stdout=False, cwd=temp_dir) - finally: - shutil.rmtree(temp_dir) - - def switch(self, dest, url, rev_options): - repo_config = os.path.join(dest, self.dirname, 'hgrc') - config = ConfigParser.SafeConfigParser() - try: - config.read(repo_config) - config.set('paths', 'default', url) - config_file = open(repo_config, 'w') - config.write(config_file) - config_file.close() - except (OSError, ConfigParser.NoSectionError), e: - logger.warn( - 'Could not switch Mercurial repository to %s: %s' - % (url, e)) - else: - call_subprocess([self.cmd, 'update', '-q'] + rev_options, cwd=dest) - - def update(self, dest, rev_options): - call_subprocess([self.cmd, 'pull', '-q'], cwd=dest) - call_subprocess( - [self.cmd, 'update', '-q'] + rev_options, cwd=dest) - - def obtain(self, dest): - url, rev = self.get_url_rev() - if rev: - rev_options = [rev] - rev_display = ' (to revision %s)' % rev - else: - rev_options = [] - rev_display = '' - if self.check_destination(dest, url, rev_options, rev_display): - logger.notify('Cloning hg %s%s to %s' - % (url, rev_display, display_path(dest))) - call_subprocess([self.cmd, 'clone', '--noupdate', '-q', url, dest]) - call_subprocess([self.cmd, 'update', '-q'] + rev_options, cwd=dest) - - def get_url(self, location): - url = call_subprocess( - [self.cmd, 'showconfig', 'paths.default'], - show_stdout=False, cwd=location).strip() - if self._is_local_repository(url): - url = path_to_url2(url) - return url.strip() - - def get_tag_revs(self, location): - tags = call_subprocess( - [self.cmd, 'tags'], show_stdout=False, cwd=location) - tag_revs = [] - for line in tags.splitlines(): - tags_match = re.search(r'([\w\d\.-]+)\s*([\d]+):.*$', line) - if tags_match: - tag = tags_match.group(1) - rev = tags_match.group(2) - tag_revs.append((rev.strip(), tag.strip())) - return dict(tag_revs) - - def get_branch_revs(self, location): - branches = call_subprocess( - [self.cmd, 'branches'], show_stdout=False, cwd=location) - branch_revs = [] - for line in branches.splitlines(): - branches_match = re.search(r'([\w\d\.-]+)\s*([\d]+):.*$', line) - if branches_match: - branch = branches_match.group(1) - rev = branches_match.group(2) - branch_revs.append((rev.strip(), branch.strip())) - return dict(branch_revs) - - def get_revision(self, location): - current_revision = call_subprocess( - [self.cmd, 'parents', '--template={rev}'], - show_stdout=False, cwd=location).strip() - return current_revision - - def get_revision_hash(self, location): - current_rev_hash = call_subprocess( - [self.cmd, 'parents', '--template={node}'], - show_stdout=False, cwd=location).strip() - return current_rev_hash - - def get_src_requirement(self, dist, location, find_tags): - repo = self.get_url(location) - if not repo.lower().startswith('hg:'): - repo = 'hg+' + repo - egg_project_name = dist.egg_name().split('-', 1)[0] - if not repo: - return None - current_rev = self.get_revision(location) - current_rev_hash = self.get_revision_hash(location) - tag_revs = self.get_tag_revs(location) - branch_revs = self.get_branch_revs(location) - if current_rev in tag_revs: - # It's a tag - full_egg_name = '%s-%s' % (egg_project_name, tag_revs[current_rev]) - elif current_rev in branch_revs: - # It's the tip of a branch - full_egg_name = '%s-%s' % (dist.egg_name(), branch_revs[current_rev]) - else: - full_egg_name = '%s-dev' % dist.egg_name() - return '%s@%s#egg=%s' % (repo, current_rev_hash, full_egg_name) - -vcs.register(Mercurial) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/subversion.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/subversion.py deleted file mode 100644 index 85715d97a..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/vcs/subversion.py +++ /dev/null @@ -1,260 +0,0 @@ -import os -import re -from pip import call_subprocess -from pip.index import Link -from pip.util import rmtree, display_path -from pip.log import logger -from pip.vcs import vcs, VersionControl - -_svn_xml_url_re = re.compile('url="([^"]+)"') -_svn_rev_re = re.compile('committed-rev="(\d+)"') -_svn_url_re = re.compile(r'URL: (.+)') -_svn_revision_re = re.compile(r'Revision: (.+)') - - -class Subversion(VersionControl): - name = 'svn' - dirname = '.svn' - repo_name = 'checkout' - schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https') - bundle_file = 'svn-checkout.txt' - guide = ('# This was an svn checkout; to make it a checkout again run:\n' - 'svn checkout --force -r %(rev)s %(url)s .\n') - - def get_info(self, location): - """Returns (url, revision), where both are strings""" - assert not location.rstrip('/').endswith(self.dirname), 'Bad directory: %s' % location - output = call_subprocess( - [self.cmd, 'info', location], show_stdout=False, extra_environ={'LANG': 'C'}) - match = _svn_url_re.search(output) - if not match: - logger.warn('Cannot determine URL of svn checkout %s' % display_path(location)) - logger.info('Output that cannot be parsed: \n%s' % output) - return None, None - url = match.group(1).strip() - match = _svn_revision_re.search(output) - if not match: - logger.warn('Cannot determine revision of svn checkout %s' % display_path(location)) - logger.info('Output that cannot be parsed: \n%s' % output) - return url, None - return url, match.group(1) - - def parse_vcs_bundle_file(self, content): - for line in content.splitlines(): - if not line.strip() or line.strip().startswith('#'): - continue - match = re.search(r'^-r\s*([^ ])?', line) - if not match: - return None, None - rev = match.group(1) - rest = line[match.end():].strip().split(None, 1)[0] - return rest, rev - return None, None - - def unpack(self, location): - """Check out the svn repository at the url to the destination location""" - url, rev = self.get_url_rev() - logger.notify('Checking out svn repository %s to %s' % (url, location)) - logger.indent += 2 - try: - if os.path.exists(location): - # Subversion doesn't like to check out over an existing directory - # --force fixes this, but was only added in svn 1.5 - rmtree(location) - call_subprocess( - [self.cmd, 'checkout', url, location], - filter_stdout=self._filter, show_stdout=False) - finally: - logger.indent -= 2 - - def export(self, location): - """Export the svn repository at the url to the destination location""" - url, rev = self.get_url_rev() - logger.notify('Exporting svn repository %s to %s' % (url, location)) - logger.indent += 2 - try: - if os.path.exists(location): - # Subversion doesn't like to check out over an existing directory - # --force fixes this, but was only added in svn 1.5 - rmtree(location) - call_subprocess( - [self.cmd, 'export', url, location], - filter_stdout=self._filter, show_stdout=False) - finally: - logger.indent -= 2 - - def switch(self, dest, url, rev_options): - call_subprocess( - [self.cmd, 'switch'] + rev_options + [url, dest]) - - def update(self, dest, rev_options): - call_subprocess( - [self.cmd, 'update'] + rev_options + [dest]) - - def obtain(self, dest): - url, rev = self.get_url_rev() - if rev: - rev_options = ['-r', rev] - rev_display = ' (to revision %s)' % rev - else: - rev_options = [] - rev_display = '' - if self.check_destination(dest, url, rev_options, rev_display): - logger.notify('Checking out %s%s to %s' - % (url, rev_display, display_path(dest))) - call_subprocess( - [self.cmd, 'checkout', '-q'] + rev_options + [url, dest]) - - def get_location(self, dist, dependency_links): - for url in dependency_links: - egg_fragment = Link(url).egg_fragment - if not egg_fragment: - continue - if '-' in egg_fragment: - ## FIXME: will this work when a package has - in the name? - key = '-'.join(egg_fragment.split('-')[:-1]).lower() - else: - key = egg_fragment - if key == dist.key: - return url.split('#', 1)[0] - return None - - def get_revision(self, location): - """ - Return the maximum revision for all files under a given location - """ - # Note: taken from setuptools.command.egg_info - revision = 0 - - for base, dirs, files in os.walk(location): - if self.dirname not in dirs: - dirs[:] = [] - continue # no sense walking uncontrolled subdirs - dirs.remove(self.dirname) - entries_fn = os.path.join(base, self.dirname, 'entries') - if not os.path.exists(entries_fn): - ## FIXME: should we warn? - continue - f = open(entries_fn) - data = f.read() - f.close() - - if data.startswith('8') or data.startswith('9') or data.startswith('10'): - data = map(str.splitlines, data.split('\n\x0c\n')) - del data[0][0] # get rid of the '8' - dirurl = data[0][3] - revs = [int(d[9]) for d in data if len(d)>9 and d[9]]+[0] - if revs: - localrev = max(revs) - else: - localrev = 0 - elif data.startswith('<?xml'): - dirurl = _svn_xml_url_re.search(data).group(1) # get repository URL - revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)]+[0] - if revs: - localrev = max(revs) - else: - localrev = 0 - else: - logger.warn("Unrecognized .svn/entries format; skipping %s", base) - dirs[:] = [] - continue - if base == location: - base_url = dirurl+'/' # save the root url - elif not dirurl.startswith(base_url): - dirs[:] = [] - continue # not part of the same svn tree, skip it - revision = max(revision, localrev) - return revision - - def get_url_rev(self): - # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it - url, rev = super(Subversion, self).get_url_rev() - if url.startswith('ssh://'): - url = 'svn+' + url - return url, rev - - def get_url(self, location): - # In cases where the source is in a subdirectory, not alongside setup.py - # we have to look up in the location until we find a real setup.py - orig_location = location - while not os.path.exists(os.path.join(location, 'setup.py')): - last_location = location - location = os.path.dirname(location) - if location == last_location: - # We've traversed up to the root of the filesystem without finding setup.py - logger.warn("Could not find setup.py for directory %s (tried all parent directories)" - % orig_location) - return None - f = open(os.path.join(location, self.dirname, 'entries')) - data = f.read() - f.close() - if data.startswith('8') or data.startswith('9') or data.startswith('10'): - data = map(str.splitlines, data.split('\n\x0c\n')) - del data[0][0] # get rid of the '8' - return data[0][3] - elif data.startswith('<?xml'): - match = _svn_xml_url_re.search(data) - if not match: - raise ValueError('Badly formatted data: %r' % data) - return match.group(1) # get repository URL - else: - logger.warn("Unrecognized .svn/entries format in %s" % location) - # Or raise exception? - return None - - def get_tag_revs(self, svn_tag_url): - stdout = call_subprocess( - [self.cmd, 'ls', '-v', svn_tag_url], show_stdout=False) - results = [] - for line in stdout.splitlines(): - parts = line.split() - rev = int(parts[0]) - tag = parts[-1].strip('/') - results.append((tag, rev)) - return results - - def find_tag_match(self, rev, tag_revs): - best_match_rev = None - best_tag = None - for tag, tag_rev in tag_revs: - if (tag_rev > rev and - (best_match_rev is None or best_match_rev > tag_rev)): - # FIXME: Is best_match > tag_rev really possible? - # or is it a sign something is wacky? - best_match_rev = tag_rev - best_tag = tag - return best_tag - - def get_src_requirement(self, dist, location, find_tags=False): - repo = self.get_url(location) - if repo is None: - return None - parts = repo.split('/') - ## FIXME: why not project name? - egg_project_name = dist.egg_name().split('-', 1)[0] - rev = self.get_revision(location) - if parts[-2] in ('tags', 'tag'): - # It's a tag, perfect! - full_egg_name = '%s-%s' % (egg_project_name, parts[-1]) - elif parts[-2] in ('branches', 'branch'): - # It's a branch :( - full_egg_name = '%s-%s-r%s' % (dist.egg_name(), parts[-1], rev) - elif parts[-1] == 'trunk': - # Trunk :-/ - full_egg_name = '%s-dev_r%s' % (dist.egg_name(), rev) - if find_tags: - tag_url = '/'.join(parts[:-1]) + '/tags' - tag_revs = self.get_tag_revs(tag_url) - match = self.find_tag_match(rev, tag_revs) - if match: - logger.notify('trunk checkout %s seems to be equivalent to tag %s' % match) - repo = '%s/%s' % (tag_url, match) - full_egg_name = '%s-%s' % (egg_project_name, match) - else: - # Don't know what it is - logger.warn('svn URL does not fit normal structure (tags/branches/trunk): %s' % repo) - full_egg_name = '%s-dev_r%s' % (egg_project_name, rev) - return 'svn+%s@%s#egg=%s' % (repo, rev, full_egg_name) - -vcs.register(Subversion) diff --git a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/venv.py b/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/venv.py deleted file mode 100644 index 708abb05b..000000000 --- a/test/lib/python2.6/site-packages/pip-0.8.1-py2.6.egg/pip/venv.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Tools for working with virtualenv environments""" - -import os -import sys -import subprocess -from pip.exceptions import BadCommand -from pip.log import logger - - -def restart_in_venv(venv, base, site_packages, args): - """ - Restart this script using the interpreter in the given virtual environment - """ - if base and not os.path.isabs(venv) and not venv.startswith('~'): - base = os.path.expanduser(base) - # ensure we have an abs basepath at this point: - # a relative one makes no sense (or does it?) - if os.path.isabs(base): - venv = os.path.join(base, venv) - - if venv.startswith('~'): - venv = os.path.expanduser(venv) - - if not os.path.exists(venv): - try: - import virtualenv - except ImportError: - print 'The virtual environment does not exist: %s' % venv - print 'and virtualenv is not installed, so a new environment cannot be created' - sys.exit(3) - print 'Creating new virtualenv environment in %s' % venv - virtualenv.logger = logger - logger.indent += 2 - virtualenv.create_environment(venv, site_packages=site_packages) - if sys.platform == 'win32': - python = os.path.join(venv, 'Scripts', 'python.exe') - # check for bin directory which is used in buildouts - if not os.path.exists(python): - python = os.path.join(venv, 'bin', 'python.exe') - else: - python = os.path.join(venv, 'bin', 'python') - if not os.path.exists(python): - python = venv - if not os.path.exists(python): - raise BadCommand('Cannot find virtual environment interpreter at %s' % python) - base = os.path.dirname(os.path.dirname(python)) - file = os.path.join(os.path.dirname(__file__), 'runner.py') - if file.endswith('.pyc'): - file = file[:-1] - proc = subprocess.Popen( - [python, file] + args + [base, '___VENV_RESTART___']) - proc.wait() - sys.exit(proc.returncode) diff --git a/test/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg b/test/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg deleted file mode 100644 index 3c72d15b5..000000000 Binary files a/test/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg and /dev/null differ diff --git a/test/lib/python2.6/site-packages/setuptools.pth b/test/lib/python2.6/site-packages/setuptools.pth deleted file mode 100644 index bf7602056..000000000 --- a/test/lib/python2.6/site-packages/setuptools.pth +++ /dev/null @@ -1 +0,0 @@ -./setuptools-0.6c11-py2.6.egg diff --git a/test/lib/python2.6/site.py b/test/lib/python2.6/site.py deleted file mode 100644 index a49cfc349..000000000 --- a/test/lib/python2.6/site.py +++ /dev/null @@ -1,713 +0,0 @@ -"""Append module search paths for third-party packages to sys.path. - -**************************************************************** -* This module is automatically imported during initialization. * -**************************************************************** - -In earlier versions of Python (up to 1.5a3), scripts or modules that -needed to use site-specific modules would place ``import site'' -somewhere near the top of their code. Because of the automatic -import, this is no longer necessary (but code that does it still -works). - -This will append site-specific paths to the module search path. On -Unix, it starts with sys.prefix and sys.exec_prefix (if different) and -appends lib/python<version>/site-packages as well as lib/site-python. -It also supports the Debian convention of -lib/python<version>/dist-packages. On other platforms (mainly Mac and -Windows), it uses just sys.prefix (and sys.exec_prefix, if different, -but this is unlikely). The resulting directories, if they exist, are -appended to sys.path, and also inspected for path configuration files. - -FOR DEBIAN, this sys.path is augmented with directories in /usr/local. -Local addons go into /usr/local/lib/python<version>/site-packages -(resp. /usr/local/lib/site-python), Debian addons install into -/usr/{lib,share}/python<version>/dist-packages. - -A path configuration file is a file whose name has the form -<package>.pth; its contents are additional directories (one per line) -to be added to sys.path. Non-existing directories (or -non-directories) are never added to sys.path; no directory is added to -sys.path more than once. Blank lines and lines beginning with -'#' are skipped. Lines starting with 'import' are executed. - -For example, suppose sys.prefix and sys.exec_prefix are set to -/usr/local and there is a directory /usr/local/lib/python2.X/site-packages -with three subdirectories, foo, bar and spam, and two path -configuration files, foo.pth and bar.pth. Assume foo.pth contains the -following: - - # foo package configuration - foo - bar - bletch - -and bar.pth contains: - - # bar package configuration - bar - -Then the following directories are added to sys.path, in this order: - - /usr/local/lib/python2.X/site-packages/bar - /usr/local/lib/python2.X/site-packages/foo - -Note that bletch is omitted because it doesn't exist; bar precedes foo -because bar.pth comes alphabetically before foo.pth; and spam is -omitted because it is not mentioned in either path configuration file. - -After these path manipulations, an attempt is made to import a module -named sitecustomize, which can perform arbitrary additional -site-specific customizations. If this import fails with an -ImportError exception, it is silently ignored. - -""" - -import sys -import os -import __builtin__ -try: - set -except NameError: - from sets import Set as set - -# Prefixes for site-packages; add additional prefixes like /usr/local here -PREFIXES = [sys.prefix, sys.exec_prefix] -# Enable per user site-packages directory -# set it to False to disable the feature or True to force the feature -ENABLE_USER_SITE = None -# for distutils.commands.install -USER_SITE = None -USER_BASE = None - -_is_pypy = hasattr(sys, 'pypy_version_info') -_is_jython = sys.platform[:4] == 'java' -if _is_jython: - ModuleType = type(os) - -def makepath(*paths): - dir = os.path.join(*paths) - if _is_jython and (dir == '__classpath__' or - dir.startswith('__pyclasspath__')): - return dir, dir - dir = os.path.abspath(dir) - return dir, os.path.normcase(dir) - -def abs__file__(): - """Set all module' __file__ attribute to an absolute path""" - for m in sys.modules.values(): - if ((_is_jython and not isinstance(m, ModuleType)) or - hasattr(m, '__loader__')): - # only modules need the abspath in Jython. and don't mess - # with a PEP 302-supplied __file__ - continue - f = getattr(m, '__file__', None) - if f is None: - continue - m.__file__ = os.path.abspath(f) - -def removeduppaths(): - """ Remove duplicate entries from sys.path along with making them - absolute""" - # This ensures that the initial path provided by the interpreter contains - # only absolute pathnames, even if we're running from the build directory. - L = [] - known_paths = set() - for dir in sys.path: - # Filter out duplicate paths (on case-insensitive file systems also - # if they only differ in case); turn relative paths into absolute - # paths. - dir, dircase = makepath(dir) - if not dircase in known_paths: - L.append(dir) - known_paths.add(dircase) - sys.path[:] = L - return known_paths - -# XXX This should not be part of site.py, since it is needed even when -# using the -S option for Python. See http://www.python.org/sf/586680 -def addbuilddir(): - """Append ./build/lib.<platform> in case we're running in the build dir - (especially for Guido :-)""" - from distutils.util import get_platform - s = "build/lib.%s-%.3s" % (get_platform(), sys.version) - if hasattr(sys, 'gettotalrefcount'): - s += '-pydebug' - s = os.path.join(os.path.dirname(sys.path[-1]), s) - sys.path.append(s) - -def _init_pathinfo(): - """Return a set containing all existing directory entries from sys.path""" - d = set() - for dir in sys.path: - try: - if os.path.isdir(dir): - dir, dircase = makepath(dir) - d.add(dircase) - except TypeError: - continue - return d - -def addpackage(sitedir, name, known_paths): - """Add a new path to known_paths by combining sitedir and 'name' or execute - sitedir if it starts with 'import'""" - if known_paths is None: - _init_pathinfo() - reset = 1 - else: - reset = 0 - fullname = os.path.join(sitedir, name) - try: - f = open(fullname, "rU") - except IOError: - return - try: - for line in f: - if line.startswith("#"): - continue - if line.startswith("import"): - exec line - continue - line = line.rstrip() - dir, dircase = makepath(sitedir, line) - if not dircase in known_paths and os.path.exists(dir): - sys.path.append(dir) - known_paths.add(dircase) - finally: - f.close() - if reset: - known_paths = None - return known_paths - -def addsitedir(sitedir, known_paths=None): - """Add 'sitedir' argument to sys.path if missing and handle .pth files in - 'sitedir'""" - if known_paths is None: - known_paths = _init_pathinfo() - reset = 1 - else: - reset = 0 - sitedir, sitedircase = makepath(sitedir) - if not sitedircase in known_paths: - sys.path.append(sitedir) # Add path component - try: - names = os.listdir(sitedir) - except os.error: - return - names.sort() - for name in names: - if name.endswith(os.extsep + "pth"): - addpackage(sitedir, name, known_paths) - if reset: - known_paths = None - return known_paths - -def addsitepackages(known_paths, sys_prefix=sys.prefix, exec_prefix=sys.exec_prefix): - """Add site-packages (and possibly site-python) to sys.path""" - prefixes = [os.path.join(sys_prefix, "local"), sys_prefix] - if exec_prefix != sys_prefix: - prefixes.append(os.path.join(exec_prefix, "local")) - - for prefix in prefixes: - if prefix: - if sys.platform in ('os2emx', 'riscos') or _is_jython: - sitedirs = [os.path.join(prefix, "Lib", "site-packages")] - elif _is_pypy: - sitedirs = [os.path.join(prefix, 'site-packages')] - elif sys.platform == 'darwin' and prefix == sys_prefix: - - if prefix.startswith("/System/Library/Frameworks/"): # Apple's Python - - sitedirs = [os.path.join("/Library/Python", sys.version[:3], "site-packages"), - os.path.join(prefix, "Extras", "lib", "python")] - - else: # any other Python distros on OSX work this way - sitedirs = [os.path.join(prefix, "lib", - "python" + sys.version[:3], "site-packages")] - - elif os.sep == '/': - sitedirs = [os.path.join(prefix, - "lib", - "python" + sys.version[:3], - "site-packages"), - os.path.join(prefix, "lib", "site-python"), - os.path.join(prefix, "python" + sys.version[:3], "lib-dynload")] - lib64_dir = os.path.join(prefix, "lib64", "python" + sys.version[:3], "site-packages") - if (os.path.exists(lib64_dir) and - os.path.realpath(lib64_dir) not in [os.path.realpath(p) for p in sitedirs]): - sitedirs.append(lib64_dir) - try: - # sys.getobjects only available in --with-pydebug build - sys.getobjects - sitedirs.insert(0, os.path.join(sitedirs[0], 'debug')) - except AttributeError: - pass - # Debian-specific dist-packages directories: - sitedirs.append(os.path.join(prefix, "lib", - "python" + sys.version[:3], - "dist-packages")) - sitedirs.append(os.path.join(prefix, "local/lib", - "python" + sys.version[:3], - "dist-packages")) - sitedirs.append(os.path.join(prefix, "lib", "dist-python")) - else: - sitedirs = [prefix, os.path.join(prefix, "lib", "site-packages")] - if sys.platform == 'darwin': - # for framework builds *only* we add the standard Apple - # locations. Currently only per-user, but /Library and - # /Network/Library could be added too - if 'Python.framework' in prefix: - home = os.environ.get('HOME') - if home: - sitedirs.append( - os.path.join(home, - 'Library', - 'Python', - sys.version[:3], - 'site-packages')) - for sitedir in sitedirs: - if os.path.isdir(sitedir): - addsitedir(sitedir, known_paths) - return None - -def check_enableusersite(): - """Check if user site directory is safe for inclusion - - The function tests for the command line flag (including environment var), - process uid/gid equal to effective uid/gid. - - None: Disabled for security reasons - False: Disabled by user (command line option) - True: Safe and enabled - """ - if hasattr(sys, 'flags') and getattr(sys.flags, 'no_user_site', False): - return False - - if hasattr(os, "getuid") and hasattr(os, "geteuid"): - # check process uid == effective uid - if os.geteuid() != os.getuid(): - return None - if hasattr(os, "getgid") and hasattr(os, "getegid"): - # check process gid == effective gid - if os.getegid() != os.getgid(): - return None - - return True - -def addusersitepackages(known_paths): - """Add a per user site-package to sys.path - - Each user has its own python directory with site-packages in the - home directory. - - USER_BASE is the root directory for all Python versions - - USER_SITE is the user specific site-packages directory - - USER_SITE/.. can be used for data. - """ - global USER_BASE, USER_SITE, ENABLE_USER_SITE - env_base = os.environ.get("PYTHONUSERBASE", None) - - def joinuser(*args): - return os.path.expanduser(os.path.join(*args)) - - #if sys.platform in ('os2emx', 'riscos'): - # # Don't know what to put here - # USER_BASE = '' - # USER_SITE = '' - if os.name == "nt": - base = os.environ.get("APPDATA") or "~" - if env_base: - USER_BASE = env_base - else: - USER_BASE = joinuser(base, "Python") - USER_SITE = os.path.join(USER_BASE, - "Python" + sys.version[0] + sys.version[2], - "site-packages") - else: - if env_base: - USER_BASE = env_base - else: - USER_BASE = joinuser("~", ".local") - USER_SITE = os.path.join(USER_BASE, "lib", - "python" + sys.version[:3], - "site-packages") - - if ENABLE_USER_SITE and os.path.isdir(USER_SITE): - addsitedir(USER_SITE, known_paths) - if ENABLE_USER_SITE: - for dist_libdir in ("lib", "local/lib"): - user_site = os.path.join(USER_BASE, dist_libdir, - "python" + sys.version[:3], - "dist-packages") - if os.path.isdir(user_site): - addsitedir(user_site, known_paths) - return known_paths - - - -def setBEGINLIBPATH(): - """The OS/2 EMX port has optional extension modules that do double duty - as DLLs (and must use the .DLL file extension) for other extensions. - The library search path needs to be amended so these will be found - during module import. Use BEGINLIBPATH so that these are at the start - of the library search path. - - """ - dllpath = os.path.join(sys.prefix, "Lib", "lib-dynload") - libpath = os.environ['BEGINLIBPATH'].split(';') - if libpath[-1]: - libpath.append(dllpath) - else: - libpath[-1] = dllpath - os.environ['BEGINLIBPATH'] = ';'.join(libpath) - - -def setquit(): - """Define new built-ins 'quit' and 'exit'. - These are simply strings that display a hint on how to exit. - - """ - if os.sep == ':': - eof = 'Cmd-Q' - elif os.sep == '\\': - eof = 'Ctrl-Z plus Return' - else: - eof = 'Ctrl-D (i.e. EOF)' - - class Quitter(object): - def __init__(self, name): - self.name = name - def __repr__(self): - return 'Use %s() or %s to exit' % (self.name, eof) - def __call__(self, code=None): - # Shells like IDLE catch the SystemExit, but listen when their - # stdin wrapper is closed. - try: - sys.stdin.close() - except: - pass - raise SystemExit(code) - __builtin__.quit = Quitter('quit') - __builtin__.exit = Quitter('exit') - - -class _Printer(object): - """interactive prompt objects for printing the license text, a list of - contributors and the copyright notice.""" - - MAXLINES = 23 - - def __init__(self, name, data, files=(), dirs=()): - self.__name = name - self.__data = data - self.__files = files - self.__dirs = dirs - self.__lines = None - - def __setup(self): - if self.__lines: - return - data = None - for dir in self.__dirs: - for filename in self.__files: - filename = os.path.join(dir, filename) - try: - fp = file(filename, "rU") - data = fp.read() - fp.close() - break - except IOError: - pass - if data: - break - if not data: - data = self.__data - self.__lines = data.split('\n') - self.__linecnt = len(self.__lines) - - def __repr__(self): - self.__setup() - if len(self.__lines) <= self.MAXLINES: - return "\n".join(self.__lines) - else: - return "Type %s() to see the full %s text" % ((self.__name,)*2) - - def __call__(self): - self.__setup() - prompt = 'Hit Return for more, or q (and Return) to quit: ' - lineno = 0 - while 1: - try: - for i in range(lineno, lineno + self.MAXLINES): - print self.__lines[i] - except IndexError: - break - else: - lineno += self.MAXLINES - key = None - while key is None: - key = raw_input(prompt) - if key not in ('', 'q'): - key = None - if key == 'q': - break - -def setcopyright(): - """Set 'copyright' and 'credits' in __builtin__""" - __builtin__.copyright = _Printer("copyright", sys.copyright) - if _is_jython: - __builtin__.credits = _Printer( - "credits", - "Jython is maintained by the Jython developers (www.jython.org).") - elif _is_pypy: - __builtin__.credits = _Printer( - "credits", - "PyPy is maintained by the PyPy developers: http://codespeak.net/pypy") - else: - __builtin__.credits = _Printer("credits", """\ - Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands - for supporting Python development. See www.python.org for more information.""") - here = os.path.dirname(os.__file__) - __builtin__.license = _Printer( - "license", "See http://www.python.org/%.3s/license.html" % sys.version, - ["LICENSE.txt", "LICENSE"], - [os.path.join(here, os.pardir), here, os.curdir]) - - -class _Helper(object): - """Define the built-in 'help'. - This is a wrapper around pydoc.help (with a twist). - - """ - - def __repr__(self): - return "Type help() for interactive help, " \ - "or help(object) for help about object." - def __call__(self, *args, **kwds): - import pydoc - return pydoc.help(*args, **kwds) - -def sethelper(): - __builtin__.help = _Helper() - -def aliasmbcs(): - """On Windows, some default encodings are not provided by Python, - while they are always available as "mbcs" in each locale. Make - them usable by aliasing to "mbcs" in such a case.""" - if sys.platform == 'win32': - import locale, codecs - enc = locale.getdefaultlocale()[1] - if enc.startswith('cp'): # "cp***" ? - try: - codecs.lookup(enc) - except LookupError: - import encodings - encodings._cache[enc] = encodings._unknown - encodings.aliases.aliases[enc] = 'mbcs' - -def setencoding(): - """Set the string encoding used by the Unicode implementation. The - default is 'ascii', but if you're willing to experiment, you can - change this.""" - encoding = "ascii" # Default value set by _PyUnicode_Init() - if 0: - # Enable to support locale aware default string encodings. - import locale - loc = locale.getdefaultlocale() - if loc[1]: - encoding = loc[1] - if 0: - # Enable to switch off string to Unicode coercion and implicit - # Unicode to string conversion. - encoding = "undefined" - if encoding != "ascii": - # On Non-Unicode builds this will raise an AttributeError... - sys.setdefaultencoding(encoding) # Needs Python Unicode build ! - - -def execsitecustomize(): - """Run custom site specific code, if available.""" - try: - import sitecustomize - except ImportError: - pass - -def virtual_install_main_packages(): - f = open(os.path.join(os.path.dirname(__file__), 'orig-prefix.txt')) - sys.real_prefix = f.read().strip() - f.close() - pos = 2 - if sys.path[0] == '': - pos += 1 - if sys.platform == 'win32': - paths = [os.path.join(sys.real_prefix, 'Lib'), os.path.join(sys.real_prefix, 'DLLs')] - elif _is_jython: - paths = [os.path.join(sys.real_prefix, 'Lib')] - elif _is_pypy: - cpyver = '%d.%d.%d' % sys.version_info[:3] - paths = [os.path.join(sys.real_prefix, 'lib_pypy'), - os.path.join(sys.real_prefix, 'lib-python', 'modified-%s' % cpyver), - os.path.join(sys.real_prefix, 'lib-python', cpyver)] - else: - paths = [os.path.join(sys.real_prefix, 'lib', 'python'+sys.version[:3])] - lib64_path = os.path.join(sys.real_prefix, 'lib64', 'python'+sys.version[:3]) - if os.path.exists(lib64_path): - paths.append(lib64_path) - # This is hardcoded in the Python executable, but relative to sys.prefix: - plat_path = os.path.join(sys.real_prefix, 'lib', 'python'+sys.version[:3], - 'plat-%s' % sys.platform) - if os.path.exists(plat_path): - paths.append(plat_path) - # This is hardcoded in the Python executable, but - # relative to sys.prefix, so we have to fix up: - for path in list(paths): - tk_dir = os.path.join(path, 'lib-tk') - if os.path.exists(tk_dir): - paths.append(tk_dir) - - # These are hardcoded in the Apple's Python executable, - # but relative to sys.prefix, so we have to fix them up: - if sys.platform == 'darwin': - hardcoded_paths = [os.path.join(sys.real_prefix, 'lib', 'python'+sys.version[:3], module) - for module in ('plat-darwin', 'plat-mac', 'plat-mac/lib-scriptpackages')] - - for path in hardcoded_paths: - if os.path.exists(path): - paths.append(path) - - sys.path.extend(paths) - -def force_global_eggs_after_local_site_packages(): - """ - Force easy_installed eggs in the global environment to get placed - in sys.path after all packages inside the virtualenv. This - maintains the "least surprise" result that packages in the - virtualenv always mask global packages, never the other way - around. - - """ - egginsert = getattr(sys, '__egginsert', 0) - for i, path in enumerate(sys.path): - if i > egginsert and path.startswith(sys.prefix): - egginsert = i - sys.__egginsert = egginsert + 1 - -def virtual_addsitepackages(known_paths): - force_global_eggs_after_local_site_packages() - return addsitepackages(known_paths, sys_prefix=sys.real_prefix) - -def fixclasspath(): - """Adjust the special classpath sys.path entries for Jython. These - entries should follow the base virtualenv lib directories. - """ - paths = [] - classpaths = [] - for path in sys.path: - if path == '__classpath__' or path.startswith('__pyclasspath__'): - classpaths.append(path) - else: - paths.append(path) - sys.path = paths - sys.path.extend(classpaths) - -def execusercustomize(): - """Run custom user specific code, if available.""" - try: - import usercustomize - except ImportError: - pass - - -def main(): - global ENABLE_USER_SITE - virtual_install_main_packages() - abs__file__() - paths_in_sys = removeduppaths() - if (os.name == "posix" and sys.path and - os.path.basename(sys.path[-1]) == "Modules"): - addbuilddir() - if _is_jython: - fixclasspath() - GLOBAL_SITE_PACKAGES = not os.path.exists(os.path.join(os.path.dirname(__file__), 'no-global-site-packages.txt')) - if not GLOBAL_SITE_PACKAGES: - ENABLE_USER_SITE = False - if ENABLE_USER_SITE is None: - ENABLE_USER_SITE = check_enableusersite() - paths_in_sys = addsitepackages(paths_in_sys) - paths_in_sys = addusersitepackages(paths_in_sys) - if GLOBAL_SITE_PACKAGES: - paths_in_sys = virtual_addsitepackages(paths_in_sys) - if sys.platform == 'os2emx': - setBEGINLIBPATH() - setquit() - setcopyright() - sethelper() - aliasmbcs() - setencoding() - execsitecustomize() - if ENABLE_USER_SITE: - execusercustomize() - # Remove sys.setdefaultencoding() so that users cannot change the - # encoding after initialization. The test for presence is needed when - # this module is run as a script, because this code is executed twice. - if hasattr(sys, "setdefaultencoding"): - del sys.setdefaultencoding - -main() - -def _script(): - help = """\ - %s [--user-base] [--user-site] - - Without arguments print some useful information - With arguments print the value of USER_BASE and/or USER_SITE separated - by '%s'. - - Exit codes with --user-base or --user-site: - 0 - user site directory is enabled - 1 - user site directory is disabled by user - 2 - uses site directory is disabled by super user - or for security reasons - >2 - unknown error - """ - args = sys.argv[1:] - if not args: - print "sys.path = [" - for dir in sys.path: - print " %r," % (dir,) - print "]" - def exists(path): - if os.path.isdir(path): - return "exists" - else: - return "doesn't exist" - print "USER_BASE: %r (%s)" % (USER_BASE, exists(USER_BASE)) - print "USER_SITE: %r (%s)" % (USER_SITE, exists(USER_BASE)) - print "ENABLE_USER_SITE: %r" % ENABLE_USER_SITE - sys.exit(0) - - buffer = [] - if '--user-base' in args: - buffer.append(USER_BASE) - if '--user-site' in args: - buffer.append(USER_SITE) - - if buffer: - print os.pathsep.join(buffer) - if ENABLE_USER_SITE: - sys.exit(0) - elif ENABLE_USER_SITE is False: - sys.exit(1) - elif ENABLE_USER_SITE is None: - sys.exit(2) - else: - sys.exit(3) - else: - import textwrap - print textwrap.dedent(help % (sys.argv[0], os.pathsep)) - sys.exit(10) - -if __name__ == '__main__': - _script() diff --git a/test/lib/python2.6/sre.py b/test/lib/python2.6/sre.py deleted file mode 120000 index 3ade4e80e..000000000 --- a/test/lib/python2.6/sre.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/sre.py \ No newline at end of file diff --git a/test/lib/python2.6/sre_compile.py b/test/lib/python2.6/sre_compile.py deleted file mode 120000 index fc13a37d5..000000000 --- a/test/lib/python2.6/sre_compile.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/sre_compile.py \ No newline at end of file diff --git a/test/lib/python2.6/sre_constants.py b/test/lib/python2.6/sre_constants.py deleted file mode 120000 index 98b0d5543..000000000 --- a/test/lib/python2.6/sre_constants.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/sre_constants.py \ No newline at end of file diff --git a/test/lib/python2.6/sre_parse.py b/test/lib/python2.6/sre_parse.py deleted file mode 120000 index 76b24c687..000000000 --- a/test/lib/python2.6/sre_parse.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/sre_parse.py \ No newline at end of file diff --git a/test/lib/python2.6/stat.py b/test/lib/python2.6/stat.py deleted file mode 120000 index 68d61e662..000000000 --- a/test/lib/python2.6/stat.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/stat.py \ No newline at end of file diff --git a/test/lib/python2.6/types.py b/test/lib/python2.6/types.py deleted file mode 120000 index 72b00ba35..000000000 --- a/test/lib/python2.6/types.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/types.py \ No newline at end of file diff --git a/test/lib/python2.6/warnings.py b/test/lib/python2.6/warnings.py deleted file mode 120000 index 1af4e2833..000000000 --- a/test/lib/python2.6/warnings.py +++ /dev/null @@ -1 +0,0 @@ -/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/warnings.py \ No newline at end of file -- cgit From 4229990fa77d6edb73b88e92750a8779c478e40c Mon Sep 17 00:00:00 2001 From: Christian Berendt <berendt@b1-systems.de> Date: Sat, 26 Feb 2011 19:09:57 +0100 Subject: replaced ConnectionFailed with Exception in tools/euca-get-ajax-console was not working for me with euca2tools 1.2 (version 2007-10-10, release 31337) --- tools/euca-get-ajax-console | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/euca-get-ajax-console b/tools/euca-get-ajax-console index 37060e74f..e407dd566 100755 --- a/tools/euca-get-ajax-console +++ b/tools/euca-get-ajax-console @@ -35,7 +35,7 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): import boto import nova from boto.ec2.connection import EC2Connection -from euca2ools import Euca2ool, InstanceValidationError, Util, ConnectionFailed +from euca2ools import Euca2ool, InstanceValidationError, Util usage_string = """ Retrieves a url to an ajax console terminal @@ -147,7 +147,7 @@ def main(): try: euca_conn = euca.make_connection() - except ConnectionFailed, e: + except Exception, e: print e.message sys.exit(1) try: -- cgit From dcfa9670a669ff881865750b2f96f62195dce2df Mon Sep 17 00:00:00 2001 From: Christian Berendt <berendt@b1-systems.de> Date: Sat, 26 Feb 2011 19:19:02 +0100 Subject: renamed flag from maximum_... to max_... --- nova/virt/disk.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 8789d8123..2bded07a4 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -40,7 +40,7 @@ flags.DEFINE_integer('block_size', 1024 * 1024 * 256, 'block_size to use for dd') flags.DEFINE_integer('timeout_nbd', 10, 'time to wait for a NBD device coming up') -flags.DEFINE_integer('maximum_nbd_devices', 16, +flags.DEFINE_integer('max_nbd_devices', 16, 'maximum number of possible nbd devices') @@ -143,7 +143,7 @@ def _unlink_device(device, nbd): utils.execute('sudo losetup --detach %s' % device) -_DEVICES = ['/dev/nbd%s' % i for i in xrange(FLAGS.maximum_nbd_devices)] +_DEVICES = ['/dev/nbd%s' % i for i in xrange(FLAGS.max_nbd_devices)] def _allocate_device(): -- cgit From 38c21546ecc079300c575e5950bcb990eecee3a3 Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Sun, 27 Feb 2011 20:28:04 -0500 Subject: execute: shell=True removed. --- nova/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/utils.py b/nova/utils.py index 0cf91e0cc..40a8d8d8c 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -125,7 +125,7 @@ def fetchfile(url, target): # c.perform() # c.close() # fp.close() - execute("curl --fail %s -o %s" % (url, target)) + execute("curl","--fail",url,"-o",target) def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): @@ -133,7 +133,7 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): env = os.environ.copy() if addl_env: env.update(addl_env) - obj = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, + obj = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) result = None if process_input != None: @@ -254,7 +254,7 @@ def last_octet(address): def get_my_linklocal(interface): try: - if_str = execute("ip -f inet6 -o addr show %s" % interface) + if_str = execute("ip","-f","inet6","-o","addr","show", interface) condition = "\s+inet6\s+([0-9a-f:]+)/\d+\s+scope\s+link" links = [re.search(condition, x) for x in if_str[0].split('\n')] address = [w.group(1) for w in links if w is not None] -- cgit From 4f90783224025618661bf8814e016843ec237875 Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Sun, 27 Feb 2011 20:52:32 -0500 Subject: execvp --- nova/volume/driver.py | 69 ++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/nova/volume/driver.py b/nova/volume/driver.py index e3744c790..e73202b73 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -97,22 +97,21 @@ class VolumeDriver(object): sizestr = '100M' else: sizestr = '%sG' % volume['size'] - self._try_execute("sudo lvcreate -L %s -n %s %s" % - (sizestr, + self._try_execute('sudo','lvcreate','-L',sizestr,'-n', volume['name'], - FLAGS.volume_group)) + FLAGS.volume_group) def delete_volume(self, volume): """Deletes a logical volume.""" try: - self._try_execute("sudo lvdisplay %s/%s" % + self._try_execute('sudo','lvdisplay','%s/%s" % (FLAGS.volume_group, volume['name'])) except Exception as e: # If the volume isn't present, then don't attempt to delete return True - self._try_execute("sudo lvremove -f %s/%s" % + self._try_execute('sudo','lvremove','-f',"%s/%s" % (FLAGS.volume_group, volume['name'])) @@ -168,12 +167,13 @@ class AOEDriver(VolumeDriver): blade_id) = self.db.volume_allocate_shelf_and_blade(context, volume['id']) self._try_execute( - "sudo vblade-persist setup %s %s %s /dev/%s/%s" % - (shelf_id, + 'sudo','vblade-persist','setup', + shelf_id, blade_id, FLAGS.aoe_eth_dev, - FLAGS.volume_group, - volume['name'])) + "/dev/%s/%s" % + (FLAGS.volume_group, + volume['name'])) # NOTE(vish): The standard _try_execute does not work here # because these methods throw errors if other # volumes on this host are in the process of @@ -182,9 +182,9 @@ class AOEDriver(VolumeDriver): # just wait a bit for the current volume to # be ready and ignore any errors. time.sleep(2) - self._execute("sudo vblade-persist auto all", + self._execute('sudo','vblade-persist','auto','all', check_exit_code=False) - self._execute("sudo vblade-persist start all", + self._execute('sudo','vblade-persist','start','all', check_exit_code=False) def remove_export(self, context, volume): @@ -192,15 +192,15 @@ class AOEDriver(VolumeDriver): (shelf_id, blade_id) = self.db.volume_get_shelf_and_blade(context, volume['id']) - self._try_execute("sudo vblade-persist stop %s %s" % - (shelf_id, blade_id)) - self._try_execute("sudo vblade-persist destroy %s %s" % - (shelf_id, blade_id)) + self._try_execute('sudo','vblade-persist','stop', + shelf_id, blade_id) + self._try_execute('sudo','vblade-persist','destroy', + shelf_id, blade_id) def discover_volume(self, _volume): """Discover volume on a remote host.""" - self._execute("sudo aoe-discover") - self._execute("sudo aoe-stat", check_exit_code=False) + self._execute('sudo','aoe-discover') + self._execute('sudo','aoe-stat', check_exit_code=False) def undiscover_volume(self, _volume): """Undiscover volume on a remote host.""" @@ -252,13 +252,16 @@ class ISCSIDriver(VolumeDriver): iscsi_name = "%s%s" % (FLAGS.iscsi_target_prefix, volume['name']) volume_path = "/dev/%s/%s" % (FLAGS.volume_group, volume['name']) - self._sync_exec("sudo ietadm --op new " - "--tid=%s --params Name=%s" % - (iscsi_target, iscsi_name), + self._sync_exec('sudo','ietadm','--op','new', + "--tid=%s" % iscsi_target, + '--params', + "Name=%s" % iscsi-name, check_exit_code=False) - self._sync_exec("sudo ietadm --op new --tid=%s " - "--lun=0 --params Path=%s,Type=fileio" % - (iscsi_target, volume_path), + self._sync_exec('sudo','ietadm','--op','new', + "--tid=%s" % iscsi_target, + '--lun=0', + '--params', + "Path=%s,Type=fileio" % volume_path, check_exit_code=False) def _ensure_iscsi_targets(self, context, host): @@ -490,16 +493,13 @@ class RBDDriver(VolumeDriver): size = 100 else: size = int(volume['size']) * 1024 - self._try_execute("rbd --pool %s --size %d create %s" % - (FLAGS.rbd_pool, - size, - volume['name'])) + self._try_execute('rbd','--pool',FLAGS.rbd_pool, + '--size', size,'create', volume['name']) def delete_volume(self, volume): """Deletes a logical volume.""" - self._try_execute("rbd --pool %s rm %s" % - (FLAGS.rbd_pool, - volume['name'])) + self._try_execute('rbd','--pool',FLAGS.rbd_pool, + 'rm', voluname['name']) def local_path(self, volume): """Returns the path of the rbd volume.""" @@ -534,7 +534,7 @@ class SheepdogDriver(VolumeDriver): def check_for_setup_error(self): """Returns an error if prerequisites aren't met""" try: - (out, err) = self._execute("collie cluster info") + (out, err) = self._execute('collie','cluster','info') if not out.startswith('running'): raise exception.Error(_("Sheepdog is not working: %s") % out) except exception.ProcessExecutionError: @@ -546,12 +546,13 @@ class SheepdogDriver(VolumeDriver): sizestr = '100M' else: sizestr = '%sG' % volume['size'] - self._try_execute("qemu-img create sheepdog:%s %s" % - (volume['name'], sizestr)) + self._try_execute('qemu-img','create', + "sheepdog:%s" %s" % volume['name'], + sizestr) def delete_volume(self, volume): """Deletes a logical volume""" - self._try_execute("collie vdi delete %s" % volume['name']) + self._try_execute('collie','vdi','delete',volume['name']) def local_path(self, volume): return "sheepdog:%s" % volume['name'] -- cgit From 953efce36b74c18a32ef9c42e6b1a57190e3ff6e Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Sun, 27 Feb 2011 20:53:53 -0500 Subject: execvp --- nova/crypto.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/nova/crypto.py b/nova/crypto.py index a34b940f5..b240a3958 100644 --- a/nova/crypto.py +++ b/nova/crypto.py @@ -105,8 +105,8 @@ def generate_key_pair(bits=1024): tmpdir = tempfile.mkdtemp() keyfile = os.path.join(tmpdir, 'temp') - utils.execute('ssh-keygen -q -b %d -N "" -f %s' % (bits, keyfile)) - (out, err) = utils.execute('ssh-keygen -q -l -f %s.pub' % (keyfile)) + utils.execute('ssh-keygen','-q','-b',"%d" % bits,'-N','""','-f',keyfile) + (out, err) = utils.execute('ssh-keygen','-q','-l','-f',"%s.pub" % (keyfile)) fingerprint = out.split(' ')[1] private_key = open(keyfile).read() public_key = open(keyfile + '.pub').read() @@ -118,7 +118,7 @@ def generate_key_pair(bits=1024): # bio = M2Crypto.BIO.MemoryBuffer() # key.save_pub_key_bio(bio) # public_key = bio.read() - # public_key, err = execute('ssh-keygen -y -f /dev/stdin', private_key) + # public_key, err = execute('ssh-keygen','-y','-f','/dev/stdin', private_key) return (private_key, public_key, fingerprint) @@ -143,8 +143,8 @@ def revoke_cert(project_id, file_name): start = os.getcwd() os.chdir(ca_folder(project_id)) # NOTE(vish): potential race condition here - utils.execute("openssl ca -config ./openssl.cnf -revoke '%s'" % file_name) - utils.execute("openssl ca -gencrl -config ./openssl.cnf -out '%s'" % + utils.execute('openssl','ca','-config','./openssl.cnf','-revoke',"'%s'" % file_name) + utils.execute('openssl','ca','-gencrl','-config','./openssl.cnf','-out',"'%s'" % FLAGS.crl_file) os.chdir(start) @@ -193,9 +193,8 @@ def generate_x509_cert(user_id, project_id, bits=1024): tmpdir = tempfile.mkdtemp() keyfile = os.path.abspath(os.path.join(tmpdir, 'temp.key')) csrfile = os.path.join(tmpdir, 'temp.csr') - utils.execute("openssl genrsa -out %s %s" % (keyfile, bits)) - utils.execute("openssl req -new -key %s -out %s -batch -subj %s" % - (keyfile, csrfile, subject)) + utils.execute('openssl','genrsa','-out',keyfile,bits) + utils.execute('openssl','req','-new','-key',keyfile,'-out',csrfile,'-batch','-subj',subject) private_key = open(keyfile).read() csr = open(csrfile).read() shutil.rmtree(tmpdir) @@ -212,8 +211,7 @@ def _ensure_project_folder(project_id): if not os.path.exists(ca_path(project_id)): start = os.getcwd() os.chdir(ca_folder()) - utils.execute("sh geninter.sh %s %s" % - (project_id, _project_cert_subject(project_id))) + utils.execute('sh','geninter.sh',project_id, _project_cert_subject(project_id)) os.chdir(start) @@ -228,8 +226,8 @@ def generate_vpn_files(project_id): start = os.getcwd() os.chdir(ca_folder()) # TODO(vish): the shell scripts could all be done in python - utils.execute("sh genvpn.sh %s %s" % - (project_id, _vpn_cert_subject(project_id))) + utils.execute('sh','genvpn.sh', + project_id, _vpn_cert_subject(project_id)) with open(csr_fn, "r") as csrfile: csr_text = csrfile.read() (serial, signed_csr) = sign_csr(csr_text, project_id) @@ -259,9 +257,9 @@ def _sign_csr(csr_text, ca_folder): start = os.getcwd() # Change working dir to CA os.chdir(ca_folder) - utils.execute("openssl ca -batch -out %s -config " - "./openssl.cnf -infiles %s" % (outbound, inbound)) - out, _err = utils.execute("openssl x509 -in %s -serial -noout" % outbound) + utils.execute('openssl','ca','-batch','-out',outbound,'-config' + './openssl.cnf','-infiles',inbound) + out, _err = utils.execute('openssl','x509','-in',outbound','-serial','-noout') serial = out.rpartition("=")[2] os.chdir(start) with open(outbound, "r") as crtfile: -- cgit From 90abcdc7ae9e3f855dadb1ccc88892a2cc7bab05 Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Sun, 27 Feb 2011 20:57:13 -0500 Subject: execvp --- nova/console/xvp.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nova/console/xvp.py b/nova/console/xvp.py index cd257e0a6..271dffa54 100644 --- a/nova/console/xvp.py +++ b/nova/console/xvp.py @@ -133,10 +133,10 @@ class XVPConsoleProxy(object): return logging.debug(_("Starting xvp")) try: - utils.execute('xvp -p %s -c %s -l %s' % - (FLAGS.console_xvp_pid, - FLAGS.console_xvp_conf, - FLAGS.console_xvp_log)) + utils.execute('xvp', + '-p',FLAGS.console_xvp_pid, + '-c',FLAGS.console_xvp_conf, + '-l',FLAGS.console_xvp_log) except exception.ProcessExecutionError, err: logging.error(_("Error starting xvp: %s") % err) @@ -190,5 +190,5 @@ class XVPConsoleProxy(object): flag = '-x' #xvp will blow up on passwords that are too long (mdragon) password = password[:maxlen] - out, err = utils.execute('xvp %s' % flag, process_input=password) + out, err = utils.execute('xvp', flag, process_input=password) return out.strip() -- cgit From 8b3e9ad11c2f5c425701f1eb4abb7b3f577ae1cc Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Mon, 28 Feb 2011 12:37:02 +0100 Subject: Add utils.synchronized decorator to allow for synchronising method entrance across multiple workers on the same host. --- nova/tests/test_misc.py | 37 ++++++++++++++++++++++++++++++++++++- nova/utils.py | 11 +++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_misc.py b/nova/tests/test_misc.py index e6da6112a..154b6fae6 100644 --- a/nova/tests/test_misc.py +++ b/nova/tests/test_misc.py @@ -14,10 +14,14 @@ # License for the specific language governing permissions and limitations # under the License. +from datetime import datetime +import errno import os +import select +import time from nova import test -from nova.utils import parse_mailmap, str_dict_replace +from nova.utils import parse_mailmap, str_dict_replace, synchronized class ProjectTestCase(test.TestCase): @@ -55,3 +59,34 @@ class ProjectTestCase(test.TestCase): '%r not listed in Authors' % missing) finally: tree.unlock() + + +class LockTestCase(test.TestCase): + def test_synchronized(self): + rpipe, wpipe = os.pipe() + pid = os.fork() + if pid > 0: + os.close(wpipe) + + @synchronized('testlock') + def f(): + rfds, _, __ = select.select([rpipe], [], [], 1) + self.assertEquals(len(rfds), 0, "The other process, which was" + " supposed to be locked, " + "wrote on its end of the " + "pipe") + os.close(rpipe) + + f() + else: + os.close(rpipe) + + @synchronized('testlock') + def g(): + try: + os.write(wpipe, "foo") + except OSError, e: + self.assertEquals(e.errno, errno.EPIPE) + return + g() + os._exit(0) diff --git a/nova/utils.py b/nova/utils.py index 0cf91e0cc..cb1ea5a7d 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -25,6 +25,7 @@ import base64 import datetime import inspect import json +import lockfile import os import random import socket @@ -491,6 +492,16 @@ def loads(s): return json.loads(s) +def synchronized(name): + def wrap(f): + def inner(*args, **kwargs): + lock = lockfile.FileLock('nova-%s.lock' % name) + with lock: + return f(*args, **kwargs) + return inner + return wrap + + def ensure_b64_encoding(val): """Safety method to ensure that values expected to be base64-encoded actually are. If they are, the value is returned unchanged. Otherwise, -- cgit From 0d3a1f9c7478ae3c4f042e1876d47359804e1973 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Mon, 28 Feb 2011 15:29:42 +0100 Subject: Wrap IptablesManager.apply() calls in utils.synchronized to avoid having different workers step on each other's toes. --- nova/network/linux_net.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 34337901a..8832f7c68 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -274,6 +274,7 @@ class IptablesManager(object): self.semaphore = semaphore.Semaphore() + @utils.synchronized('iptables') def apply(self): """Apply the current in-memory set of iptables rules -- cgit From a457595224a5ca5cdb0191ba2f6fa542d16e18f5 Mon Sep 17 00:00:00 2001 From: Nirmal Ranganathan <nirmal.ranganathan@rackspace.com> Date: Mon, 28 Feb 2011 11:25:14 -0600 Subject: Removed extraneous newline --- nova/tests/test_compute.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index e338a7cfa..949b5e6eb 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -274,4 +274,3 @@ class ComputeTestCase(test.TestCase): type = instance_types.get_by_flavor_id("1") self.assertEqual(type, 'm1.tiny') - -- cgit From 7a6daa8d92f4f11fe2fce8fb2f4b11d96cb98c2d Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" <kevin.mitchell@rackspace.com> Date: Mon, 28 Feb 2011 17:27:19 +0000 Subject: Suppress stack traces unless --verbose is specified --- nova/log.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nova/log.py b/nova/log.py index 87a21ddb4..d194ab8f0 100644 --- a/nova/log.py +++ b/nova/log.py @@ -266,7 +266,10 @@ class NovaRootLogger(NovaLogger): def handle_exception(type, value, tb): - logging.root.critical(str(value), exc_info=(type, value, tb)) + extra = {} + if FLAGS.verbose: + extra['exc_info'] = (type, value, tb) + logging.root.critical(str(value), **extra) def reset(): -- cgit From c1bcf1dead8734a02172b4ac20b24fbbb7dbb993 Mon Sep 17 00:00:00 2001 From: Cerberus <matt.dietz@rackspace.com> Date: Mon, 28 Feb 2011 11:40:22 -0600 Subject: Rename migration to coincide with latest trunk changes --- .../versions/004_add_instance_migrations.py | 61 ---------------------- .../versions/007_add_instance_migrations.py | 61 ++++++++++++++++++++++ 2 files changed, 61 insertions(+), 61 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py deleted file mode 100644 index 4fda525f1..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/004_add_instance_migrations.py +++ /dev/null @@ -1,61 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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.from sqlalchemy import * - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - -# -# New Tables -# - -migrations = Table('migrations', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('source_compute', String(255)), - Column('dest_compute', String(255)), - Column('dest_host', String(255)), - Column('instance_id', Integer, ForeignKey('instances.id'), - nullable=True), - Column('status', String(255)), - ) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (migrations, ): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py new file mode 100644 index 000000000..4fda525f1 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py @@ -0,0 +1,61 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 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.from sqlalchemy import * + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + +# +# New Tables +# + +migrations = Table('migrations', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('source_compute', String(255)), + Column('dest_compute', String(255)), + Column('dest_host', String(255)), + Column('instance_id', Integer, ForeignKey('instances.id'), + nullable=True), + Column('status', String(255)), + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (migrations, ): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise -- cgit From 8da6796789767b1341cb5a650066b67ad3191c74 Mon Sep 17 00:00:00 2001 From: Cerberus <matt.dietz@rackspace.com> Date: Mon, 28 Feb 2011 12:30:02 -0600 Subject: Merge review fixes --- nova/virt/xenapi/vm_utils.py | 17 ++++-- nova/virt/xenapi/vmops.py | 27 ++++++---- .../xenserver/xenapi/etc/xapi.d/plugins/migration | 62 +++++----------------- 3 files changed, 45 insertions(+), 61 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index e7ad1f686..870660dea 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -300,6 +300,13 @@ class VMHelper(HelperBase): the UUID """ return session.call_xenapi('SR.get_by_name_label', sr_label)[0] + @classmethod + def get_sr_path(cls, session, sr_label='slices'): + """ Finds the SR and then coerces it into a path on the dom0 file + system """ + # TODO(mdietz): replace this with the flag once unified-images merges + return '/var/run/sr-mount/%s' % cls.get_sr(session, sr_label) + @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_id): """ Requests that the Glance plugin bundle the specified VDIs and @@ -508,13 +515,17 @@ class VMHelper(HelperBase): @classmethod def scan_sr(cls, session, instance_id=None, sr_ref=None): + """Scans the SR specified by sr_ref""" if sr_ref: LOG.debug(_("Re-scanning SR %s"), sr_ref) task = session.call_xenapi('Async.SR.scan', sr_ref) session.wait_for_task(instance_id, task) - else: - sr_ref = cls.get_sr(session) - session.call_xenapi('SR.scan', sr_ref) + + @classmethod + def scan_default_sr(cls, session): + """Looks for the system default SR and triggers a re-scan""" + sr_ref = cls.get_sr(session) + session.call_xenapi('SR.scan', sr_ref) def get_rrd(host, uuid): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index f5278ff07..b3e5627d8 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -298,8 +298,10 @@ class VMOps(object): VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) cow_uuid = vm_vdi_rec['uuid'] - params = {'host': dest, 'vdi_uuid': base_copy_uuid, - 'instance_id': instance.id, } + params = {'host': dest, + 'vdi_uuid': base_copy_uuid, + 'instance_id': instance.id, + 'sr_path': VMHelper.get_sr_path(self._session), } task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) @@ -308,8 +310,11 @@ class VMOps(object): # Now power down the instance and transfer the COW VHD self._shutdown(instance, vm_ref, method='clean') - params = {'host': dest, 'vdi_uuid': cow_uuid, - 'instance_id': instance.id, } + params = {'host': dest, + 'vdi_uuid': cow_uuid, + 'instance_id': instance.id, + 'sr_path': VMHelper.get_sr_path(self._session), } + task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) self._session.wait_for_task(instance.id, task) @@ -326,14 +331,15 @@ class VMOps(object): 'old_base_copy_uuid': disk_info['base_copy'], 'old_cow_uuid': disk_info['cow'], 'new_base_copy_uuid': new_base_copy_uuid, - 'new_cow_uuid': new_cow_uuid, } + 'new_cow_uuid': new_cow_uuid, + 'sr_path': VMHelper.get_sr_path(self._session), } task = self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) self._session.wait_for_task(instance.id, task) # Now we rescan the SR so we find the VHDs - VMHelper.scan_sr(self._session) + VMHelper.scan_default_sr(self._session) return new_cow_uuid @@ -411,7 +417,7 @@ class VMOps(object): raise RuntimeError(resp_dict['message']) return resp_dict['message'] - def _shutdown(self, instance, vm, method='hard'): + def _shutdown(self, instance, vm, hard=True): """Shutdown an instance """ state = self.get_info(instance['name'])['state'] if state == power_state.SHUTDOWN: @@ -421,10 +427,11 @@ class VMOps(object): try: task = None - if method == 'clean': - task = self._session.call_xenapi('Async.VM.clean_shutdown', vm) - else: + if hard: task = self._session.call_xenapi('Async.VM.hard_shutdown', vm) + else: + task = self._session.call_xenapi('Async.VM.clean_shutdown', vm) + self._session.wait_for_task(instance.id, task) except self.XenAPI.Failure, exc: LOG.exception(exc) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration index 7a6eefda2..4aa89863a 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/migration @@ -1,7 +1,6 @@ #!/usr/bin/env python -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. +# Copyright 2010 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -31,38 +30,6 @@ import XenAPIPlugin from pluginlib_nova import * configure_logging('migration') -SSH_HOSTS = '/root/.ssh/known_hosts' -DEVNULL = '/dev/null' -KEYSCAN = '/usr/bin/ssh-keyscan' -RSYNC = '/usr/bin/rsync' -FILE_SR_PATH = '/var/run/sr-mount' -IMAGE_PATH = '/images/' -VHD_UTIL = '/usr/sbin/vhd-util' - -def get_sr_path(session): - sr_ref = find_sr(session) - - if sr_ref is None: - raise Exception('Cannot find SR to read VDI from') - - sr_rec = session.xenapi.SR.get_record(sr_ref) - sr_uuid = sr_rec["uuid"] - sr_path = os.path.join(FILE_SR_PATH, sr_uuid) - return sr_path - -def find_sr(session): - host = get_this_host(session) - srs = session.xenapi.SR.get_all() - for sr in srs: - sr_rec = session.xenapi.SR.get_record(sr) - if not ('i18n-key' in sr_rec['other_config'] and - sr_rec['other_config']['i18n-key'] == 'local-storage'): - continue - for pbd in sr_rec['PBDs']: - pbd_rec = session.xenapi.PBD.get_record(pbd) - if pbd_rec['host'] == host: - return sr - return None def move_vhds_into_sr(session, args): """Moves the VHDs from their copied location to the SR""" @@ -75,13 +42,13 @@ def move_vhds_into_sr(session, args): new_base_copy_uuid = params['new_base_copy_uuid'] new_cow_uuid = params['new_cow_uuid'] - sr_path = get_sr_path(session) + sr_path = params['sr_path'] sr_temp_path = "%s/images/" % sr_path - # Discover the copied VHDs locally, and then set up paths to copy + # Discover the copied VHDs locally, and then set up paths to copy # them to under the SR - source_image_path = "%s/instance%d" % (IMAGE_PATH, instance_id) - source_base_copy_path = "%s/%s.vhd" % (source_image_path, + source_image_path = "%s/instance%d" % ('/images/', instance_id) + source_base_copy_path = "%s/%s.vhd" % (source_image_path, old_base_copy_uuid) source_cow_path = "%s/%s.vhd" % (source_image_path, old_cow_uuid) @@ -102,11 +69,10 @@ def move_vhds_into_sr(session, args): os.rmdir(source_image_path) # Link the COW to the base copy - logging.debug('Attaching COW to the base copy %s -> %s' % + logging.debug('Attaching COW to the base copy %s -> %s' % (new_cow_path, new_base_copy_path)) - subprocess.call([VHD_UTIL, 'modify', '-n', new_cow_path, '-p', - new_base_copy_path]) - + subprocess.call(shlex.split('/usr/sbin/vhd-util modify -n %s -p %s' % + (new_cow_path, new_base_copy_path))) logging.debug('Moving VHDs into SR %s' % sr_path) shutil.move("%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid), sr_path) shutil.move("%s/%s.vhd" % (temp_vhd_path, new_cow_uuid), sr_path) @@ -122,19 +88,19 @@ def transfer_vhd(session, args): instance_id = params['instance_id'] host = params['host'] vdi_uuid = params['vdi_uuid'] - sr_path = get_sr_path(session) + sr_path = params['sr_path'] vhd_path = "%s.vhd" % vdi_uuid source_path = "%s/%s" % (sr_path, vhd_path) - dest_path = '%s:%sinstance%d/' % (host, IMAGE_PATH, instance_id) + dest_path = '%s:%sinstance%d/' % (host, '/images/', instance_id) - logging.debug("Preparing to transmit %s to %s" % (source_path, + logging.debug("Preparing to transmit %s to %s" % (source_path, dest_path)) ssh_cmd = 'ssh -o StrictHostKeyChecking=no' - rsync_args = ['nohup', RSYNC, '-av', '--progress', '-e', ssh_cmd, - source_path, dest_path] + rsync_args = shlex.split('nohup /usr/bin/rsync -av --progress -e %s %s %s' + % (ssh_cmd, source_path, dest_path)) logging.debug('rsync %s' % (' '.join(rsync_args, ))) @@ -148,4 +114,4 @@ def transfer_vhd(session, args): if __name__ == '__main__': XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd, - 'move_vhds_into_sr':move_vhds_into_sr, }) + 'move_vhds_into_sr': move_vhds_into_sr, }) -- cgit From 7f3dbdab80a4b36a75c860fe1748dfbd03228f2a Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Mon, 28 Feb 2011 11:29:52 -0800 Subject: moving nova-manage integration tests to smoke tests --- nova/tests/test_nova_manage.py | 130 ----------------------------------------- 1 file changed, 130 deletions(-) delete mode 100644 nova/tests/test_nova_manage.py diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py deleted file mode 100644 index 03aea53c9..000000000 --- a/nova/tests/test_nova_manage.py +++ /dev/null @@ -1,130 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -""" -Tests For Nova-Manage -""" - -import time -import os -import subprocess - -from nova import test -from nova.db.sqlalchemy.session import get_session -from nova.db.sqlalchemy import models - - -class NovaManageTestCase(test.TestCase): - """Test case for nova-manage""" - def setUp(self): - super(NovaManageTestCase, self).setUp() - session = get_session() - max_flavorid = session.query(models.InstanceTypes).\ - order_by("flavorid desc").first() - self.flavorid = str(max_flavorid["flavorid"] + 1) - self.name = str(int(time.time())) - self.fnull = open(os.devnull, 'w') - - def teardown(self): - self.fnull.close() - - def test_create_and_delete_instance_types(self): - myname = self.name + "create_and_delete" - retcode = subprocess.call([ - "bin/nova-manage", - "instance_type", - "create", - self.name, - "256", - "1", - "120", - self.flavorid, - "2", - "10", - "10"], - stdout=self.fnull) - self.assertEqual(0, retcode) - retcode = subprocess.call(["bin/nova-manage", "instance_type", - "delete", self.name], stdout=self.fnull) - self.assertEqual(0, retcode) - retcode = subprocess.call(["bin/nova-manage", "instance_type", - "delete", self.name, "--purge"], - stdout=self.fnull) - self.assertEqual(0, retcode) - - def test_list_instance_types_or_flavors(self): - for c in ["instance_type", "flavor"]: - retcode = subprocess.call(["bin/nova-manage", c, \ - "list"], stdout=self.fnull) - self.assertEqual(0, retcode) - - def test_list_specific_instance_type(self): - retcode = subprocess.call(["bin/nova-manage", "instance_type", "list", - "m1.medium"], stdout=self.fnull) - self.assertEqual(0, retcode) - - def test_should_error_on_bad_create_args(self): - # shouldn't be able to create instance type with 0 vcpus - retcode = subprocess.call(["bin/nova-manage", "instance_type", - "create", self.name + "bad_args", - "256", "0", "120", self.flavorid], - stdout=self.fnull) - self.assertEqual(1, retcode) - - def test_should_fail_on_duplicate_flavorid(self): - # flavorid 1 is set in migration seed data - retcode = subprocess.call(["bin/nova-manage", "instance_type",\ - "create", self.name + "dupflavor", "256", - "1", "120", "1"], stdout=self.fnull) - self.assertEqual(3, retcode) - - def test_should_fail_on_duplicate_name(self): - duplicate_name = self.name + "dup_name" - retcode = subprocess.call([ - "bin/nova-manage", - "instance_type", - "create", - duplicate_name, - "256", - "1", - "120", - self.flavorid, - "2", - "10", - "10"], - stdout=self.fnull) - self.assertEqual(0, retcode) - duplicate_retcode = subprocess.call([ - "bin/nova-manage", - "instance_type", - "create", - duplicate_name, - "512", - "1", - "240", - str(int(self.flavorid) + 1), - "2", - "10", - "10"], - stdout=self.fnull) - self.assertEqual(3, duplicate_retcode) - delete_retcode = subprocess.call(["bin/nova-manage", "instance_type", - "delete", duplicate_name, "--purge"], - stdout=self.fnull) - self.assertEqual(0, delete_retcode) - - def test_instance_type_delete_should_fail_without_valid_name(self): - retcode = subprocess.call(["bin/nova-manage", "instance_type", - "delete", "doesntexist"], - stdout=self.fnull) - self.assertEqual(1, retcode) -- cgit From 05a96b320cf1d6b911b0edb11df0ed408a894e77 Mon Sep 17 00:00:00 2001 From: Brian Lamar <brian.lamar@rackspace.com> Date: Mon, 28 Feb 2011 14:49:03 -0500 Subject: Edited `nova.api.openstack.common:limited` method to raise an HTTPBadRequest exception if a negative limit or offset is given. I'm not confident that this is the correct approach, because I guess this method could be called out of an API/WSGI context, but the method *is* located in the OpenStack API module and is currently only used in WSGI-capable methods, so we should be safe. --- nova/api/openstack/common.py | 8 +++++++- nova/tests/api/openstack/test_common.py | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 1dc3767e2..9f85c5c8a 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -15,6 +15,8 @@ # License for the specific language governing permissions and limitations # under the License. +import webob.exc + from nova import exception @@ -27,7 +29,8 @@ def limited(items, request, max_limit=1000): GET variables. 'offset' is where to start in the list, and 'limit' is the maximum number of items to return. If 'limit' is not specified, 0, or > max_limit, we default - to max_limit. + to max_limit. Negative values for either offset or limit + will cause exc.HTTPBadRequest() exceptions to be raised. @kwarg max_limit: The maximum number of items to return from 'items' """ try: @@ -40,6 +43,9 @@ def limited(items, request, max_limit=1000): except ValueError: limit = max_limit + if offset < 0 or limit < 0: + raise webob.exc.HTTPBadRequest() + limit = min(max_limit, limit or max_limit) range_end = offset + limit return items[offset:range_end] diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py index 59d850157..92023362c 100644 --- a/nova/tests/api/openstack/test_common.py +++ b/nova/tests/api/openstack/test_common.py @@ -19,6 +19,7 @@ Test suites for 'common' code used throughout the OpenStack HTTP API. """ +import webob.exc from webob import Request @@ -160,3 +161,23 @@ class LimiterTest(test.TestCase): self.assertEqual(limited(items, req, max_limit=2000), items[3:]) req = Request.blank('/?offset=3000&limit=10') self.assertEqual(limited(items, req, max_limit=2000), []) + + def test_limiter_negative_limit(self): + """ + Test a negative limit. + """ + def _limit_large(): + limited(self.large, req, max_limit=2000) + + req = Request.blank('/?limit=-3000') + self.assertRaises(webob.exc.HTTPBadRequest, _limit_large) + + def test_limiter_negative_offset(self): + """ + Test a negative offset. + """ + def _limit_large(): + limited(self.large, req, max_limit=2000) + + req = Request.blank('/?offset=-30') + self.assertRaises(webob.exc.HTTPBadRequest, _limit_large) -- cgit From 167de65b41aa7188f01ace2359074abc8029ada2 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Mon, 28 Feb 2011 11:49:17 -0800 Subject: moved nova-manage flavors docs --- doc/source/adminguide/managing.instance.types.rst | 83 ----------------------- doc/source/runnova/managing.instance.types.rst | 83 +++++++++++++++++++++++ 2 files changed, 83 insertions(+), 83 deletions(-) delete mode 100644 doc/source/adminguide/managing.instance.types.rst create mode 100644 doc/source/runnova/managing.instance.types.rst diff --git a/doc/source/adminguide/managing.instance.types.rst b/doc/source/adminguide/managing.instance.types.rst deleted file mode 100644 index 30a47e47d..000000000 --- a/doc/source/adminguide/managing.instance.types.rst +++ /dev/null @@ -1,83 +0,0 @@ - - Copyright 2010-2011 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. - -Managing Instance Types and Flavors -=================================== - -What are Instance Types or Flavors ? ------------------------------------- - -Instance types are the container descriptions (meta-data) about instances. In layman terms, this is the size of the instance (vCPUs, RAM, etc.) that you will be launching. In the EC2 API, these are called by names such as "m1.large" or "m1.tiny", while the OpenStack API terms these "flavors" with names like "" - -Flavors are simply the name for instance types used in the OpenStack API. In nova, these are equivalent terms, so when you create an instance type you are also creating a flavor. For the rest of this document, I will refer to these as instance types. - -In the current (Cactus) version of nova, instance types can only be created by the nova administrator through the nova-manage command. Future versions of nova (in concert with the OpenStack API or EC2 API), may expose this functionality directly to users. - -Basic Management ----------------- - -Instance types / flavor are managed through the nova-manage binary with -the "instance_type" command and an appropriate subcommand. Note that you can also use -the "flavor" command as a synonym for "instance_types". - -To see all currently active instance types, use the list subcommand:: - - # nova-manage instance_type list - m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - -By default, the list subcommand only shows active instance types. To see all instance types -(even those deleted), add the argument 1 after the list subcommand like so:: - - # nova-manage instance_type list 1 - m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.deleted: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - -To create an instance type, use the "create" subcommand with the following positional arguments: - * memory (expressed in megabytes) - * vcpu(s) (integer) - * local storage (expressed in gigabytes) - * flavorid (unique integer) - * swap space (expressed in megabytes, defaults to zero, optional) - * RXTX quotas (expressed in gigabytes, defaults to zero, optional) - * RXTX cap (expressed in gigabytes, defaults to zero, optional) - -The following example creates an instance type named "m1.xxlarge":: - - # nova-manage instance_type create m1.xxlarge 32768 16 320 0 0 0 - m1.xxlarge created - -To delete an instance type, use the "delete" subcommand and specify the name:: - - # nova-manage instance_type delete m1.xxlarge - m1.xxlarge deleted - -Please note that the "delete" command only marks the instance type as -inactive in the database; it does not actually remove the instance type. This is done -to preserve the instance type definition for long running instances (which may not -terminate for months or years). If you are sure that you want to delete this instance -type from the database, pass the "--purge" flag after the name:: - - # nova-manage instance_type delete m1.xxlarge --purge - m1.xxlarge deleted diff --git a/doc/source/runnova/managing.instance.types.rst b/doc/source/runnova/managing.instance.types.rst new file mode 100644 index 000000000..30a47e47d --- /dev/null +++ b/doc/source/runnova/managing.instance.types.rst @@ -0,0 +1,83 @@ + + Copyright 2010-2011 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. + +Managing Instance Types and Flavors +=================================== + +What are Instance Types or Flavors ? +------------------------------------ + +Instance types are the container descriptions (meta-data) about instances. In layman terms, this is the size of the instance (vCPUs, RAM, etc.) that you will be launching. In the EC2 API, these are called by names such as "m1.large" or "m1.tiny", while the OpenStack API terms these "flavors" with names like "" + +Flavors are simply the name for instance types used in the OpenStack API. In nova, these are equivalent terms, so when you create an instance type you are also creating a flavor. For the rest of this document, I will refer to these as instance types. + +In the current (Cactus) version of nova, instance types can only be created by the nova administrator through the nova-manage command. Future versions of nova (in concert with the OpenStack API or EC2 API), may expose this functionality directly to users. + +Basic Management +---------------- + +Instance types / flavor are managed through the nova-manage binary with +the "instance_type" command and an appropriate subcommand. Note that you can also use +the "flavor" command as a synonym for "instance_types". + +To see all currently active instance types, use the list subcommand:: + + # nova-manage instance_type list + m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + +By default, the list subcommand only shows active instance types. To see all instance types +(even those deleted), add the argument 1 after the list subcommand like so:: + + # nova-manage instance_type list 1 + m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.deleted: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + +To create an instance type, use the "create" subcommand with the following positional arguments: + * memory (expressed in megabytes) + * vcpu(s) (integer) + * local storage (expressed in gigabytes) + * flavorid (unique integer) + * swap space (expressed in megabytes, defaults to zero, optional) + * RXTX quotas (expressed in gigabytes, defaults to zero, optional) + * RXTX cap (expressed in gigabytes, defaults to zero, optional) + +The following example creates an instance type named "m1.xxlarge":: + + # nova-manage instance_type create m1.xxlarge 32768 16 320 0 0 0 + m1.xxlarge created + +To delete an instance type, use the "delete" subcommand and specify the name:: + + # nova-manage instance_type delete m1.xxlarge + m1.xxlarge deleted + +Please note that the "delete" command only marks the instance type as +inactive in the database; it does not actually remove the instance type. This is done +to preserve the instance type definition for long running instances (which may not +terminate for months or years). If you are sure that you want to delete this instance +type from the database, pass the "--purge" flag after the name:: + + # nova-manage instance_type delete m1.xxlarge --purge + m1.xxlarge deleted -- cgit From 7ad5fe27144e592df7e794a0748301d41603377e Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Mon, 28 Feb 2011 13:16:07 -0800 Subject: refactored nova-manage list (-all, <name>) and fixed docs --- bin/nova-manage | 37 +++++++++++++------------- doc/source/man/novamanage.rst | 35 ++++++++++++++++++++++++ doc/source/runnova/managing.instance.types.rst | 14 ++++++++-- nova/db/sqlalchemy/api.py | 18 +++++++------ 4 files changed, 75 insertions(+), 29 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index b977eb94d..0b71da41e 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -666,8 +666,9 @@ class InstanceTypeCommands(object): """Class for managing instance types / flavors.""" def _print_instance_types(self, n, val): + deleted = ('', ', inactive')[val["deleted"] == 1] print ("%s: Memory: %sMB, VCPUS: %s, Storage: %sGB, FlavorID: %s, " - "Swap: %sGB, RXTX Quota: %sGB, RXTX Cap: %sMB") % ( + "Swap: %sGB, RXTX Quota: %sGB, RXTX Cap: %sMB%s") % ( n, val["memory_mb"], val["vcpus"], @@ -675,7 +676,8 @@ class InstanceTypeCommands(object): val["flavorid"], val["swap"], val["rxtx_quota"], - val["rxtx_cap"]) + val["rxtx_cap"], + deleted) def create( self, @@ -688,8 +690,8 @@ class InstanceTypeCommands(object): rxtx_quota=0, rxtx_cap=0): """Creates instance types / flavors - arguments: name memory vcpus local_gb flavorid swap rxtx_quota - rxtx_cap + arguments: name memory vcpus local_gb flavorid [swap] [rxtx_quota] + [rxtx_cap] """ try: instance_types.create( @@ -736,25 +738,22 @@ class InstanceTypeCommands(object): print "%s %s" % (name, verb) def list(self, name=None): - """Lists all or specific instance types / flavors + """Lists all active or specific instance types / flavors arguments: [name]""" - if name == None: - try: + try: + if name == None: inst_types = instance_types.get_all_types() - except exception.NotFound, e: - print e - sys.exit(1) + elif name == "--all": + inst_types = instance_types.get_all_types(1) else: - for k, v in inst_types.iteritems(): - self._print_instance_types(k, v) - else: - try: inst_types = instance_types.get_instance_type(name) - except exception.NotFound, e: - print e - sys.exit(1) - else: - self._print_instance_types(name, inst_types) + except exception.DBError, e: + _db_error(e) + if isinstance(inst_types.values()[0], dict): + for k, v in inst_types.iteritems(): + self._print_instance_types(k, v) + else: + self._print_instance_types(name, inst_types) CATEGORIES = [ diff --git a/doc/source/man/novamanage.rst b/doc/source/man/novamanage.rst index bb9d7a7fe..94e93129a 100644 --- a/doc/source/man/novamanage.rst +++ b/doc/source/man/novamanage.rst @@ -179,6 +179,41 @@ Nova Floating IPs Displays a list of all floating IP addresses. +Nova Flavor +~~~~~~~~~~~ + +``nova-manage flavor list`` + + Outputs a list of all active flavors to the screen. + +``nova-manage flavor list --all`` + + Outputs a list of all flavors (active and inactive) to the screen. + +``nova-manage flavor create <name> <memory> <vCPU> <local_storage> <flavorID> <swap> <RXTX Quota> <RXTX Cap>`` + + creates a flavor with the following positional arguments: + * memory (expressed in megabytes) + * vcpu(s) (integer) + * local storage (expressed in gigabytes) + * flavorid (unique integer) + * swap space (expressed in megabytes, defaults to zero, optional) + * RXTX quotas (expressed in gigabytes, defaults to zero, optional) + * RXTX cap (expressed in gigabytes, defaults to zero, optional) + +``nova-manage flavor delete <name>`` + + Delete the flavor with the name <name>. This marks the flavor as inactive and cannot be launched. However, the record stays in the database for archival and billing purposes. + +``nova-manage flavor delete <name> --purge`` + + Purges the flavor with the name <name>. This removes this flavor from the database. + + +Nova Instance_type +~~~~~~~~~~~~~~~~~~ + +Nova instance_type is a alias for FILES ======== diff --git a/doc/source/runnova/managing.instance.types.rst b/doc/source/runnova/managing.instance.types.rst index 30a47e47d..656268f8e 100644 --- a/doc/source/runnova/managing.instance.types.rst +++ b/doc/source/runnova/managing.instance.types.rst @@ -44,9 +44,9 @@ To see all currently active instance types, use the list subcommand:: m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB By default, the list subcommand only shows active instance types. To see all instance types -(even those deleted), add the argument 1 after the list subcommand like so:: +(even those deleted), use the listall subcommand:: - # nova-manage instance_type list 1 + # nova-manage instance_type listall m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB @@ -81,3 +81,13 @@ type from the database, pass the "--purge" flag after the name:: # nova-manage instance_type delete m1.xxlarge --purge m1.xxlarge deleted + +To see all instance types (inactive + active), use the list subcommand with the "--all" flag:: + + # nova-manage instance_type list --all + m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index d47b11891..f4cd16d9a 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2090,16 +2090,18 @@ def instance_type_create(context, values): @require_context def instance_type_get_all(context, inactive=0): """ - Returns a dict describing all non-deleted instance_types with name as key: - {'m1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3)} + Returns a dict describing all instance_types with name as key. """ session = get_session() - inst_types = session.query(models.InstanceTypes).\ - filter_by(deleted=inactive).\ - order_by("name").\ - all() + if inactive: + inst_types = session.query(models.InstanceTypes).\ + order_by("name").\ + all() + else: + inst_types = session.query(models.InstanceTypes).\ + filter_by(deleted=inactive).\ + order_by("name").\ + all() if inst_types: inst_dict = {} for i in inst_types: -- cgit From d5736e925f288462f6325130be0af49f0ace5884 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Mon, 28 Feb 2011 23:31:09 +0100 Subject: Add a lock_path flag for lock files. --- nova/flags.py | 2 ++ nova/utils.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/nova/flags.py b/nova/flags.py index 8cf199b2f..213d4d4e1 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -321,6 +321,8 @@ DEFINE_integer('auth_token_ttl', 3600, 'Seconds for auth tokens to linger') DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../'), "Top-level directory for maintaining nova's state") +DEFINE_string('lock_path', os.path.join(os.path.dirname(__file__), '../'), + "Directory for lock files") DEFINE_string('logdir', None, 'output to a per-service log file in named ' 'directory') diff --git a/nova/utils.py b/nova/utils.py index cb1ea5a7d..48f12350f 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -44,11 +44,13 @@ from eventlet.green import subprocess from nova import exception from nova.exception import ProcessExecutionError +from nova import flags from nova import log as logging LOG = logging.getLogger("nova.utils") TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" +FLAGS = flags.FLAGS def import_class(import_str): @@ -495,7 +497,8 @@ def loads(s): def synchronized(name): def wrap(f): def inner(*args, **kwargs): - lock = lockfile.FileLock('nova-%s.lock' % name) + lock = lockfile.FileLock(os.path.join(FLAGS.lock_path, + 'nova-%s.lock' % name)) with lock: return f(*args, **kwargs) return inner -- cgit From 2e12ee1c98241ac38b59e93fb7b4b05b66ccadc9 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Mon, 28 Feb 2011 15:05:50 -0800 Subject: fixed pep8 --- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 7531af4ec..aa12d432a 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -207,8 +207,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): 'transfer-encoding': 'chunked', 'x-image-meta-is_public': 'True', 'x-image-meta-status': 'queued', - 'x-image-meta-type': 'vhd' - } + 'x-image-meta-type': 'vhd'} for header, value in headers.iteritems(): conn.putheader(header, value) conn.endheaders() -- cgit From aafd83233a675ccf5f3c4e737d966652e45a0ecb Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Mon, 28 Feb 2011 16:28:46 -0800 Subject: replaced ugly INSTANCE_TYPE constant with (slightly less ugly) stubs --- nova/test.py | 8 -------- nova/tests/db/fakes.py | 20 ++++++++++++++++++-- nova/tests/test_quota.py | 17 ++++++++++++++--- nova/tests/test_xenapi.py | 2 +- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/nova/test.py b/nova/test.py index 0329383b8..d8a47464f 100644 --- a/nova/test.py +++ b/nova/test.py @@ -48,14 +48,6 @@ flags.DEFINE_bool('fake_tests', True, 'should we use everything for testing') -INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - - def skip_if_fake(func): """Decorator that skips a test if running in fake mode""" def _skipper(*args, **kw): diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index d9a5032ee..d760dc456 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -22,12 +22,20 @@ import time from nova import db from nova import test from nova import utils -from nova.compute import instance_types def stub_out_db_instance_api(stubs): """ Stubs out the db API for creating Instances """ + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': + dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': + dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + class FakeModel(object): """ Stubs out for model """ def __init__(self, values): @@ -42,10 +50,16 @@ def stub_out_db_instance_api(stubs): else: raise NotImplementedError() + def fake_instance_type_get_all(context, inactive=0): + return INSTANCE_TYPES + + def fake_instance_type_get_by_name(context, name): + return INSTANCE_TYPES[name] + def fake_instance_create(values): """ Stubs out the db.instance_create method """ - type_data = test.INSTANCE_TYPES[values['instance_type']] + type_data = INSTANCE_TYPES[values['instance_type']] base_options = { 'name': values['name'], @@ -74,3 +88,5 @@ def stub_out_db_instance_api(stubs): stubs.Set(db, 'instance_create', fake_instance_create) stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) + stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all) + stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name) diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index faafe2d31..4ecb36b54 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -74,19 +74,30 @@ class QuotaTestCase(test.TestCase): vol['size'] = size return db.volume_create(self.context, vol)['id'] + def _get_instance_type(self, name): + instance_types = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': + dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': + dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + return instance_types[name] + def test_quota_overrides(self): """Make sure overriding a projects quotas works""" num_instances = quota.allowed_instances(self.context, 100, - test.INSTANCE_TYPES['m1.small']) + self._get_instance_type('m1.small')) self.assertEqual(num_instances, 2) db.quota_create(self.context, {'project_id': self.project.id, 'instances': 10}) num_instances = quota.allowed_instances(self.context, 100, - test.INSTANCE_TYPES['m1.small']) + self._get_instance_type('m1.small')) self.assertEqual(num_instances, 4) db.quota_update(self.context, self.project.id, {'cores': 100}) num_instances = quota.allowed_instances(self.context, 100, - test.INSTANCE_TYPES['m1.small']) + self._get_instance_type('m1.small')) self.assertEqual(num_instances, 10) # metadata_items diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 27131c45e..106c0bd6f 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -233,7 +233,7 @@ class XenAPIVMTestCase(test.TestCase): vm = vms[0] # Check that m1.large above turned into the right thing. - instance_type = test.INSTANCE_TYPES['m1.large'] + instance_type = db.instance_type_get_by_name(conn, 'm1.large') mem_kib = long(instance_type['memory_mb']) << 10 mem_bytes = str(mem_kib << 10) vcpus = instance_type['vcpus'] -- cgit From 4572ffcf734b734870b90497063fc27e7642f67c Mon Sep 17 00:00:00 2001 From: Naveed Massjouni <naveedm9@gmail.com> Date: Mon, 28 Feb 2011 19:56:46 -0500 Subject: No reason to initialize metadata twice. --- nova/api/openstack/servers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 7d20f681b..69273ad7b 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -72,8 +72,6 @@ def _translate_detail_keys(inst): public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address') inst_dict['addresses']['public'] = public_ips - inst_dict['metadata'] = {} - # Return the metadata as a dictionary metadata = {} for item in inst['metadata']: -- cgit From 69779dcdc5584fafa95974f263cef14912a12ed7 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Mon, 28 Feb 2011 17:06:35 -0800 Subject: refactored adminclient --- nova/api/ec2/admin.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index d867e5da9..51a06bb26 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -29,7 +29,6 @@ from nova import flags from nova import log as logging from nova import utils from nova.auth import manager -from nova.compute import instance_types FLAGS = flags.FLAGS @@ -115,21 +114,10 @@ class AdminController(object): def __str__(self): return 'AdminController' - # FIXME(kpepple) this is untested code path. def describe_instance_types(self, _context, **_kwargs): """Returns all active instance types data (vcpus, memory, etc.)""" - # return {'instanceTypeSet': [instance_dict(n, v) for n, v in - # instance_types.INSTANCE_TYPES.iteritems()]} - # return {'instanceTypeSet': - # [for i in db.instance_type_get_all(): instance_dict(i)]} return {'instanceTypeSet': [db.instance_type_get_all(_context)]} - # FIXME(kpepple) this is untested code path. - def describe_instance_type(self, _context, name, **_kwargs): - """Returns a specific active instance types data""" - return {'instanceTypeSet': - [instance_dict(db.instance_type_get_by_name(name))]} - def describe_user(self, _context, name, **_kwargs): """Returns user data, including access and secret keys.""" return user_dict(manager.AuthManager().get_user(name)) -- cgit From 160d5751486ef7658089868eaeba23b87b695925 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Mon, 28 Feb 2011 18:32:55 -0800 Subject: updated docs --- doc/source/api/autoindex.rst | 6 ++++++ doc/source/man/novamanage.rst | 5 +++-- doc/source/nova.concepts.rst | 5 +++++ doc/source/runnova/managing.instance.types.rst | 29 ++++++++++---------------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/doc/source/api/autoindex.rst b/doc/source/api/autoindex.rst index 41fc1f4a9..329a465db 100644 --- a/doc/source/api/autoindex.rst +++ b/doc/source/api/autoindex.rst @@ -43,6 +43,9 @@ nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst + nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst + nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst + nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst nova..db.sqlalchemy.migration.rst nova..db.sqlalchemy.models.rst nova..db.sqlalchemy.session.rst @@ -101,6 +104,7 @@ nova..tests.test_console.rst nova..tests.test_direct.rst nova..tests.test_flags.rst + nova..tests.test_instance_types.rst nova..tests.test_localization.rst nova..tests.test_log.rst nova..tests.test_middleware.rst @@ -110,7 +114,9 @@ nova..tests.test_rpc.rst nova..tests.test_scheduler.rst nova..tests.test_service.rst + nova..tests.test_test.rst nova..tests.test_twistd.rst + nova..tests.test_utils.rst nova..tests.test_virt.rst nova..tests.test_volume.rst nova..tests.test_xenapi.rst diff --git a/doc/source/man/novamanage.rst b/doc/source/man/novamanage.rst index 94e93129a..17ba91bef 100644 --- a/doc/source/man/novamanage.rst +++ b/doc/source/man/novamanage.rst @@ -190,7 +190,7 @@ Nova Flavor Outputs a list of all flavors (active and inactive) to the screen. -``nova-manage flavor create <name> <memory> <vCPU> <local_storage> <flavorID> <swap> <RXTX Quota> <RXTX Cap>`` +``nova-manage flavor create <name> <memory> <vCPU> <local_storage> <flavorID> <(optional) swap> <(optional) RXTX Quota> <(optional) RXTX Cap>`` creates a flavor with the following positional arguments: * memory (expressed in megabytes) @@ -213,7 +213,8 @@ Nova Flavor Nova Instance_type ~~~~~~~~~~~~~~~~~~ -Nova instance_type is a alias for +The instance_type command is provided as an alias for the flavor command. All the same subcommands and arguments from nova-manage flavor can be used. + FILES ======== diff --git a/doc/source/nova.concepts.rst b/doc/source/nova.concepts.rst index e9687dc98..45cc4b879 100644 --- a/doc/source/nova.concepts.rst +++ b/doc/source/nova.concepts.rst @@ -64,6 +64,11 @@ Concept: Instances An 'instance' is a word for a virtual machine that runs inside the cloud. +Concept: Instance Type +---------------------- + +An 'instance type' describes the compute, memory and storage capacity of nova computing instances. In layman terms, this is the size (in terms of vCPUs, RAM, etc.) of the virtual server that you will be launching. + Concept: System Architecture ---------------------------- diff --git a/doc/source/runnova/managing.instance.types.rst b/doc/source/runnova/managing.instance.types.rst index 656268f8e..37bb2e99e 100644 --- a/doc/source/runnova/managing.instance.types.rst +++ b/doc/source/runnova/managing.instance.types.rst @@ -1,4 +1,4 @@ - +.. Copyright 2010-2011 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. @@ -21,9 +21,13 @@ Managing Instance Types and Flavors What are Instance Types or Flavors ? ------------------------------------ -Instance types are the container descriptions (meta-data) about instances. In layman terms, this is the size of the instance (vCPUs, RAM, etc.) that you will be launching. In the EC2 API, these are called by names such as "m1.large" or "m1.tiny", while the OpenStack API terms these "flavors" with names like "" +Instance types describe the compute, memory and storage capacity of nova computing instances. In layman terms, this is the size (in terms of vCPUs, RAM, etc.) of the virtual server that you will be launching. In the EC2 API, these are called by names such as "m1.large" or "m1.tiny", while the OpenStack API terms these "flavors" with names like "512 MB Server". + +In Nova, "flavor" and "instance type" are equivalent terms. When you create an EC2 instance type, you are also creating a OpenStack API flavor. To reduce repetition, for the rest of this document I will refer to these as instance types. -Flavors are simply the name for instance types used in the OpenStack API. In nova, these are equivalent terms, so when you create an instance type you are also creating a flavor. For the rest of this document, I will refer to these as instance types. +Instance types can be in either the active or inactive state: + * Active instance types are available to be used for launching instances + * Inactive instance types are not available for launching instances In the current (Cactus) version of nova, instance types can only be created by the nova administrator through the nova-manage command. Future versions of nova (in concert with the OpenStack API or EC2 API), may expose this functionality directly to users. @@ -43,16 +47,15 @@ To see all currently active instance types, use the list subcommand:: m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB -By default, the list subcommand only shows active instance types. To see all instance types -(even those deleted), use the listall subcommand:: +By default, the list subcommand only shows active instance types. To see all instance types (inactive and active), use the list subcommand with the "--all" flag:: - # nova-manage instance_type listall + # nova-manage instance_type list --all m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.deleted: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB + m1.deleted: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB, inactive To create an instance type, use the "create" subcommand with the following positional arguments: * memory (expressed in megabytes) @@ -80,14 +83,4 @@ terminate for months or years). If you are sure that you want to delete this ins type from the database, pass the "--purge" flag after the name:: # nova-manage instance_type delete m1.xxlarge --purge - m1.xxlarge deleted - -To see all instance types (inactive + active), use the list subcommand with the "--all" flag:: - - # nova-manage instance_type list --all - m1.medium: Memory: 4096MB, VCPUS: 2, Storage: 40GB, FlavorID: 3, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.large: Memory: 8192MB, VCPUS: 4, Storage: 80GB, FlavorID: 4, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.tiny: Memory: 512MB, VCPUS: 1, Storage: 0GB, FlavorID: 1, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.xlarge: Memory: 16384MB, VCPUS: 8, Storage: 160GB, FlavorID: 5, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - m1.small: Memory: 2048MB, VCPUS: 1, Storage: 20GB, FlavorID: 2, Swap: 0GB, RXTX Quota: 0GB, RXTX Cap: 0MB - + m1.xxlarge purged -- cgit From 6ddada8ed734dd91502a3f86eed8fb66803d08f3 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Mon, 28 Feb 2011 18:33:33 -0800 Subject: updated docs --- doc/.autogenerated | 276 ++--------------------------------------------------- 1 file changed, 6 insertions(+), 270 deletions(-) diff --git a/doc/.autogenerated b/doc/.autogenerated index e4c98ec9b..b11735994 100644 --- a/doc/.autogenerated +++ b/doc/.autogenerated @@ -40,6 +40,9 @@ source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst source/api/nova..db.sqlalchemy.migration.rst source/api/nova..db.sqlalchemy.models.rst source/api/nova..db.sqlalchemy.session.rst @@ -98,6 +101,7 @@ source/api/nova..tests.test_compute.rst source/api/nova..tests.test_console.rst source/api/nova..tests.test_direct.rst source/api/nova..tests.test_flags.rst +source/api/nova..tests.test_instance_types.rst source/api/nova..tests.test_localization.rst source/api/nova..tests.test_log.rst source/api/nova..tests.test_middleware.rst @@ -107,7 +111,9 @@ source/api/nova..tests.test_quota.rst source/api/nova..tests.test_rpc.rst source/api/nova..tests.test_scheduler.rst source/api/nova..tests.test_service.rst +source/api/nova..tests.test_test.rst source/api/nova..tests.test_twistd.rst +source/api/nova..tests.test_utils.rst source/api/nova..tests.test_virt.rst source/api/nova..tests.test_volume.rst source/api/nova..tests.test_xenapi.rst @@ -134,273 +140,3 @@ source/api/nova..volume.manager.rst source/api/nova..volume.san.rst source/api/nova..wsgi.rst source/api/autoindex.rst -source/api/nova..adminclient.rst -source/api/nova..api.direct.rst -source/api/nova..api.ec2.admin.rst -source/api/nova..api.ec2.apirequest.rst -source/api/nova..api.ec2.cloud.rst -source/api/nova..api.ec2.metadatarequesthandler.rst -source/api/nova..api.openstack.auth.rst -source/api/nova..api.openstack.backup_schedules.rst -source/api/nova..api.openstack.common.rst -source/api/nova..api.openstack.consoles.rst -source/api/nova..api.openstack.faults.rst -source/api/nova..api.openstack.flavors.rst -source/api/nova..api.openstack.images.rst -source/api/nova..api.openstack.servers.rst -source/api/nova..api.openstack.shared_ip_groups.rst -source/api/nova..api.openstack.zones.rst -source/api/nova..auth.dbdriver.rst -source/api/nova..auth.fakeldap.rst -source/api/nova..auth.ldapdriver.rst -source/api/nova..auth.manager.rst -source/api/nova..auth.signer.rst -source/api/nova..cloudpipe.pipelib.rst -source/api/nova..compute.api.rst -source/api/nova..compute.instance_types.rst -source/api/nova..compute.manager.rst -source/api/nova..compute.monitor.rst -source/api/nova..compute.power_state.rst -source/api/nova..console.api.rst -source/api/nova..console.fake.rst -source/api/nova..console.manager.rst -source/api/nova..console.xvp.rst -source/api/nova..context.rst -source/api/nova..crypto.rst -source/api/nova..db.api.rst -source/api/nova..db.base.rst -source/api/nova..db.migration.rst -source/api/nova..db.sqlalchemy.api.rst -source/api/nova..db.sqlalchemy.migrate_repo.manage.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst -source/api/nova..db.sqlalchemy.migration.rst -source/api/nova..db.sqlalchemy.models.rst -source/api/nova..db.sqlalchemy.session.rst -source/api/nova..exception.rst -source/api/nova..fakememcache.rst -source/api/nova..fakerabbit.rst -source/api/nova..flags.rst -source/api/nova..image.glance.rst -source/api/nova..image.local.rst -source/api/nova..image.s3.rst -source/api/nova..image.service.rst -source/api/nova..log.rst -source/api/nova..manager.rst -source/api/nova..network.api.rst -source/api/nova..network.linux_net.rst -source/api/nova..network.manager.rst -source/api/nova..objectstore.bucket.rst -source/api/nova..objectstore.handler.rst -source/api/nova..objectstore.image.rst -source/api/nova..objectstore.stored.rst -source/api/nova..quota.rst -source/api/nova..rpc.rst -source/api/nova..scheduler.chance.rst -source/api/nova..scheduler.driver.rst -source/api/nova..scheduler.manager.rst -source/api/nova..scheduler.simple.rst -source/api/nova..scheduler.zone.rst -source/api/nova..service.rst -source/api/nova..test.rst -source/api/nova..tests.api.openstack.fakes.rst -source/api/nova..tests.api.openstack.test_adminapi.rst -source/api/nova..tests.api.openstack.test_api.rst -source/api/nova..tests.api.openstack.test_auth.rst -source/api/nova..tests.api.openstack.test_common.rst -source/api/nova..tests.api.openstack.test_faults.rst -source/api/nova..tests.api.openstack.test_flavors.rst -source/api/nova..tests.api.openstack.test_images.rst -source/api/nova..tests.api.openstack.test_ratelimiting.rst -source/api/nova..tests.api.openstack.test_servers.rst -source/api/nova..tests.api.openstack.test_shared_ip_groups.rst -source/api/nova..tests.api.openstack.test_zones.rst -source/api/nova..tests.api.test_wsgi.rst -source/api/nova..tests.db.fakes.rst -source/api/nova..tests.declare_flags.rst -source/api/nova..tests.fake_flags.rst -source/api/nova..tests.glance.stubs.rst -source/api/nova..tests.hyperv_unittest.rst -source/api/nova..tests.objectstore_unittest.rst -source/api/nova..tests.real_flags.rst -source/api/nova..tests.runtime_flags.rst -source/api/nova..tests.test_access.rst -source/api/nova..tests.test_api.rst -source/api/nova..tests.test_auth.rst -source/api/nova..tests.test_cloud.rst -source/api/nova..tests.test_compute.rst -source/api/nova..tests.test_console.rst -source/api/nova..tests.test_direct.rst -source/api/nova..tests.test_flags.rst -source/api/nova..tests.test_localization.rst -source/api/nova..tests.test_log.rst -source/api/nova..tests.test_middleware.rst -source/api/nova..tests.test_misc.rst -source/api/nova..tests.test_network.rst -source/api/nova..tests.test_quota.rst -source/api/nova..tests.test_rpc.rst -source/api/nova..tests.test_scheduler.rst -source/api/nova..tests.test_service.rst -source/api/nova..tests.test_twistd.rst -source/api/nova..tests.test_virt.rst -source/api/nova..tests.test_volume.rst -source/api/nova..tests.test_xenapi.rst -source/api/nova..tests.xenapi.stubs.rst -source/api/nova..twistd.rst -source/api/nova..utils.rst -source/api/nova..version.rst -source/api/nova..virt.connection.rst -source/api/nova..virt.disk.rst -source/api/nova..virt.fake.rst -source/api/nova..virt.hyperv.rst -source/api/nova..virt.images.rst -source/api/nova..virt.libvirt_conn.rst -source/api/nova..virt.xenapi.fake.rst -source/api/nova..virt.xenapi.network_utils.rst -source/api/nova..virt.xenapi.vm_utils.rst -source/api/nova..virt.xenapi.vmops.rst -source/api/nova..virt.xenapi.volume_utils.rst -source/api/nova..virt.xenapi.volumeops.rst -source/api/nova..virt.xenapi_conn.rst -source/api/nova..volume.api.rst -source/api/nova..volume.driver.rst -source/api/nova..volume.manager.rst -source/api/nova..volume.san.rst -source/api/nova..wsgi.rst -source/api/nova..adminclient.rst -source/api/nova..api.direct.rst -source/api/nova..api.ec2.admin.rst -source/api/nova..api.ec2.apirequest.rst -source/api/nova..api.ec2.cloud.rst -source/api/nova..api.ec2.metadatarequesthandler.rst -source/api/nova..api.openstack.auth.rst -source/api/nova..api.openstack.backup_schedules.rst -source/api/nova..api.openstack.common.rst -source/api/nova..api.openstack.consoles.rst -source/api/nova..api.openstack.faults.rst -source/api/nova..api.openstack.flavors.rst -source/api/nova..api.openstack.images.rst -source/api/nova..api.openstack.servers.rst -source/api/nova..api.openstack.shared_ip_groups.rst -source/api/nova..api.openstack.zones.rst -source/api/nova..auth.dbdriver.rst -source/api/nova..auth.fakeldap.rst -source/api/nova..auth.ldapdriver.rst -source/api/nova..auth.manager.rst -source/api/nova..auth.signer.rst -source/api/nova..cloudpipe.pipelib.rst -source/api/nova..compute.api.rst -source/api/nova..compute.instance_types.rst -source/api/nova..compute.manager.rst -source/api/nova..compute.monitor.rst -source/api/nova..compute.power_state.rst -source/api/nova..console.api.rst -source/api/nova..console.fake.rst -source/api/nova..console.manager.rst -source/api/nova..console.xvp.rst -source/api/nova..context.rst -source/api/nova..crypto.rst -source/api/nova..db.api.rst -source/api/nova..db.base.rst -source/api/nova..db.migration.rst -source/api/nova..db.sqlalchemy.api.rst -source/api/nova..db.sqlalchemy.migrate_repo.manage.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst -source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst -source/api/nova..db.sqlalchemy.migration.rst -source/api/nova..db.sqlalchemy.models.rst -source/api/nova..db.sqlalchemy.session.rst -source/api/nova..exception.rst -source/api/nova..fakememcache.rst -source/api/nova..fakerabbit.rst -source/api/nova..flags.rst -source/api/nova..image.glance.rst -source/api/nova..image.local.rst -source/api/nova..image.s3.rst -source/api/nova..image.service.rst -source/api/nova..log.rst -source/api/nova..manager.rst -source/api/nova..network.api.rst -source/api/nova..network.linux_net.rst -source/api/nova..network.manager.rst -source/api/nova..objectstore.bucket.rst -source/api/nova..objectstore.handler.rst -source/api/nova..objectstore.image.rst -source/api/nova..objectstore.stored.rst -source/api/nova..quota.rst -source/api/nova..rpc.rst -source/api/nova..scheduler.chance.rst -source/api/nova..scheduler.driver.rst -source/api/nova..scheduler.manager.rst -source/api/nova..scheduler.simple.rst -source/api/nova..scheduler.zone.rst -source/api/nova..service.rst -source/api/nova..test.rst -source/api/nova..tests.api.openstack.fakes.rst -source/api/nova..tests.api.openstack.test_adminapi.rst -source/api/nova..tests.api.openstack.test_api.rst -source/api/nova..tests.api.openstack.test_auth.rst -source/api/nova..tests.api.openstack.test_common.rst -source/api/nova..tests.api.openstack.test_faults.rst -source/api/nova..tests.api.openstack.test_flavors.rst -source/api/nova..tests.api.openstack.test_images.rst -source/api/nova..tests.api.openstack.test_ratelimiting.rst -source/api/nova..tests.api.openstack.test_servers.rst -source/api/nova..tests.api.openstack.test_shared_ip_groups.rst -source/api/nova..tests.api.openstack.test_zones.rst -source/api/nova..tests.api.test_wsgi.rst -source/api/nova..tests.db.fakes.rst -source/api/nova..tests.declare_flags.rst -source/api/nova..tests.fake_flags.rst -source/api/nova..tests.glance.stubs.rst -source/api/nova..tests.hyperv_unittest.rst -source/api/nova..tests.objectstore_unittest.rst -source/api/nova..tests.real_flags.rst -source/api/nova..tests.runtime_flags.rst -source/api/nova..tests.test_access.rst -source/api/nova..tests.test_api.rst -source/api/nova..tests.test_auth.rst -source/api/nova..tests.test_cloud.rst -source/api/nova..tests.test_compute.rst -source/api/nova..tests.test_console.rst -source/api/nova..tests.test_direct.rst -source/api/nova..tests.test_flags.rst -source/api/nova..tests.test_localization.rst -source/api/nova..tests.test_log.rst -source/api/nova..tests.test_middleware.rst -source/api/nova..tests.test_misc.rst -source/api/nova..tests.test_network.rst -source/api/nova..tests.test_quota.rst -source/api/nova..tests.test_rpc.rst -source/api/nova..tests.test_scheduler.rst -source/api/nova..tests.test_service.rst -source/api/nova..tests.test_twistd.rst -source/api/nova..tests.test_virt.rst -source/api/nova..tests.test_volume.rst -source/api/nova..tests.test_xenapi.rst -source/api/nova..tests.xenapi.stubs.rst -source/api/nova..twistd.rst -source/api/nova..utils.rst -source/api/nova..version.rst -source/api/nova..virt.connection.rst -source/api/nova..virt.disk.rst -source/api/nova..virt.fake.rst -source/api/nova..virt.hyperv.rst -source/api/nova..virt.images.rst -source/api/nova..virt.libvirt_conn.rst -source/api/nova..virt.xenapi.fake.rst -source/api/nova..virt.xenapi.network_utils.rst -source/api/nova..virt.xenapi.vm_utils.rst -source/api/nova..virt.xenapi.vmops.rst -source/api/nova..virt.xenapi.volume_utils.rst -source/api/nova..virt.xenapi.volumeops.rst -source/api/nova..virt.xenapi_conn.rst -source/api/nova..volume.api.rst -source/api/nova..volume.driver.rst -source/api/nova..volume.manager.rst -source/api/nova..volume.san.rst -source/api/nova..wsgi.rst -- cgit From fbcbf5ef805748bea29e4135ee8989830064c273 Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Tue, 1 Mar 2011 10:55:13 -0600 Subject: Fixed trunk merge issues --- nova/virt/xenapi/vm_utils.py | 2 +- nova/virt/xenapi/vmops.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 527d8700c..977c19359 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -343,7 +343,7 @@ class VMHelper(HelperBase): kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'download_vhd', kwargs) - vdi_uuid = session.wait_for_task(instance_id, task) + vdi_uuid = session.wait_for_task(task, instance_id) scan_sr(session, instance_id, sr_ref) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 43b63bfd8..a3379fa7f 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -411,7 +411,7 @@ class VMOps(object): args = {'kernel-file': kernel, 'ramdisk-file': ramdisk} task = self._session.async_call_plugin( 'glance', 'remove_kernel_ramdisk', args) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) LOG.debug(_("kernel/ramdisk files removed")) -- cgit From 282a18a4c15f066e371596104f783f522309c5ee Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Tue, 1 Mar 2011 10:40:56 -0800 Subject: corrected copyrights for new files --- doc/source/runnova/managing.instance.types.rst | 4 +--- nova/compute/instance_types.py | 1 + nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py | 1 + nova/tests/test_instance_types.py | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/source/runnova/managing.instance.types.rst b/doc/source/runnova/managing.instance.types.rst index 37bb2e99e..746077716 100644 --- a/doc/source/runnova/managing.instance.types.rst +++ b/doc/source/runnova/managing.instance.types.rst @@ -1,7 +1,5 @@ .. - Copyright 2010-2011 United States Government as represented by the - Administrator of the National Aeronautics and Space Administration. - All Rights Reserved. + Copyright 2011 Ken Pepple 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/compute/instance_types.py b/nova/compute/instance_types.py index ce4b25964..7d401fc86 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -4,6 +4,7 @@ # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # Copyright (c) 2010 Citrix Systems, Inc. +# Copyright 2011 Ken Pepple # # 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/migrate_repo/versions/007_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py index fec191214..66609054e 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py @@ -1,5 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright 2011 Ken Pepple # 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 diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 1b192ca28..edc538879 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -1,5 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright 2011 Ken Pepple # 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 -- cgit From 93b69176277217a3cfae738dd328e649081a370f Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Tue, 1 Mar 2011 13:26:31 -0600 Subject: Review feedback --- nova/compute/manager.py | 42 ++++++++++++++++++++++++------------------ nova/virt/xenapi/vmops.py | 42 ++++++++++++++++++++++-------------------- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index ef7ed55c9..3af97683f 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -370,16 +370,19 @@ class ComputeManager(manager.Manager): context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) LOG.audit(_('instance %s: rescuing'), instance_id, context=context) - self.db.instance_set_state(context, - instance_id, - power_state.NOSTATE, - 'rescuing') + self.db.instance_set_state( + context, + instance_id, + power_state.NOSTATE, + 'rescuing') self.network_manager.setup_compute_network(context, instance_id) - self.driver.rescue(instance_ref, - lambda result: self._update_state_callback(self, - context, - instance_id, - result)) + self.driver.rescue( + instance_ref, + lambda result: self._update_state_callback( + self, + context, + instance_id, + result)) self._update_state(context, instance_id) @exception.wrap_exception @@ -389,15 +392,18 @@ class ComputeManager(manager.Manager): context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) LOG.audit(_('instance %s: unrescuing'), instance_id, context=context) - self.db.instance_set_state(context, - instance_id, - power_state.NOSTATE, - 'unrescuing') - self.driver.unrescue(instance_ref, - lambda result: self._update_state_callback(self, - context, - instance_id, - result)) + self.db.instance_set_state( + context, + instance_id, + power_state.NOSTATE, + 'unrescuing') + self.driver.unrescue( + instance_ref, + lambda result: self._update_state_callback( + self, + context, + instance_id, + result)) self._update_state(context, instance_id) @staticmethod diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a3379fa7f..1c58f529d 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -202,18 +202,19 @@ class VMOps(object): _('Instance not present %s') % instance_name) return vm - def _bootlock(self, vm, unlock=False): + def _acquire_bootlock(self, vm): """Prevent an instance from booting""" - if unlock: - self._session.call_xenapi( - "VM.remove_from_blocked_operations", - vm, - "start") - else: - self._session.call_xenapi( - "VM.set_blocked_operations", - vm, - {"start": ""}) + self._session.call_xenapi( + "VM.set_blocked_operations", + vm, + {"start": ""}) + + def _release_bootlock(self, vm): + """Allow an instance to boot""" + self._session.call_xenapi( + "VM.remove_from_blocked_operations", + vm, + "start") def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance @@ -338,7 +339,7 @@ class VMOps(object): raise RuntimeError(resp_dict['message']) return resp_dict['message'] - def _shutdown(self, instance, vm): + def _shutdown(self, instance, vm, hard=True): """Shutdown an instance""" state = self.get_info(instance['name'])['state'] if state == power_state.SHUTDOWN: @@ -350,12 +351,13 @@ class VMOps(object): LOG.debug(_("Shutting down VM for Instance %(instance_id)s") % locals()) try: - try: - task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) - self._session.wait_for_task(task, instance.id) - except self.XenAPI.Failure: + task = None + if hard: task = self._session.call_xenapi("Async.VM.hard_shutdown", vm) - self._session.wait_for_task(task, instance.id) + else: + task = self._session.call_xenapi("Async.VM.clean_shutdown", vm) + + self._session.wait_for_task(task, instance.id) except self.XenAPI.Failure, exc: LOG.exception(exc) @@ -501,7 +503,7 @@ class VMOps(object): vm = self._get_vm_opaque_ref(instance) self._shutdown(instance, vm) - self._bootlock(vm) + self._acquire_bootlock(vm) instance._rescue = True self.spawn(instance) @@ -533,7 +535,7 @@ class VMOps(object): for vbd_ref in vbds: vbd = self._session.get_xenapi().VBD.get_record(vbd_ref) - if vbd["userdevice"] == str(1): + if vbd["userdevice"] == "1": VMHelper.unplug_vbd(self._session, vbd_ref) VMHelper.destroy_vbd(self._session, vbd_ref) @@ -551,7 +553,7 @@ class VMOps(object): task2 = self._session.call_xenapi('Async.VM.destroy', rescue_vm) self._session.wait_for_task(task2, instance.id) - self._bootlock(original_vm, unlock=True) + self._release_bootlock(original_vm) self._start(instance, original_vm) def get_info(self, instance): -- cgit From be9004ffa4c70358c8edda1f33ffe7ba7e1ae1ee Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Tue, 1 Mar 2011 20:49:46 +0100 Subject: Use functools.wraps to make sure wrapped method's metadata (docstring and name) doesn't get mangled. --- nova/tests/test_misc.py | 12 ++++++++++-- nova/utils.py | 6 ++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/nova/tests/test_misc.py b/nova/tests/test_misc.py index 154b6fae6..9f572b58e 100644 --- a/nova/tests/test_misc.py +++ b/nova/tests/test_misc.py @@ -14,11 +14,9 @@ # License for the specific language governing permissions and limitations # under the License. -from datetime import datetime import errno import os import select -import time from nova import test from nova.utils import parse_mailmap, str_dict_replace, synchronized @@ -62,6 +60,16 @@ class ProjectTestCase(test.TestCase): class LockTestCase(test.TestCase): + def test_synchronized_wrapped_function_metadata(self): + @synchronized('whatever') + def foo(): + """Bar""" + pass + self.assertEquals(foo.__doc__, 'Bar', "Wrapped function's docstring " + "got lost") + self.assertEquals(foo.__name__, 'foo', "Wrapped function's name " + "got mangled") + def test_synchronized(self): rpipe, wpipe = os.pipe() pid = os.fork() diff --git a/nova/utils.py b/nova/utils.py index 48f12350f..9929e6fef 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -23,11 +23,14 @@ System-level utilities and helper functions. import base64 import datetime +import functools import inspect import json import lockfile +import netaddr import os import random +import re import socket import string import struct @@ -35,8 +38,6 @@ import sys import time import types from xml.sax import saxutils -import re -import netaddr from eventlet import event from eventlet import greenthread @@ -496,6 +497,7 @@ def loads(s): def synchronized(name): def wrap(f): + @functools.wraps(f) def inner(*args, **kwargs): lock = lockfile.FileLock(os.path.join(FLAGS.lock_path, 'nova-%s.lock' % name)) -- cgit From 58ac632f8e08b248d234deffdb56fe3a33a25130 Mon Sep 17 00:00:00 2001 From: Masanori Itoh <itoumsn@nttdata.co.jp> Date: Thu, 3 Mar 2011 00:12:48 +0900 Subject: Port Todd's lp720157 fix to the current trunk, rev 752. --- nova/api/ec2/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 5adc2c075..5a63dc8da 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -198,6 +198,12 @@ class Requestify(wsgi.Middleware): try: # Raise KeyError if omitted action = req.params['Action'] + # Fix bug lp:720157 for older (version 1) clients + version = req.params['SignatureVersion'] + if int(version) == 1: + non_args.remove('SignatureMethod') + if 'SignatureMethod' in args: + args.pop('SignatureMethod') for non_arg in non_args: # Remove, but raise KeyError if omitted args.pop(non_arg) -- cgit From f617fc087367a3d65bd4b826bf735f65fec9d2fd Mon Sep 17 00:00:00 2001 From: Masanori Itoh <itoumsn@nttdata.co.jp> Date: Thu, 3 Mar 2011 00:28:04 +0900 Subject: Change DescribeKeyPairs response tag from keypairsSet to keySet, and fix lp720133. --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 844ccbe5e..c6309f03c 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -298,7 +298,7 @@ class CloudController(object): 'keyFingerprint': key_pair['fingerprint'], }) - return {'keypairsSet': result} + return {'keySet': result} def create_key_pair(self, context, key_name, **kwargs): LOG.audit(_("Create key pair %s"), key_name, context=context) -- cgit From 6d62f387e39b42821f8a8f6ca560dd47b3bb9c7e Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Wed, 2 Mar 2011 15:42:21 -0600 Subject: Inject IPv6 data into XenStore for instance --- nova/virt/xenapi/vmops.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index bc39aa140..094b41588 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -514,18 +514,30 @@ class VMOps(object): network_IPs = [ip for ip in IPs if ip.network_id == network.id] def ip_dict(ip): - return {'netmask': network['netmask'], - 'enabled': '1', - 'ip': ip.address} + return { + "ip": ip.address, + "netmask": network["netmask"], + "enabled": "1"} + + def ip6_dict(ip6): + return { + "ip": ip6.addressV6, + "netmask": ip6.netmaskV6, + "gateway": ip6.gatewayV6, + "enabled": "1"} mac_id = instance.mac_address.replace(':', '') location = 'vm-data/networking/%s' % mac_id - mapping = {'label': network['label'], - 'gateway': network['gateway'], - 'mac': instance.mac_address, - 'dns': [network['dns']], - 'ips': [ip_dict(ip) for ip in network_IPs]} + mapping = { + 'label': network['label'], + 'gateway': network['gateway'], + 'mac': instance.mac_address, + 'dns': [network['dns']], + 'ips': [ip_dict(ip) for ip in network_IPs], + 'ip6s': [ip6_dict(ip) for ip in network_IPs]} + self.write_to_param_xenstore(vm_opaque_ref, {location: mapping}) + try: self.write_to_xenstore(vm_opaque_ref, location, mapping['location']) -- cgit From 953fe68ce9b27322003200c464c121464761d1e2 Mon Sep 17 00:00:00 2001 From: Cerberus <matt.dietz@rackspace.com> Date: Wed, 2 Mar 2011 15:46:50 -0600 Subject: merge fixes --- nova/compute/api.py | 4 +- nova/virt/xenapi/vm_utils.py | 4 +- nova/virt/xenapi/vmops.py | 48 ++++++++++------------ plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 2 +- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 52d30c7f7..fb7ef1aed 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -430,7 +430,7 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.Error(_("No finished migrations found for \ + raise exception.NotFound(_("No finished migrations found for \ instance")) params = {'migration_id': migration_ref['id']} @@ -444,7 +444,7 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.Error(_("No finished migrations found for \ + raise exception.NotFound(_("No finished migrations found for \ instance")) instance_ref = self.db.instance_get(context, instance_id) params = {'migration_id': migration_ref['id']} diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index eea9bb0b9..3ee963fa7 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -310,8 +310,7 @@ class VMHelper(HelperBase): def get_sr_path(cls, session, sr_label='slices'): """ Finds the SR and then coerces it into a path on the dom0 file system """ - # TODO(mdietz): replace this with the flag once unified-images merges - return '/var/run/sr-mount/%s' % cls.get_sr(session, sr_label) + return FLAGS.xenapi_sr_base_path + cls.get_sr(session, sr_label) @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_id): @@ -649,6 +648,7 @@ class VMHelper(HelperBase): @classmethod def scan_default_sr(cls, session): """Looks for the system default SR and triggers a re-scan""" + #FIXME(sirp/mdietz): refactor scan_default_sr in there sr_ref = cls.get_sr(session) session.call_xenapi('SR.scan', sr_ref) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 5157f18f1..b54084842 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -105,7 +105,7 @@ class VMOps(object): vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', disk) if disk_image_type == ImageType.DISK_RAW: - #Have a look at the VDI and see if it has a PV kernel + # Have a look at the VDI and see if it has a PV kernel pv_kernel = VMHelper.lookup_image(self._session, instance.id, vdi_ref) elif disk_image_type == ImageType.DISK_VHD: @@ -113,7 +113,6 @@ class VMOps(object): # configurable as Windows will use HVM. pv_kernel = True - #Have a look at the VDI and see if it has a PV kernel if instance.kernel_id: kernel = VMHelper.fetch_image(self._session, instance.id, instance.kernel_id, user, project, ImageType.KERNEL_RAMDISK) @@ -240,29 +239,20 @@ class VMOps(object): that will bundle the VHDs together and then push the bundle into Glance. """ - - with self._get_snapshot(instance) as snapshot: + template_vm_ref = None + try: + template_vm_ref, template_vdi_uuids = self._get_snapshot(instance) # call plugin to ship snapshot off to glance VMHelper.upload_image( - self._session, instance.id, snapshot.vdi_uuids, image_id) + self._session, instance.id, template_vdi_uuids, image_id) + finally: + if template_vm_ref: + self.virt._destroy(self.instance, template_vm_ref, shutdown=False, + destroy_kernel_ramdisk=False) logging.debug(_("Finished snapshot and upload for VM %s"), instance) def _get_snapshot(self, instance): - class Snapshot(object): - def __init__(self, virt, instance, vm_ref, vdis): - self.instance = instance - self.vdi_uuids = vdis - self.virt = virt - self.vm_ref = vm_ref - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - self.virt._destroy(self.instance, self.vm_ref, shutdown=False, - destroy_kernel_ramdisk=False) - #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added @@ -273,8 +263,7 @@ class VMOps(object): try: template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( self._session, instance.id, vm_ref, label) - return Snapshot(self, instance, template_vm_ref, - template_vdi_uuids) + return template_vm_ref, template_vdi_uuids except self.XenAPI.Failure, exc: logging.error(_("Unable to Snapshot %(vm_ref)s: %(exc)s") % locals()) @@ -294,9 +283,11 @@ class VMOps(object): # from the snapshot creation base_copy_uuid = cow_uuid = None - with self._get_snapshot(instance) as snapshot: + template_vdi_uuids = template_vm_ref = None + try: # transfer the base copy - base_copy_uuid = snapshot.vdi_uuids[1] + template_vm_ref, template_vdi_uuids = self._get_snapshot(instance) + base_copy_uuid = template_vdi_uuids[1] vdi_ref, vm_vdi_rec = \ VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) cow_uuid = vm_vdi_rec['uuid'] @@ -304,7 +295,7 @@ class VMOps(object): params = {'host': dest, 'vdi_uuid': base_copy_uuid, 'instance_id': instance.id, - 'sr_path': VMHelper.get_sr_path(self._session), } + 'sr_path': VMHelper.get_sr_path(self._session)} task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) @@ -322,6 +313,11 @@ class VMOps(object): {'params': pickle.dumps(params)}) self._session.wait_for_task(instance.id, task) + finally: + if template_vm_ref: + self.virt._destroy(self.instance, template_vm_ref, shutdown=False, + destroy_kernel_ramdisk=False) + # TODO(mdietz): we could also consider renaming these to something # sensible so we don't need to blindly pass around dictionaries return {'base_copy': base_copy_uuid, 'cow': cow_uuid} @@ -332,9 +328,9 @@ class VMOps(object): new_cow_uuid = str(uuid.uuid4()) params = {'instance_id': instance.id, 'old_base_copy_uuid': disk_info['base_copy'], - 'old_cow_uuid': disk_info['cow'], + 'old_cow_uuid': disk_info['cow'], 'new_base_copy_uuid': new_base_copy_uuid, - 'new_cow_uuid': new_cow_uuid, + 'new_cow_uuid': new_cow_uuid, 'sr_path': VMHelper.get_sr_path(self._session), } task = self._session.async_call_plugin('migration', diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index d08754c19..aa12d432a 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -207,7 +207,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): 'transfer-encoding': 'chunked', 'x-image-meta-is_public': 'True', 'x-image-meta-status': 'queued', - 'x-image-meta-type': 'vhd', } + 'x-image-meta-type': 'vhd'} for header, value in headers.iteritems(): conn.putheader(header, value) conn.endheaders() -- cgit From 67d9051551775df73aed118a3ca307c61d284225 Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Wed, 2 Mar 2011 16:20:54 -0600 Subject: Added IPv6 migrations --- .../versions/007_add_ipv6_to_fixed_ips.py | 90 ++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 3 + 2 files changed, 93 insertions(+) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py new file mode 100644 index 000000000..427934d53 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py @@ -0,0 +1,90 @@ +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# Table stub-definitions +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +# +fixed_ips = Table( + "fixed_ips", + meta, + Column( + "id", + Integer(), + primary_key=True, + nullable=False)) + +# +# New Tables +# +# None + +# +# Tables to alter +# +# None + +# +# Columns to add to existing tables +# + +fixed_ips_addressV6 = Column( + "addressV6", + String( + length=255, + convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)) + + +fixed_ips_netmaskV6 = Column( + "netmaskV6", + String( + length=3, + convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)) + + +fixed_ips_gatewayV6 = Column( + "gatewayV6", + String( + length=255, + convert_unicode=False, + assert_unicode=None, + unicode_error=None, + _warn_on_bytestring=False)) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + + # Add columns to existing tables + fixed_ips.create_column(fixed_ips_addressV6) + fixed_ips.create_column(fixed_ips_netmaskV6) + fixed_ips.create_column(fixed_ips_gatewayV6) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 1882efeba..821fd4a83 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -437,6 +437,9 @@ class FixedIp(BASE, NovaBase): allocated = Column(Boolean, default=False) leased = Column(Boolean, default=False) reserved = Column(Boolean, default=False) + addressV6 = Column(String(255)) + netmaskV6 = Column(String(3)) + gatewayV6 = Column(String(255)) class User(BASE, NovaBase): -- cgit From e34e9dd982870915f8c4dbf84a08bece42b0c592 Mon Sep 17 00:00:00 2001 From: Josh Kearney <josh@jk0.org> Date: Wed, 2 Mar 2011 17:09:50 -0600 Subject: Updated docstrings --- nova/virt/xenapi/vmops.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 1c58f529d..10d5c0160 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -495,7 +495,12 @@ class VMOps(object): self._wait_with_callback(instance.id, task, callback) def rescue(self, instance, callback): - """Rescue the specified instance""" + """Rescue the specified instance + - shutdown the instance VM + - set 'bootlock' to prevent the instance from starting in rescue + - spawn a rescue VM (the vm name-label will be instance-N-rescue) + + """ rescue_vm = VMHelper.lookup(self._session, instance.name + "-rescue") if rescue_vm: raise RuntimeError(_( @@ -521,7 +526,12 @@ class VMOps(object): self._session.call_xenapi("Async.VBD.plug", vbd_ref) def unrescue(self, instance, callback): - """Unrescue the specified instance""" + """Unrescue the specified instance + - unplug the instance VM's disk from the rescue VM + - teardown the rescue VM + - release the bootlock to allow the instance VM to start + + """ rescue_vm = VMHelper.lookup(self._session, instance.name + "-rescue") if not rescue_vm: -- cgit From 077a77a1ab6fbec468b36e2975c1e185235c17ff Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 15:49:51 -0800 Subject: removed create and delete method (and corresponding tests) from flavors.py --- nova/api/openstack/flavors.py | 20 -------------------- nova/tests/api/openstack/test_flavors.py | 14 ++------------ 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 375e12b18..4db812c2d 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -56,26 +56,6 @@ class Controller(wsgi.Controller): return dict(flavor=values) raise faults.Fault(exc.HTTPNotFound()) - def create(self, req): - """Create a flavor.""" - #TODO(jk0): Finish this later - #instance_types.create( - # name, - # memory, - # vcpus, - # local_gb, - # flavor_id, - # swap, - # rxtx_quota, - # rxtx_cap) - return "CREATE! %s" % req - - def delete(self, req, id): - """Delete a flavor.""" - #TODO(jk0): Finish this later - #instance_type.destroy(name) - return "DELETE! %s %s" % (req, id) - def _all_ids(self): """Return the list of all flavorids.""" # FIXME(kpepple) do we really need admin context here ? diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 2626f92ba..319767bb5 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -46,17 +46,7 @@ class FlavorsTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) - def test_create_flavor(self): - req = webob.Request.blank("/v1.0/flavors") - req.method = "POST" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - - def test_delete_flavor(self): - req = webob.Request.blank("/v1.0/flavors/1") - req.method = "DELETE" + def test_get_flavor_by_id(self): + req = webob.Request.blank('/v1.0/flavors/1') res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) - - def test_get_flavor_by_id(self): - pass -- cgit From 092e26667b4cf7389cbbb0d206fe7721515262eb Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 16:07:05 -0800 Subject: fixed coding style per devcamcar review notes --- bin/nova-manage | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 0b71da41e..15bbe2f45 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -669,40 +669,19 @@ class InstanceTypeCommands(object): deleted = ('', ', inactive')[val["deleted"] == 1] print ("%s: Memory: %sMB, VCPUS: %s, Storage: %sGB, FlavorID: %s, " "Swap: %sGB, RXTX Quota: %sGB, RXTX Cap: %sMB%s") % ( - n, - val["memory_mb"], - val["vcpus"], - val["local_gb"], - val["flavorid"], - val["swap"], - val["rxtx_quota"], - val["rxtx_cap"], - deleted) - - def create( - self, - name, - memory, - vcpus, - local_gb, - flavorid, - swap=0, - rxtx_quota=0, - rxtx_cap=0): + n, val["memory_mb"], val["vcpus"], val["local_gb"], + val["flavorid"], val["swap"], val["rxtx_quota"], + val["rxtx_cap"], deleted) + + def create(self, name, memory, vcpus, local_gb, flavorid, + swap=0, rxtx_quota=0, rxtx_cap=0): """Creates instance types / flavors arguments: name memory vcpus local_gb flavorid [swap] [rxtx_quota] [rxtx_cap] """ try: - instance_types.create( - name, - memory, - vcpus, - local_gb, - flavorid, - swap, - rxtx_quota, - rxtx_cap) + instance_types.create(name, memory, vcpus, local_gb, + flavorid, swap, rxtx_quota, rxtx_cap) except exception.InvalidInputException: print "Must supply valid parameters to create instance type" print e -- cgit From 4243e8e2b41f1438023b1184b1281474b27b5467 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 16:09:27 -0800 Subject: coding style change per devcamcar review --- nova/compute/instance_types.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 7d401fc86..20cf1faee 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -30,15 +30,8 @@ from nova import exception FLAGS = flags.FLAGS -def create( - name, - memory, - vcpus, - local_gb, - flavorid, - swap=0, - rxtx_quota=0, - rxtx_cap=0): +def create(name, memory, vcpus, local_gb, flavorid, swap=0, + rxtx_quota=0,rxtx_cap=0): """Creates instance types / flavors arguments: name memory vcpus local_gb flavorid swap rxtx_quota rxtx_cap """ -- cgit From 0bf74ef365688476b2b3a44e353c0062989d33b5 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 16:12:22 -0800 Subject: fixed _context typo --- nova/api/ec2/admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 51a06bb26..d9a4ef999 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -114,9 +114,9 @@ class AdminController(object): def __str__(self): return 'AdminController' - def describe_instance_types(self, _context, **_kwargs): + def describe_instance_types(self, context, **_kwargs): """Returns all active instance types data (vcpus, memory, etc.)""" - return {'instanceTypeSet': [db.instance_type_get_all(_context)]} + return {'instanceTypeSet': [db.instance_type_get_all(context)]} def describe_user(self, _context, name, **_kwargs): """Returns user data, including access and secret keys.""" -- cgit From 22ec4e190ccf9e30a7862e1ee7d90f2a0858c438 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 16:12:36 -0800 Subject: pep8 --- nova/compute/instance_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 20cf1faee..f360a7ac3 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -31,7 +31,7 @@ FLAGS = flags.FLAGS def create(name, memory, vcpus, local_gb, flavorid, swap=0, - rxtx_quota=0,rxtx_cap=0): + rxtx_quota=0, rxtx_cap=0): """Creates instance types / flavors arguments: name memory vcpus local_gb flavorid swap rxtx_quota rxtx_cap """ -- cgit From 86aed7edae3dd90741d0da704a99460701b8bcc7 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 16:32:09 -0800 Subject: added in req.environ for context --- nova/api/openstack/flavors.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 4db812c2d..f3d040ba3 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -41,25 +41,19 @@ class Controller(wsgi.Controller): def detail(self, req): """Return all flavors in detail.""" - items = [self.show(req, id)['flavor'] for id in self._all_ids()] + items = [self.show(req, id)['flavor'] for id in self._all_ids(req)] return dict(flavors=items) def show(self, req, id): """Return data about the given flavor id.""" - # FIXME(kpepple) do we really need admin context here ? - ctxt = context.get_admin_context() + ctxt = req.environ['nova.context'] values = db.instance_type_get_by_flavor_id(ctxt, id) - # FIXME(kpepple) refactor db call to return dict - # v = val.values()[0] - # item = dict(ram=v['memory_mb'], disk=v['local_gb'], - # id=v['flavorid'], name=val.keys()[0]) return dict(flavor=values) raise faults.Fault(exc.HTTPNotFound()) - def _all_ids(self): + def _all_ids(self, req): """Return the list of all flavorids.""" - # FIXME(kpepple) do we really need admin context here ? - ctxt = context.get_admin_context() + ctxt = req.environ['nova.context'] inst_types = db.instance_type_get_all(ctxt) flavor_ids = [inst_types[i]['flavorid'] for i in inst_types.keys()] - return flavor_ids + return sorted(flavor_ids) -- cgit From 7305bc9a47c03d9b471d747341ca3e95e89f56f4 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 16:35:53 -0800 Subject: pep8 --- bin/nova-manage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-manage b/bin/nova-manage index 15bbe2f45..9bf3a1bb3 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -673,7 +673,7 @@ class InstanceTypeCommands(object): val["flavorid"], val["swap"], val["rxtx_quota"], val["rxtx_cap"], deleted) - def create(self, name, memory, vcpus, local_gb, flavorid, + def create(self, name, memory, vcpus, local_gb, flavorid, swap=0, rxtx_quota=0, rxtx_cap=0): """Creates instance types / flavors arguments: name memory vcpus local_gb flavorid [swap] [rxtx_quota] -- cgit From 28896fcfb474662fe339fa5b05aec33b3896b4fa Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 16:37:02 -0800 Subject: changed _context --- nova/db/sqlalchemy/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f4cd16d9a..919dda118 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2077,7 +2077,7 @@ def console_get(context, console_id, instance_id=None): @require_admin_context -def instance_type_create(context, values): +def instance_type_create(_context, values): try: instance_type_ref = models.InstanceTypes() instance_type_ref.update(values) -- cgit From 1abd891f65ea8291dc0c3f2075de80dc92c0d431 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 16:46:32 -0800 Subject: corrected error message --- nova/compute/instance_types.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index f360a7ac3..d31d3c304 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -58,8 +58,7 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0, rxtx_quota=rxtx_quota, rxtx_cap=rxtx_cap)) except exception.DBError: - raise exception.ApiError(_("Cannot create instance type: %s"), - name) + raise exception.ApiError(_("Cannot create instance type: %s" % name)) def destroy(name): @@ -71,8 +70,7 @@ def destroy(name): try: db.instance_type_destroy(context.get_admin_context(), name) except exception.NotFound: - raise exception.ApiError(_("Unknown instance type: %s"), - name) + raise exception.ApiError(_("Unknown instance type: %s" % name)) def purge(name): @@ -84,8 +82,7 @@ def purge(name): try: db.instance_type_purge(context.get_admin_context(), name) except exception.NotFound: - raise exception.ApiError(_("Unknown instance type: %s"), - name) + raise exception.ApiError(_("Unknown instance type: %s" % name)) def get_all_types(inactive=0): @@ -109,8 +106,7 @@ def get_instance_type(name): inst_type = db.instance_type_get_by_name(ctxt, name) return inst_type except exception.DBError: - raise exception.ApiError(_("Unknown instance type: %s"), - name) + raise exception.ApiError(_("Unknown instance type: %s" % name)) def get_by_type(instance_type): @@ -123,8 +119,8 @@ def get_by_type(instance_type): inst_type = db.instance_type_get_by_name(ctxt, instance_type) return inst_type['name'] except exception.DBError: - raise exception.ApiError(_("Unknown instance type: %s"), - instance_type) + raise exception.ApiError(_("Unknown instance type: %s" %\ + instance_type)) def get_by_flavor_id(flavor_id): @@ -136,5 +132,4 @@ def get_by_flavor_id(flavor_id): flavor = db.instance_type_get_by_flavor_id(ctxt, flavor_id) return flavor['name'] except exception.DBError: - raise exception.ApiError(_("Unknown flavor: %s"), - flavor_id) + raise exception.ApiError(_("Unknown flavor: %s" % flavor_id)) -- cgit From b39a3f099a4410c95658334fc1907a8eb6b5a5dc Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 16:57:56 -0800 Subject: adding new source docs --- doc/.autogenerated | 141 +++++++++++++++++++++ ...ate_repo.versions.005_add_instance_metadata.rst | 6 + ...o.versions.006_add_provider_data_to_volumes.rst | 6 + ...igrate_repo.versions.007_add_instance_types.rst | 6 + doc/source/api/nova..tests.test_instance_types.rst | 6 + doc/source/api/nova..tests.test_test.rst | 6 + doc/source/api/nova..tests.test_utils.rst | 6 + 7 files changed, 177 insertions(+) create mode 100644 doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst create mode 100644 doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst create mode 100644 doc/source/api/nova..tests.test_instance_types.rst create mode 100644 doc/source/api/nova..tests.test_test.rst create mode 100644 doc/source/api/nova..tests.test_utils.rst diff --git a/doc/.autogenerated b/doc/.autogenerated index b11735994..456c8ad1e 100644 --- a/doc/.autogenerated +++ b/doc/.autogenerated @@ -140,3 +140,144 @@ source/api/nova..volume.manager.rst source/api/nova..volume.san.rst source/api/nova..wsgi.rst source/api/autoindex.rst +source/api/nova..adminclient.rst +source/api/nova..api.direct.rst +source/api/nova..api.ec2.admin.rst +source/api/nova..api.ec2.apirequest.rst +source/api/nova..api.ec2.cloud.rst +source/api/nova..api.ec2.metadatarequesthandler.rst +source/api/nova..api.openstack.auth.rst +source/api/nova..api.openstack.backup_schedules.rst +source/api/nova..api.openstack.common.rst +source/api/nova..api.openstack.consoles.rst +source/api/nova..api.openstack.faults.rst +source/api/nova..api.openstack.flavors.rst +source/api/nova..api.openstack.images.rst +source/api/nova..api.openstack.servers.rst +source/api/nova..api.openstack.shared_ip_groups.rst +source/api/nova..api.openstack.zones.rst +source/api/nova..auth.dbdriver.rst +source/api/nova..auth.fakeldap.rst +source/api/nova..auth.ldapdriver.rst +source/api/nova..auth.manager.rst +source/api/nova..auth.signer.rst +source/api/nova..cloudpipe.pipelib.rst +source/api/nova..compute.api.rst +source/api/nova..compute.instance_types.rst +source/api/nova..compute.manager.rst +source/api/nova..compute.monitor.rst +source/api/nova..compute.power_state.rst +source/api/nova..console.api.rst +source/api/nova..console.fake.rst +source/api/nova..console.manager.rst +source/api/nova..console.xvp.rst +source/api/nova..context.rst +source/api/nova..crypto.rst +source/api/nova..db.api.rst +source/api/nova..db.base.rst +source/api/nova..db.migration.rst +source/api/nova..db.sqlalchemy.api.rst +source/api/nova..db.sqlalchemy.migrate_repo.manage.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.001_austin.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.002_bexar.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.003_add_label_to_networks.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.004_add_zone_tables.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst +source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst +source/api/nova..db.sqlalchemy.migration.rst +source/api/nova..db.sqlalchemy.models.rst +source/api/nova..db.sqlalchemy.session.rst +source/api/nova..exception.rst +source/api/nova..fakememcache.rst +source/api/nova..fakerabbit.rst +source/api/nova..flags.rst +source/api/nova..image.glance.rst +source/api/nova..image.local.rst +source/api/nova..image.s3.rst +source/api/nova..image.service.rst +source/api/nova..log.rst +source/api/nova..manager.rst +source/api/nova..network.api.rst +source/api/nova..network.linux_net.rst +source/api/nova..network.manager.rst +source/api/nova..objectstore.bucket.rst +source/api/nova..objectstore.handler.rst +source/api/nova..objectstore.image.rst +source/api/nova..objectstore.stored.rst +source/api/nova..quota.rst +source/api/nova..rpc.rst +source/api/nova..scheduler.chance.rst +source/api/nova..scheduler.driver.rst +source/api/nova..scheduler.manager.rst +source/api/nova..scheduler.simple.rst +source/api/nova..scheduler.zone.rst +source/api/nova..service.rst +source/api/nova..test.rst +source/api/nova..tests.api.openstack.fakes.rst +source/api/nova..tests.api.openstack.test_adminapi.rst +source/api/nova..tests.api.openstack.test_api.rst +source/api/nova..tests.api.openstack.test_auth.rst +source/api/nova..tests.api.openstack.test_common.rst +source/api/nova..tests.api.openstack.test_faults.rst +source/api/nova..tests.api.openstack.test_flavors.rst +source/api/nova..tests.api.openstack.test_images.rst +source/api/nova..tests.api.openstack.test_ratelimiting.rst +source/api/nova..tests.api.openstack.test_servers.rst +source/api/nova..tests.api.openstack.test_shared_ip_groups.rst +source/api/nova..tests.api.openstack.test_zones.rst +source/api/nova..tests.api.test_wsgi.rst +source/api/nova..tests.db.fakes.rst +source/api/nova..tests.declare_flags.rst +source/api/nova..tests.fake_flags.rst +source/api/nova..tests.glance.stubs.rst +source/api/nova..tests.hyperv_unittest.rst +source/api/nova..tests.objectstore_unittest.rst +source/api/nova..tests.real_flags.rst +source/api/nova..tests.runtime_flags.rst +source/api/nova..tests.test_access.rst +source/api/nova..tests.test_api.rst +source/api/nova..tests.test_auth.rst +source/api/nova..tests.test_cloud.rst +source/api/nova..tests.test_compute.rst +source/api/nova..tests.test_console.rst +source/api/nova..tests.test_direct.rst +source/api/nova..tests.test_flags.rst +source/api/nova..tests.test_instance_types.rst +source/api/nova..tests.test_localization.rst +source/api/nova..tests.test_log.rst +source/api/nova..tests.test_middleware.rst +source/api/nova..tests.test_misc.rst +source/api/nova..tests.test_network.rst +source/api/nova..tests.test_quota.rst +source/api/nova..tests.test_rpc.rst +source/api/nova..tests.test_scheduler.rst +source/api/nova..tests.test_service.rst +source/api/nova..tests.test_test.rst +source/api/nova..tests.test_twistd.rst +source/api/nova..tests.test_utils.rst +source/api/nova..tests.test_virt.rst +source/api/nova..tests.test_volume.rst +source/api/nova..tests.test_xenapi.rst +source/api/nova..tests.xenapi.stubs.rst +source/api/nova..twistd.rst +source/api/nova..utils.rst +source/api/nova..version.rst +source/api/nova..virt.connection.rst +source/api/nova..virt.disk.rst +source/api/nova..virt.fake.rst +source/api/nova..virt.hyperv.rst +source/api/nova..virt.images.rst +source/api/nova..virt.libvirt_conn.rst +source/api/nova..virt.xenapi.fake.rst +source/api/nova..virt.xenapi.network_utils.rst +source/api/nova..virt.xenapi.vm_utils.rst +source/api/nova..virt.xenapi.vmops.rst +source/api/nova..virt.xenapi.volume_utils.rst +source/api/nova..virt.xenapi.volumeops.rst +source/api/nova..virt.xenapi_conn.rst +source/api/nova..volume.api.rst +source/api/nova..volume.driver.rst +source/api/nova..volume.manager.rst +source/api/nova..volume.san.rst +source/api/nova..wsgi.rst diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst new file mode 100644 index 000000000..cef0c243e --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.005_add_instance_metadata + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst new file mode 100644 index 000000000..a15697196 --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.006_add_provider_data_to_volumes + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst new file mode 100644 index 000000000..38842d1af --- /dev/null +++ b/doc/source/api/nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types.rst @@ -0,0 +1,6 @@ +The :mod:`nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types` Module +============================================================================== +.. automodule:: nova..db.sqlalchemy.migrate_repo.versions.007_add_instance_types + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_instance_types.rst b/doc/source/api/nova..tests.test_instance_types.rst new file mode 100644 index 000000000..ebe689966 --- /dev/null +++ b/doc/source/api/nova..tests.test_instance_types.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_instance_types` Module +============================================================================== +.. automodule:: nova..tests.test_instance_types + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_test.rst b/doc/source/api/nova..tests.test_test.rst new file mode 100644 index 000000000..389eb3c99 --- /dev/null +++ b/doc/source/api/nova..tests.test_test.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_test` Module +============================================================================== +.. automodule:: nova..tests.test_test + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/api/nova..tests.test_utils.rst b/doc/source/api/nova..tests.test_utils.rst new file mode 100644 index 000000000..d61a7021f --- /dev/null +++ b/doc/source/api/nova..tests.test_utils.rst @@ -0,0 +1,6 @@ +The :mod:`nova..tests.test_utils` Module +============================================================================== +.. automodule:: nova..tests.test_utils + :members: + :undoc-members: + :show-inheritance: -- cgit From 45662001c477bdce7cd50b4f7f67e06479c3cbd3 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 16:59:38 -0800 Subject: requested style change --- nova/compute/instance_types.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index d31d3c304..09b42f319 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -48,8 +48,7 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0, try: db.instance_type_create( context.get_admin_context(), - dict( - name=name, + dict(name=name, memory_mb=memory, vcpus=vcpus, local_gb=local_gb, -- cgit From 507a13d8dcdc11ea7638c8904d6d0de22d8e109b Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 17:14:51 -0800 Subject: added logging to instance_types for DB errors per code review --- nova/compute/instance_types.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 09b42f319..79c879e8e 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -24,10 +24,12 @@ The built-in instance properties. from nova import context from nova import db -from nova import flags from nova import exception +from nova import flags +from nova import log as logging FLAGS = flags.FLAGS +LOG = logging.getLogger('nova.instance_types') def create(name, memory, vcpus, local_gb, flavorid, swap=0, @@ -56,7 +58,8 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0, swap=swap, rxtx_quota=rxtx_quota, rxtx_cap=rxtx_cap)) - except exception.DBError: + except exception.DBError, e: + LOG.exception(_('DB error: %s' % e)) raise exception.ApiError(_("Cannot create instance type: %s" % name)) @@ -69,6 +72,7 @@ def destroy(name): try: db.instance_type_destroy(context.get_admin_context(), name) except exception.NotFound: + LOG.exception(_('Instance type %s not found for deletion' % name)) raise exception.ApiError(_("Unknown instance type: %s" % name)) @@ -81,6 +85,7 @@ def purge(name): try: db.instance_type_purge(context.get_admin_context(), name) except exception.NotFound: + LOG.exception(_('Instance type %s not found for purge' % name)) raise exception.ApiError(_("Unknown instance type: %s" % name)) @@ -117,7 +122,8 @@ def get_by_type(instance_type): ctxt = context.get_admin_context() inst_type = db.instance_type_get_by_name(ctxt, instance_type) return inst_type['name'] - except exception.DBError: + except exception.DBError, e: + LOG.exception(_('DB error: %s' % e)) raise exception.ApiError(_("Unknown instance type: %s" %\ instance_type)) @@ -130,5 +136,6 @@ def get_by_flavor_id(flavor_id): ctxt = context.get_admin_context() flavor = db.instance_type_get_by_flavor_id(ctxt, flavor_id) return flavor['name'] - except exception.DBError: + except exception.DBError, e: + LOG.exception(_('DB error: %s' % e)) raise exception.ApiError(_("Unknown flavor: %s" % flavor_id)) -- cgit From 74f2a7537e9e4b8259a4179adc21eef59e59d3c5 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 17:38:42 -0800 Subject: catching bare except: --- nova/compute/instance_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 79c879e8e..fa02a5dfa 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -40,7 +40,7 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0, for option in [memory, vcpus, local_gb, flavorid]: try: int(option) - except: + except ValueError: raise exception.InvalidInputException( _("create arguments must be positive integers")) if (int(memory) <= 0) or (int(vcpus) <= 0) or (int(local_gb) < 0): -- cgit From dc8e308819fb383b317ff866288965a27016557e Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 2 Mar 2011 17:54:04 -0800 Subject: moved migration to 008 (sigh) --- .../versions/007_add_instance_types.py | 87 ---------------------- .../versions/008_add_instance_types.py | 87 ++++++++++++++++++++++ 2 files changed, 87 insertions(+), 87 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py deleted file mode 100644 index 66609054e..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_types.py +++ /dev/null @@ -1,87 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Ken Pepple -# 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 * -from migrate import * - -from nova import api -from nova import db -from nova import log as logging - -import datetime - -meta = MetaData() - - -# -# New Tables -# -instance_types = Table('instance_types', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('name', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False), - unique=True), - Column('id', Integer(), primary_key=True, nullable=False), - Column('memory_mb', Integer(), nullable=False), - Column('vcpus', Integer(), nullable=False), - Column('local_gb', Integer(), nullable=False), - Column('flavorid', Integer(), nullable=False, unique=True), - Column('swap', Integer(), nullable=False, default=0), - Column('rxtx_quota', Integer(), nullable=False, default=0), - Column('rxtx_cap', Integer(), nullable=False, default=0)) - - -def upgrade(migrate_engine): - # Upgrade operations go here - # Don't create your own engine; bind migrate_engine - # to your metadata - meta.bind = migrate_engine - try: - instance_types.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating instance_types table') - raise - - # Here are the old static instance types - INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), - 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), - 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), - 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), - 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} - try: - i = instance_types.insert() - for name, values in INSTANCE_TYPES.iteritems(): - # FIXME(kpepple) should we be seeding created_at / updated_at ? - # now = datetime.datatime.utcnow() - i.execute({'name': name, 'memory_mb': values["memory_mb"], - 'vcpus': values["vcpus"], 'deleted': 0, - 'local_gb': values["local_gb"], - 'flavorid': values["flavorid"]}) - except Exception: - logging.info(repr(table)) - logging.exception('Exception while seeding instance_types table') - raise - - -def downgrade(migrate_engine): - # Operations to reverse the above upgrade go here. - for table in (instance_types): - table.drop() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py new file mode 100644 index 000000000..66609054e --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py @@ -0,0 +1,87 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Ken Pepple +# 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 * +from migrate import * + +from nova import api +from nova import db +from nova import log as logging + +import datetime + +meta = MetaData() + + +# +# New Tables +# +instance_types = Table('instance_types', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + unique=True), + Column('id', Integer(), primary_key=True, nullable=False), + Column('memory_mb', Integer(), nullable=False), + Column('vcpus', Integer(), nullable=False), + Column('local_gb', Integer(), nullable=False), + Column('flavorid', Integer(), nullable=False, unique=True), + Column('swap', Integer(), nullable=False, default=0), + Column('rxtx_quota', Integer(), nullable=False, default=0), + Column('rxtx_cap', Integer(), nullable=False, default=0)) + + +def upgrade(migrate_engine): + # Upgrade operations go here + # Don't create your own engine; bind migrate_engine + # to your metadata + meta.bind = migrate_engine + try: + instance_types.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating instance_types table') + raise + + # Here are the old static instance types + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2), + 'm1.medium': dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4), + 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + try: + i = instance_types.insert() + for name, values in INSTANCE_TYPES.iteritems(): + # FIXME(kpepple) should we be seeding created_at / updated_at ? + # now = datetime.datatime.utcnow() + i.execute({'name': name, 'memory_mb': values["memory_mb"], + 'vcpus': values["vcpus"], 'deleted': 0, + 'local_gb': values["local_gb"], + 'flavorid': values["flavorid"]}) + except Exception: + logging.info(repr(table)) + logging.exception('Exception while seeding instance_types table') + raise + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + for table in (instance_types): + table.drop() -- cgit From f9cce74cad854d377de113a619dc42df10b9a2ba Mon Sep 17 00:00:00 2001 From: Masanori Itoh <itoumsn@nttdata.co.jp> Date: Thu, 3 Mar 2011 14:10:42 +0900 Subject: Updated Authors and .mailmap --- .mailmap | 1 + Authors | 1 + 2 files changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index df012e066..ed4404ad5 100644 --- a/.mailmap +++ b/.mailmap @@ -15,6 +15,7 @@ <corywright@gmail.com> <cory.wright@rackspace.com> <devin.carlen@gmail.com> <devcamcar@illian.local> <ewan.mellor@citrix.com> <emellor@silver> +<itoumsn@nttdata.co.jp> <itoumsn@shayol> <jaypipes@gmail.com> <jpipes@serialcoder> <jmckenty@gmail.com> <jmckenty@joshua-mckentys-macbook-pro.local> <jmckenty@gmail.com> <jmckenty@yyj-dhcp171.corp.flock.com> diff --git a/Authors b/Authors index b279d8a53..7993955e2 100644 --- a/Authors +++ b/Authors @@ -39,6 +39,7 @@ Ken Pepple <ken.pepple@gmail.com> Kevin L. Mitchell <kevin.mitchell@rackspace.com> Koji Iida <iida.koji@lab.ntt.co.jp> Lorin Hochstein <lorin@isi.edu> +Masanori Itoh <itoumsn@nttdata.co.jp> Matt Dietz <matt.dietz@rackspace.com> Michael Gundlach <michael.gundlach@rackspace.com> Monsyne Dragon <mdragon@rackspace.com> -- cgit From f7645bc2fb46ab7649faf12c794834d94839e4d2 Mon Sep 17 00:00:00 2001 From: Thierry Carrez <thierry@openstack.org> Date: Thu, 3 Mar 2011 13:55:01 +0100 Subject: Fix regression in the way libvirt_conn gets its instance_types --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 8a83e657f..9f7315c17 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -607,7 +607,7 @@ class LibvirtConnection(object): user=user, project=project, size=size) - type_data = instance_types.get_instance_type([inst['instance_type']]) + type_data = instance_types.get_instance_type(inst['instance_type']) if type_data['local_gb']: self._cache_image(fn=self._create_local, -- cgit From 26c217d1f16b100b9dc615388ee315e6daf336ce Mon Sep 17 00:00:00 2001 From: Masanori Itoh <itoumsn@nttdata.co.jp> Date: Fri, 4 Mar 2011 00:30:19 +0900 Subject: Updated DescribeKeyPairs response tag checked in nova/tests/test_cloud.py --- nova/tests/test_cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 061910013..b195fa520 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -267,7 +267,7 @@ class CloudTestCase(test.TestCase): self._create_key('test1') self._create_key('test2') result = self.cloud.describe_key_pairs(self.context) - keys = result["keypairsSet"] + keys = result["keySet"] self.assertTrue(filter(lambda k: k['keyName'] == 'test1', keys)) self.assertTrue(filter(lambda k: k['keyName'] == 'test2', keys)) -- cgit From a62e603e8b1cedd89ca0c71f1cdc928d19c68a4d Mon Sep 17 00:00:00 2001 From: Brian Waldon <brian.waldon@rackspace.com> Date: Thu, 3 Mar 2011 11:04:33 -0500 Subject: adding wsgi.Request class to add custom best_match; adding new class to wsgify decorators; replacing all references to webob.Request in non-test code to wsgi.Request --- nova/api/direct.py | 4 ++-- nova/api/ec2/__init__.py | 14 +++++------ nova/api/ec2/metadatarequesthandler.py | 2 +- nova/api/openstack/__init__.py | 4 ++-- nova/api/openstack/auth.py | 4 ++-- nova/api/openstack/common.py | 2 +- nova/api/openstack/faults.py | 2 +- nova/api/openstack/ratelimiting/__init__.py | 4 ++-- nova/wsgi.py | 37 ++++++++++++++++++++++------- 9 files changed, 47 insertions(+), 26 deletions(-) diff --git a/nova/api/direct.py b/nova/api/direct.py index 208b6d086..cd237f649 100644 --- a/nova/api/direct.py +++ b/nova/api/direct.py @@ -187,7 +187,7 @@ class ServiceWrapper(wsgi.Controller): def __init__(self, service_handle): self.service_handle = service_handle - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): arg_dict = req.environ['wsgiorg.routing_args'][1] action = arg_dict['action'] @@ -218,7 +218,7 @@ class Proxy(object): self.prefix = prefix def __do_request(self, path, context, **kwargs): - req = webob.Request.blank(path) + req = wsgi.Request.blank(path) req.method = 'POST' req.body = urllib.urlencode({'json': utils.dumps(kwargs)}) req.environ['openstack.context'] = context diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 5adc2c075..58b1ecf03 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -53,7 +53,7 @@ flags.DEFINE_list('lockout_memcached_servers', None, class RequestLogging(wsgi.Middleware): """Access-Log akin logging for all EC2 API requests.""" - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): start = utils.utcnow() rv = req.get_response(self.application) @@ -112,7 +112,7 @@ class Lockout(wsgi.Middleware): debug=0) super(Lockout, self).__init__(application) - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): access_key = str(req.params['AWSAccessKeyId']) failures_key = "authfailures-%s" % access_key @@ -141,7 +141,7 @@ class Authenticate(wsgi.Middleware): """Authenticate an EC2 request and add 'ec2.context' to WSGI environ.""" - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): # Read request signature and access id. try: @@ -190,7 +190,7 @@ class Requestify(wsgi.Middleware): super(Requestify, self).__init__(app) self.controller = utils.import_class(controller)() - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod', 'SignatureVersion', 'Version', 'Timestamp'] @@ -269,7 +269,7 @@ class Authorizer(wsgi.Middleware): }, } - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): context = req.environ['ec2.context'] controller = req.environ['ec2.request'].controller.__class__.__name__ @@ -303,7 +303,7 @@ class Executor(wsgi.Application): response, or a 400 upon failure. """ - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): context = req.environ['ec2.context'] api_request = req.environ['ec2.request'] @@ -365,7 +365,7 @@ class Executor(wsgi.Application): class Versions(wsgi.Application): - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): """Respond to a request for all EC2 versions.""" # available api versions diff --git a/nova/api/ec2/metadatarequesthandler.py b/nova/api/ec2/metadatarequesthandler.py index 6fb441656..28f99b0ef 100644 --- a/nova/api/ec2/metadatarequesthandler.py +++ b/nova/api/ec2/metadatarequesthandler.py @@ -65,7 +65,7 @@ class MetadataRequestHandler(wsgi.Application): data = data[item] return data - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): cc = cloud.CloudController() remote_address = req.remote_addr diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 274330e3b..b5439788d 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -47,7 +47,7 @@ flags.DEFINE_bool('allow_admin_api', class FaultWrapper(wsgi.Middleware): """Calls down the middleware stack, making exceptions into faults.""" - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): try: return req.get_response(self.application) @@ -115,7 +115,7 @@ class APIRouter(wsgi.Router): class Versions(wsgi.Application): - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): """Respond to a request for all OpenStack API versions.""" response = { diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 6011e6115..de8905f46 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -46,7 +46,7 @@ class AuthMiddleware(wsgi.Middleware): self.auth = auth.manager.AuthManager() super(AuthMiddleware, self).__init__(application) - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): if not self.has_authentication(req): return self.authenticate(req) @@ -121,7 +121,7 @@ class AuthMiddleware(wsgi.Middleware): username - string key - string API key - req - webob.Request object + req - wsgi.Request object """ ctxt = context.get_admin_context() user = self.auth.get_user_from_access_key(key) diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 9f85c5c8a..49b970d75 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -25,7 +25,7 @@ def limited(items, request, max_limit=1000): Return a slice of items according to requested offset and limit. @param items: A sliceable entity - @param request: `webob.Request` possibly containing 'offset' and 'limit' + @param request: `wsgi.Request` possibly containing 'offset' and 'limit' GET variables. 'offset' is where to start in the list, and 'limit' is the maximum number of items to return. If 'limit' is not specified, 0, or > max_limit, we default diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py index 224a7ef0b..c70b00fa3 100644 --- a/nova/api/openstack/faults.py +++ b/nova/api/openstack/faults.py @@ -42,7 +42,7 @@ class Fault(webob.exc.HTTPException): """Create a Fault for the given webob.exc.exception.""" self.wrapped_exc = exception - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): """Generate a WSGI response based on the exception passed to ctor.""" # Replace the body with fault details. diff --git a/nova/api/openstack/ratelimiting/__init__.py b/nova/api/openstack/ratelimiting/__init__.py index cbb4b897e..88ffc3246 100644 --- a/nova/api/openstack/ratelimiting/__init__.py +++ b/nova/api/openstack/ratelimiting/__init__.py @@ -57,7 +57,7 @@ class RateLimitingMiddleware(wsgi.Middleware): self.limiter = WSGIAppProxy(service_host) super(RateLimitingMiddleware, self).__init__(application) - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): """Rate limit the request. @@ -183,7 +183,7 @@ class WSGIApp(object): """Create the WSGI application using the given Limiter instance.""" self.limiter = limiter - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): parts = req.path_info.split('/') # format: /limiter/<username>/<urlencoded action> diff --git a/nova/wsgi.py b/nova/wsgi.py index 1eb66d067..67216d540 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -82,6 +82,27 @@ class Server(object): log=WritableLogger(logger)) +class Request(webob.Request): + + def best_match(self): + """ + Determine the most acceptable content-type based on the + query extension then the Accept header + """ + + parts = self.path.rsplit(".", 1) + + if len(parts) > 1: + format = parts[1] + if format in ["json", "xml"]: + return "application/{0}".format(parts[1]) + + ctypes = ["application/json", "application/xml"] + bm = self.accept.best_match(ctypes) + + return bm or "application/json" + + class Application(object): """Base WSGI application wrapper. Subclasses need to implement __call__.""" @@ -113,7 +134,7 @@ class Application(object): def __call__(self, environ, start_response): r"""Subclasses will probably want to implement __call__ like this: - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): # Any of the following objects work as responses: @@ -199,7 +220,7 @@ class Middleware(Application): """Do whatever you'd like to the response.""" return response - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): response = self.process_request(req) if response: @@ -212,7 +233,7 @@ class Debug(Middleware): """Helper class that can be inserted into any WSGI application chain to get information about the request and response.""" - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): print ("*" * 40) + " REQUEST ENVIRON" for key, value in req.environ.items(): @@ -276,7 +297,7 @@ class Router(object): self._router = routes.middleware.RoutesMiddleware(self._dispatch, self.map) - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): """ Route the incoming request to a controller based on self.map. @@ -285,7 +306,7 @@ class Router(object): return self._router @staticmethod - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def _dispatch(req): """ Called by self._router after matching the incoming request to a route @@ -304,11 +325,11 @@ class Controller(object): WSGI app that reads routing information supplied by RoutesMiddleware and calls the requested action method upon itself. All action methods must, in addition to their normal parameters, accept a 'req' argument - which is the incoming webob.Request. They raise a webob.exc exception, + which is the incoming wsgi.Request. They raise a webob.exc exception, or return a dict which will be serialized by requested content type. """ - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): """ Call the method specified in req.environ by RoutesMiddleware. @@ -358,7 +379,7 @@ class Serializer(object): needed to serialize a dictionary to that type. """ self.metadata = metadata or {} - req = webob.Request.blank('', environ) + req = wsgi.Request.blank('', environ) suffix = req.path_info.split('.')[-1].lower() if suffix == 'json': self.handler = self._to_json -- cgit From 6d075754bdd4090342bf4f79c726a52923c311a8 Mon Sep 17 00:00:00 2001 From: Brian Waldon <brian.waldon@rackspace.com> Date: Thu, 3 Mar 2011 12:45:34 -0500 Subject: adding wsgi.Controller and wsgi.Request testing; fixing format keyword argument exception --- nova/tests/api/test_wsgi.py | 120 +++++++++++++++++++++++++++++++++++++------- nova/wsgi.py | 4 +- 2 files changed, 105 insertions(+), 19 deletions(-) diff --git a/nova/tests/api/test_wsgi.py b/nova/tests/api/test_wsgi.py index 2c7852214..cf2d0e297 100644 --- a/nova/tests/api/test_wsgi.py +++ b/nova/tests/api/test_wsgi.py @@ -21,11 +21,13 @@ Test WSGI basics and provide some helper functions for other WSGI tests. """ +import json from nova import test import routes import webob +from nova import exception from nova import wsgi @@ -66,30 +68,112 @@ class Test(test.TestCase): result = webob.Request.blank('/bad').get_response(Router()) self.assertNotEqual(result.body, "Router result") - def test_controller(self): - class Controller(wsgi.Controller): - """Test controller to call from router.""" - test = self +class ControllerTest(test.TestCase): + + class TestRouter(wsgi.Router): + + class TestController(wsgi.Controller): + + _serialization_metadata = { + 'application/xml': { + "attributes": { + "test": ["id"]}}} def show(self, req, id): # pylint: disable-msg=W0622,C0103 - """Default action called for requests with an ID.""" - self.test.assertEqual(req.path_info, '/tests/123') - self.test.assertEqual(id, '123') - return id + return {"test": {"id": id}} + + def __init__(self): + mapper = routes.Mapper() + mapper.resource("test", "tests", controller=self.TestController()) + wsgi.Router.__init__(self, mapper) + + def test_show(self): + request = wsgi.Request.blank('/tests/123') + result = request.get_response(self.TestRouter()) + self.assertEqual(json.loads(result.body), {"test": {"id": "123"}}) + + def test_content_type_from_accept_xml(self): + request = webob.Request.blank('/tests/123') + request.headers["Accept"] = "application/xml" + result = request.get_response(self.TestRouter()) + self.assertEqual(result.headers["Content-Type"], "application/xml") + + def test_content_type_from_accept_json(self): + request = wsgi.Request.blank('/tests/123') + request.headers["Accept"] = "application/json" + result = request.get_response(self.TestRouter()) + self.assertEqual(result.headers["Content-Type"], "application/json") + + def test_content_type_from_query_extension_xml(self): + request = wsgi.Request.blank('/tests/123.xml') + result = request.get_response(self.TestRouter()) + self.assertEqual(result.headers["Content-Type"], "application/xml") + + def test_content_type_from_query_extension_json(self): + request = wsgi.Request.blank('/tests/123.json') + result = request.get_response(self.TestRouter()) + self.assertEqual(result.headers["Content-Type"], "application/json") + + def test_content_type_default_when_unsupported(self): + request = wsgi.Request.blank('/tests/123.unsupported') + request.headers["Accept"] = "application/unsupported1" + result = request.get_response(self.TestRouter()) + self.assertEqual(result.status_int, 200) + self.assertEqual(result.headers["Content-Type"], "application/json") + + +class RequestTest(test.TestCase): + + def test_content_type_from_accept_xml(self): + request = wsgi.Request.blank('/tests/123') + request.headers["Accept"] = "application/xml" + result = request.best_match() + self.assertEqual(result, "application/xml") + + request = wsgi.Request.blank('/tests/123') + request.headers["Accept"] = "application/json" + result = request.best_match() + self.assertEqual(result, "application/json") + + request = wsgi.Request.blank('/tests/123') + request.headers["Accept"] = "application/xml, application/json" + result = request.best_match() + self.assertEqual(result, "application/json") + + request = wsgi.Request.blank('/tests/123') + request.headers["Accept"] = \ + "application/json; q=0.3, application/xml; q=0.9" + result = request.best_match() + self.assertEqual(result, "application/xml") + + def test_content_type_from_query_extension(self): + request = wsgi.Request.blank('/tests/123.xml') + result = request.best_match() + self.assertEqual(result, "application/xml") + + request = wsgi.Request.blank('/tests/123.json') + result = request.best_match() + self.assertEqual(result, "application/json") + + request = wsgi.Request.blank('/tests/123.invalid') + result = request.best_match() + self.assertEqual(result, "application/json") + + def test_content_type_accept_and_query_extension(self): + request = wsgi.Request.blank('/tests/123.xml') + request.headers["Accept"] = "application/json" + result = request.best_match() + self.assertEqual(result, "application/xml") + + def test_content_type_accept_default(self): + request = wsgi.Request.blank('/tests/123.unsupported') + request.headers["Accept"] = "application/unsupported1" + result = request.best_match() + self.assertEqual(result, "application/json") - class Router(wsgi.Router): - """Test router.""" - def __init__(self): - mapper = routes.Mapper() - mapper.resource("test", "tests", controller=Controller()) - super(Router, self).__init__(mapper) - result = webob.Request.blank('/tests/123').get_response(Router()) - self.assertEqual(result.body, "123") - result = webob.Request.blank('/test/123').get_response(Router()) - self.assertNotEqual(result.body, "123") class SerializerTest(test.TestCase): diff --git a/nova/wsgi.py b/nova/wsgi.py index 67216d540..4577439cb 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -339,6 +339,8 @@ class Controller(object): method = getattr(self, action) del arg_dict['controller'] del arg_dict['action'] + if 'format' in arg_dict: + del arg_dict['format'] arg_dict['req'] = req result = method(**arg_dict) if type(result) is dict: @@ -379,7 +381,7 @@ class Serializer(object): needed to serialize a dictionary to that type. """ self.metadata = metadata or {} - req = wsgi.Request.blank('', environ) + req = Request.blank('', environ) suffix = req.path_info.split('.')[-1].lower() if suffix == 'json': self.handler = self._to_json -- cgit From c363c2aaacb01cbbe8dcdaa4bda2e5d2531ab8e8 Mon Sep 17 00:00:00 2001 From: Rick Harris <rick.harris@rackspace.com> Date: Thu, 3 Mar 2011 18:21:54 +0000 Subject: Use %s in case instance_id came through as a string --- nova/compute/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 35a7d7bc0..5f68fcc4d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -319,12 +319,12 @@ class API(base.Base): try: instance = self.get(context, instance_id) except exception.NotFound: - LOG.warning(_("Instance %d was not found during terminate"), + LOG.warning(_("Instance %s was not found during terminate"), instance_id) raise if (instance['state_description'] == 'terminating'): - LOG.warning(_("Instance %d is already being terminated"), + LOG.warning(_("Instance %s is already being terminated"), instance_id) return -- cgit From 0e1a458166ad1e89ca0755d88b8efec39855ee5c Mon Sep 17 00:00:00 2001 From: Cerberus <matt.dietz@rackspace.com> Date: Thu, 3 Mar 2011 13:18:37 -0600 Subject: Renaming my migration yet again --- .../versions/007_add_instance_migrations.py | 61 ---------------------- .../versions/009_add_instance_migrations.py | 61 ++++++++++++++++++++++ 2 files changed, 61 insertions(+), 61 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py deleted file mode 100644 index 4fda525f1..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_instance_migrations.py +++ /dev/null @@ -1,61 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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.from sqlalchemy import * - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - -# -# New Tables -# - -migrations = Table('migrations', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('source_compute', String(255)), - Column('dest_compute', String(255)), - Column('dest_host', String(255)), - Column('instance_id', Integer, ForeignKey('instances.id'), - nullable=True), - Column('status', String(255)), - ) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (migrations, ): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py new file mode 100644 index 000000000..4fda525f1 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py @@ -0,0 +1,61 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 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.from sqlalchemy import * + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + +# +# New Tables +# + +migrations = Table('migrations', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('source_compute', String(255)), + Column('dest_compute', String(255)), + Column('dest_host', String(255)), + Column('instance_id', Integer, ForeignKey('instances.id'), + nullable=True), + Column('status', String(255)), + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (migrations, ): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise -- cgit From 848aced747a60c47d76efcb2147041339df4a628 Mon Sep 17 00:00:00 2001 From: Brian Waldon <brian.waldon@rackspace.com> Date: Thu, 3 Mar 2011 17:21:21 -0500 Subject: Refactor wsgi.Serializer away from handling Requests directly; now require Content-Type in all requests; fix tests according to new code --- nova/api/direct.py | 2 +- nova/api/openstack/__init__.py | 4 +- nova/api/openstack/consoles.py | 2 +- nova/api/openstack/faults.py | 5 +- nova/api/openstack/images.py | 2 +- nova/api/openstack/servers.py | 9 ++- nova/api/openstack/zones.py | 4 +- nova/exception.py | 4 ++ nova/tests/api/openstack/test_servers.py | 1 + nova/tests/api/openstack/test_zones.py | 15 +++-- nova/tests/api/test_wsgi.py | 95 +++++++++++++++++++------------ nova/tests/test_direct.py | 3 + nova/wsgi.py | 96 ++++++++++++++++++++------------ 13 files changed, 152 insertions(+), 90 deletions(-) diff --git a/nova/api/direct.py b/nova/api/direct.py index cd237f649..1d699f947 100644 --- a/nova/api/direct.py +++ b/nova/api/direct.py @@ -206,7 +206,7 @@ class ServiceWrapper(wsgi.Controller): params = dict([(str(k), v) for (k, v) in params.iteritems()]) result = method(context, **params) if type(result) is dict or type(result) is list: - return self._serialize(result, req) + return self._serialize(result, req.best_match()) else: return result diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index b5439788d..6e1a2a06c 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -124,4 +124,6 @@ class Versions(wsgi.Application): metadata = { "application/xml": { "attributes": dict(version=["status", "id"])}} - return wsgi.Serializer(req.environ, metadata).to_content_type(response) + + content_type = req.best_match() + return wsgi.Serializer(metadata).serialize(response, content_type) diff --git a/nova/api/openstack/consoles.py b/nova/api/openstack/consoles.py index 9ebdbe710..8c291c2eb 100644 --- a/nova/api/openstack/consoles.py +++ b/nova/api/openstack/consoles.py @@ -65,7 +65,7 @@ class Controller(wsgi.Controller): def create(self, req, server_id): """Creates a new console""" - #info = self._deserialize(req.body, req) + #info = self._deserialize(req.body, req.get_content_type()) self.console_api.create_console( req.environ['nova.context'], int(server_id)) diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py index c70b00fa3..075fdb997 100644 --- a/nova/api/openstack/faults.py +++ b/nova/api/openstack/faults.py @@ -57,6 +57,7 @@ class Fault(webob.exc.HTTPException): fault_data[fault_name]['retryAfter'] = retry # 'code' is an attribute on the fault tag itself metadata = {'application/xml': {'attributes': {fault_name: 'code'}}} - serializer = wsgi.Serializer(req.environ, metadata) - self.wrapped_exc.body = serializer.to_content_type(fault_data) + serializer = wsgi.Serializer(metadata) + content_type = req.best_match() + self.wrapped_exc.body = serializer.serialize(fault_data, content_type) return self.wrapped_exc diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index cf85a496f..98f0dd96b 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -151,7 +151,7 @@ class Controller(wsgi.Controller): def create(self, req): context = req.environ['nova.context'] - env = self._deserialize(req.body, req) + env = self._deserialize(req.body, req.get_content_type()) instance_id = env["image"]["serverId"] name = env["image"]["name"] diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 08b95b46a..24d2826af 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -141,7 +141,7 @@ class Controller(wsgi.Controller): def create(self, req): """ Creates a new server for a given user """ - env = self._deserialize(req.body, req) + env = self._deserialize(req.body, req.get_content_type()) if not env: return faults.Fault(exc.HTTPUnprocessableEntity()) @@ -182,7 +182,10 @@ class Controller(wsgi.Controller): def update(self, req, id): """ Updates the server name or password """ - inst_dict = self._deserialize(req.body, req) + if len(req.body) == 0: + raise exc.HTTPUnprocessableEntity() + + inst_dict = self._deserialize(req.body, req.get_content_type()) if not inst_dict: return faults.Fault(exc.HTTPUnprocessableEntity()) @@ -205,7 +208,7 @@ class Controller(wsgi.Controller): def action(self, req, id): """ Multi-purpose method used to reboot, rebuild, and resize a server """ - input_dict = self._deserialize(req.body, req) + input_dict = self._deserialize(req.body, req.get_content_type()) #TODO(sandy): rebuild/resize not supported. try: reboot_type = input_dict['reboot']['type'] diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index d5206da20..cf6cd789f 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -67,13 +67,13 @@ class Controller(wsgi.Controller): def create(self, req): context = req.environ['nova.context'] - env = self._deserialize(req.body, req) + env = self._deserialize(req.body, req.get_content_type()) zone = db.zone_create(context, env["zone"]) return dict(zone=_scrub_zone(zone)) def update(self, req, id): context = req.environ['nova.context'] - env = self._deserialize(req.body, req) + env = self._deserialize(req.body, req.get_content_type()) zone_id = int(id) zone = db.zone_update(context, zone_id, env["zone"]) return dict(zone=_scrub_zone(zone)) diff --git a/nova/exception.py b/nova/exception.py index 7d65bd6a5..93c5fe3d7 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -88,6 +88,10 @@ class InvalidInputException(Error): pass +class InvalidContentType(Error): + pass + + class TimeoutException(Error): pass diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 78beb7df9..fae08d0be 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -227,6 +227,7 @@ class ServersTest(test.TestCase): req = webob.Request.blank('/v1.0/servers') req.method = 'POST' req.body = json.dumps(body) + req.headers["Content-Type"] = "application/json" res = req.get_response(fakes.wsgi_app()) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 555b206b9..d0da8eaaf 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -86,24 +86,27 @@ class ZonesTest(test.TestCase): def test_get_zone_list(self): req = webob.Request.blank('/v1.0/zones') + req.headers["Content-Type"] = "application/json" res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) self.assertEqual(len(res_dict['zones']), 2) def test_get_zone_by_id(self): req = webob.Request.blank('/v1.0/zones/1') + req.headers["Content-Type"] = "application/json" res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) + self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) self.assertEqual(res_dict['zone']['id'], 1) self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') self.assertFalse('password' in res_dict['zone']) - self.assertEqual(res.status_int, 200) def test_zone_delete(self): req = webob.Request.blank('/v1.0/zones/1') + req.headers["Content-Type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) @@ -112,13 +115,14 @@ class ZonesTest(test.TestCase): body = dict(zone=dict(api_url='http://blah.zoo', username='fred', password='fubar')) req = webob.Request.blank('/v1.0/zones') + req.headers["Content-Type"] = "application/json" req.method = 'POST' req.body = json.dumps(body) res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) self.assertEqual(res_dict['zone']['id'], 1) self.assertEqual(res_dict['zone']['api_url'], 'http://blah.zoo') self.assertFalse('username' in res_dict['zone']) @@ -126,13 +130,14 @@ class ZonesTest(test.TestCase): def test_zone_update(self): body = dict(zone=dict(username='zeb', password='sneaky')) req = webob.Request.blank('/v1.0/zones/1') + req.headers["Content-Type"] = "application/json" req.method = 'PUT' req.body = json.dumps(body) res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) self.assertEqual(res_dict['zone']['id'], 1) self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') self.assertFalse('username' in res_dict['zone']) diff --git a/nova/tests/api/test_wsgi.py b/nova/tests/api/test_wsgi.py index cf2d0e297..7c0135656 100644 --- a/nova/tests/api/test_wsgi.py +++ b/nova/tests/api/test_wsgi.py @@ -93,29 +93,29 @@ class ControllerTest(test.TestCase): result = request.get_response(self.TestRouter()) self.assertEqual(json.loads(result.body), {"test": {"id": "123"}}) - def test_content_type_from_accept_xml(self): + def test_response_content_type_from_accept_xml(self): request = webob.Request.blank('/tests/123') request.headers["Accept"] = "application/xml" result = request.get_response(self.TestRouter()) self.assertEqual(result.headers["Content-Type"], "application/xml") - def test_content_type_from_accept_json(self): + def test_response_content_type_from_accept_json(self): request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = "application/json" result = request.get_response(self.TestRouter()) self.assertEqual(result.headers["Content-Type"], "application/json") - def test_content_type_from_query_extension_xml(self): + def test_response_content_type_from_query_extension_xml(self): request = wsgi.Request.blank('/tests/123.xml') result = request.get_response(self.TestRouter()) self.assertEqual(result.headers["Content-Type"], "application/xml") - def test_content_type_from_query_extension_json(self): + def test_response_content_type_from_query_extension_json(self): request = wsgi.Request.blank('/tests/123.json') result = request.get_response(self.TestRouter()) self.assertEqual(result.headers["Content-Type"], "application/json") - def test_content_type_default_when_unsupported(self): + def test_response_content_type_default_when_unsupported(self): request = wsgi.Request.blank('/tests/123.unsupported') request.headers["Accept"] = "application/unsupported1" result = request.get_response(self.TestRouter()) @@ -125,6 +125,17 @@ class ControllerTest(test.TestCase): class RequestTest(test.TestCase): + def test_request_content_type_missing(self): + request = wsgi.Request.blank('/tests/123') + request.body = "<body />" + self.assertRaises(webob.exc.HTTPBadRequest, request.get_content_type) + + def test_request_content_type_unsupported(self): + request = wsgi.Request.blank('/tests/123') + request.headers["Content-Type"] = "text/html" + request.body = "asdf<br />" + self.assertRaises(webob.exc.HTTPBadRequest, request.get_content_type) + def test_content_type_from_accept_xml(self): request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = "application/xml" @@ -173,40 +184,48 @@ class RequestTest(test.TestCase): self.assertEqual(result, "application/json") - - - class SerializerTest(test.TestCase): - def match(self, url, accept, expect): + def test_xml(self): input_dict = dict(servers=dict(a=(2, 3))) expected_xml = '<servers><a>(2,3)</a></servers>' + serializer = wsgi.Serializer() + result = serializer.serialize(input_dict, "application/xml") + result = result.replace('\n', '').replace(' ', '') + self.assertEqual(result, expected_xml) + + def test_json(self): + input_dict = dict(servers=dict(a=(2, 3))) expected_json = '{"servers":{"a":[2,3]}}' - req = webob.Request.blank(url, headers=dict(Accept=accept)) - result = wsgi.Serializer(req.environ).to_content_type(input_dict) + serializer = wsgi.Serializer() + result = serializer.serialize(input_dict, "application/json") result = result.replace('\n', '').replace(' ', '') - if expect == 'xml': - self.assertEqual(result, expected_xml) - elif expect == 'json': - self.assertEqual(result, expected_json) - else: - raise "Bad expect value" - - def test_basic(self): - self.match('/servers/4.json', None, expect='json') - self.match('/servers/4', 'application/json', expect='json') - self.match('/servers/4', 'application/xml', expect='xml') - self.match('/servers/4.xml', None, expect='xml') - - def test_defaults_to_json(self): - self.match('/servers/4', None, expect='json') - self.match('/servers/4', 'text/html', expect='json') - - def test_suffix_takes_precedence_over_accept_header(self): - self.match('/servers/4.xml', 'application/json', expect='xml') - self.match('/servers/4.xml.', 'application/json', expect='json') - - def test_deserialize(self): + self.assertEqual(result, expected_json) + + def test_unsupported_content_type(self): + serializer = wsgi.Serializer() + self.assertRaises(exception.InvalidContentType, serializer.serialize, + {}, "text/null") + + def test_deserialize_json(self): + data = """{"a": { + "a1": "1", + "a2": "2", + "bs": ["1", "2", "3", {"c": {"c1": "1"}}], + "d": {"e": "1"}, + "f": "1"}}""" + as_dict = dict(a={ + 'a1': '1', + 'a2': '2', + 'bs': ['1', '2', '3', {'c': dict(c1='1')}], + 'd': {'e': '1'}, + 'f': '1'}) + metadata = {} + serializer = wsgi.Serializer(metadata) + self.assertEqual(serializer.deserialize(data, "application/json"), + as_dict) + + def test_deserialize_xml(self): xml = """ <a a1="1" a2="2"> <bs><b>1</b><b>2</b><b>3</b><b><c c1="1"/></b></bs> @@ -221,11 +240,13 @@ class SerializerTest(test.TestCase): 'd': {'e': '1'}, 'f': '1'}) metadata = {'application/xml': dict(plurals={'bs': 'b', 'ts': 't'})} - serializer = wsgi.Serializer({}, metadata) - self.assertEqual(serializer.deserialize(xml), as_dict) + serializer = wsgi.Serializer(metadata) + self.assertEqual(serializer.deserialize(xml, "application/xml"), + as_dict) def test_deserialize_empty_xml(self): xml = """<a></a>""" as_dict = {"a": {}} - serializer = wsgi.Serializer({}) - self.assertEqual(serializer.deserialize(xml), as_dict) + serializer = wsgi.Serializer() + self.assertEqual(serializer.deserialize(xml, "application/xml"), + as_dict) diff --git a/nova/tests/test_direct.py b/nova/tests/test_direct.py index b6bfab534..85bfcfd85 100644 --- a/nova/tests/test_direct.py +++ b/nova/tests/test_direct.py @@ -59,6 +59,7 @@ class DirectTestCase(test.TestCase): req.headers['X-OpenStack-User'] = 'user1' req.headers['X-OpenStack-Project'] = 'proj1' resp = req.get_response(self.auth_router) + self.assertEqual(resp.status_int, 200) data = json.loads(resp.body) self.assertEqual(data['user'], 'user1') self.assertEqual(data['project'], 'proj1') @@ -69,6 +70,7 @@ class DirectTestCase(test.TestCase): req.method = 'POST' req.body = 'json=%s' % json.dumps({'data': 'foo'}) resp = req.get_response(self.router) + self.assertEqual(resp.status_int, 200) resp_parsed = json.loads(resp.body) self.assertEqual(resp_parsed['data'], 'foo') @@ -78,6 +80,7 @@ class DirectTestCase(test.TestCase): req.method = 'POST' req.body = 'data=foo' resp = req.get_response(self.router) + self.assertEqual(resp.status_int, 200) resp_parsed = json.loads(resp.body) self.assertEqual(resp_parsed['data'], 'foo') diff --git a/nova/wsgi.py b/nova/wsgi.py index 4577439cb..c3e08522d 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -36,6 +36,7 @@ import webob.exc from paste import deploy +from nova import exception from nova import flags from nova import log as logging from nova import utils @@ -102,6 +103,14 @@ class Request(webob.Request): return bm or "application/json" + def get_content_type(self): + try: + ct = self.headers["Content-Type"] + assert ct in ("application/xml", "application/json") + return ct + except Exception: + raise webob.exc.HTTPBadRequest("Invalid content type") + class Application(object): """Base WSGI application wrapper. Subclasses need to implement __call__.""" @@ -343,30 +352,41 @@ class Controller(object): del arg_dict['format'] arg_dict['req'] = req result = method(**arg_dict) + if type(result) is dict: - return self._serialize(result, req) + content_type = req.best_match() + body = self._serialize(result, content_type) + + response = webob.Response() + response.headers["Content-Type"] = content_type + response.body = body + return response + else: return result - def _serialize(self, data, request): + def _serialize(self, data, content_type): """ - Serialize the given dict to the response type requested in request. + Serialize the given dict to the provided content_type. Uses self._serialization_metadata if it exists, which is a dict mapping MIME types to information needed to serialize to that type. """ _metadata = getattr(type(self), "_serialization_metadata", {}) - serializer = Serializer(request.environ, _metadata) - return serializer.to_content_type(data) + serializer = Serializer(_metadata) + try: + return serializer.serialize(data, content_type) + except exception.InvalidContentType: + raise webob.exc.HTTPNotAcceptable() - def _deserialize(self, data, request): + def _deserialize(self, data, content_type): """ - Deserialize the request body to the response type requested in request. + Deserialize the request body to the specefied content type. Uses self._serialization_metadata if it exists, which is a dict mapping MIME types to information needed to serialize to that type. """ _metadata = getattr(type(self), "_serialization_metadata", {}) - serializer = Serializer(request.environ, _metadata) - return serializer.deserialize(data) + serializer = Serializer(_metadata) + return serializer.deserialize(data, content_type) class Serializer(object): @@ -374,50 +394,52 @@ class Serializer(object): Serializes and deserializes dictionaries to certain MIME types. """ - def __init__(self, environ, metadata=None): + def __init__(self, metadata=None): """ Create a serializer based on the given WSGI environment. 'metadata' is an optional dict mapping MIME types to information needed to serialize a dictionary to that type. """ self.metadata = metadata or {} - req = Request.blank('', environ) - suffix = req.path_info.split('.')[-1].lower() - if suffix == 'json': - self.handler = self._to_json - elif suffix == 'xml': - self.handler = self._to_xml - elif 'application/json' in req.accept: - self.handler = self._to_json - elif 'application/xml' in req.accept: - self.handler = self._to_xml - else: - # This is the default - self.handler = self._to_json - def to_content_type(self, data): - """ - Serialize a dictionary into a string. + def _get_serialize_handler(self, content_type): + handlers = { + "application/json": self._to_json, + "application/xml": self._to_xml, + } + + try: + return handlers[content_type] + except Exception: + raise exception.InvalidContentType() - The format of the string will be decided based on the Content Type - requested in self.environ: by Accept: header, or by URL suffix. + def serialize(self, data, content_type): + """ + Serialize a dictionary into a string of the specified content type. """ - return self.handler(data) + return self._get_serialize_handler(content_type)(data) - def deserialize(self, datastring): + def deserialize(self, datastring, content_type): """ Deserialize a string to a dictionary. The string must be in the format of a supported MIME type. """ - datastring = datastring.strip() + return self.get_deserialize_handler(content_type)(datastring) + + def get_deserialize_handler(self, content_type): + handlers = { + "application/json": self._from_json, + "application/xml": self._from_xml, + } + try: - is_xml = (datastring[0] == '<') - if not is_xml: - return utils.loads(datastring) - return self._from_xml(datastring) - except: - return None + return handlers[content_type] + except Exception: + raise exception.InvalidContentType() + + def _from_json(self, datastring): + return utils.loads(datastring) def _from_xml(self, datastring): xmldata = self.metadata.get('application/xml', {}) -- cgit From a433ddeda77aaa4462694661ecdca71eed6db669 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Fri, 4 Mar 2011 02:36:55 +0000 Subject: Replace objectstore images with S3 image service backending to glance or local --- bin/nova-manage | 2 +- nova/api/ec2/cloud.py | 127 +++++++++-------- nova/flags.py | 2 +- nova/image/glance.py | 29 ++-- nova/image/s3.py | 280 ++++++++++++++++++++++++++++---------- nova/image/service.py | 4 +- nova/tests/api/openstack/fakes.py | 11 +- nova/tests/fake_flags.py | 1 + nova/tests/test_cloud.py | 22 ++- nova/tests/test_compute.py | 7 +- nova/tests/test_direct.py | 3 +- nova/tests/test_quota.py | 6 +- 12 files changed, 334 insertions(+), 160 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 9bf3a1bb3..0f7604aeb 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -81,7 +81,7 @@ from nova import log as logging from nova import quota from nova import rpc from nova import utils -from nova.api.ec2.cloud import ec2_id_to_id +from nova.api.ec2.ec2utils import ec2_id_to_id from nova.auth import manager from nova.cloudpipe import pipelib from nova.compute import instance_types diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 844ccbe5e..8c2e77d86 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -39,7 +39,9 @@ from nova import log as logging from nova import network from nova import utils from nova import volume +from nova.api.ec2 import ec2utils from nova.compute import instance_types +from nova.image import s3 FLAGS = flags.FLAGS @@ -73,30 +75,19 @@ def _gen_key(context, user_id, key_name): return {'private_key': private_key, 'fingerprint': fingerprint} -def ec2_id_to_id(ec2_id): - """Convert an ec2 ID (i-[base 16 number]) to an instance id (int)""" - return int(ec2_id.split('-')[-1], 16) - - -def id_to_ec2_id(instance_id, template='i-%08x'): - """Convert an instance ID (int) to an ec2 ID (i-[base 16 number])""" - return template % instance_id - - class CloudController(object): """ CloudController provides the critical dispatch between inbound API calls through the endpoint and messages sent to the other nodes. """ def __init__(self): - self.image_service = utils.import_object(FLAGS.image_service) + self.image_service = s3.S3ImageService() self.network_api = network.API() self.volume_api = volume.API() self.compute_api = compute.API( network_api=self.network_api, - image_service=self.image_service, volume_api=self.volume_api, - hostname_factory=id_to_ec2_id) + hostname_factory=ec2utils.id_to_ec2_id) self.setup() def __str__(self): @@ -154,7 +145,7 @@ class CloudController(object): availability_zone = self._get_availability_zone_by_host(ctxt, host) floating_ip = db.instance_get_floating_address(ctxt, instance_ref['id']) - ec2_id = id_to_ec2_id(instance_ref['id']) + ec2_id = ec2utils.id_to_ec2_id(instance_ref['id']) data = { 'user-data': base64.b64decode(instance_ref['user_data']), 'meta-data': { @@ -525,7 +516,7 @@ class CloudController(object): ec2_id = instance_id[0] else: ec2_id = instance_id - instance_id = ec2_id_to_id(ec2_id) + instance_id = ec2utils.ec2_id_to_id(ec2_id) output = self.compute_api.get_console_output( context, instance_id=instance_id) now = datetime.datetime.utcnow() @@ -535,7 +526,7 @@ class CloudController(object): def get_ajax_console(self, context, instance_id, **kwargs): ec2_id = instance_id[0] - instance_id = ec2_id_to_id(ec2_id) + instance_id = ec2utils.ec2_id_to_id(ec2_id) return self.compute_api.get_ajax_console(context, instance_id=instance_id) @@ -543,7 +534,7 @@ class CloudController(object): if volume_id: volumes = [] for ec2_id in volume_id: - internal_id = ec2_id_to_id(ec2_id) + internal_id = ec2utils.ec2_id_to_id(ec2_id) volume = self.volume_api.get(context, internal_id) volumes.append(volume) else: @@ -556,11 +547,11 @@ class CloudController(object): instance_data = None if volume.get('instance', None): instance_id = volume['instance']['id'] - instance_ec2_id = id_to_ec2_id(instance_id) + instance_ec2_id = ec2utils.id_to_ec2_id(instance_id) instance_data = '%s[%s]' % (instance_ec2_id, volume['instance']['host']) v = {} - v['volumeId'] = id_to_ec2_id(volume['id'], 'vol-%08x') + v['volumeId'] = ec2utils.id_to_ec2_id(volume['id'], 'vol-%08x') v['status'] = volume['status'] v['size'] = volume['size'] v['availabilityZone'] = volume['availability_zone'] @@ -578,8 +569,7 @@ class CloudController(object): 'device': volume['mountpoint'], 'instanceId': instance_ec2_id, 'status': 'attached', - 'volumeId': id_to_ec2_id(volume['id'], - 'vol-%08x')}] + 'volumeId': v['volumeId']}] else: v['attachmentSet'] = [{}] @@ -598,12 +588,12 @@ class CloudController(object): return {'volumeSet': [self._format_volume(context, dict(volume))]} def delete_volume(self, context, volume_id, **kwargs): - volume_id = ec2_id_to_id(volume_id) + volume_id = ec2utils.ec2_id_to_id(volume_id) self.volume_api.delete(context, volume_id=volume_id) return True def update_volume(self, context, volume_id, **kwargs): - volume_id = ec2_id_to_id(volume_id) + volume_id = ec2utils.ec2_id_to_id(volume_id) updatable_fields = ['display_name', 'display_description'] changes = {} for field in updatable_fields: @@ -614,8 +604,8 @@ class CloudController(object): return True def attach_volume(self, context, volume_id, instance_id, device, **kwargs): - volume_id = ec2_id_to_id(volume_id) - instance_id = ec2_id_to_id(instance_id) + volume_id = ec2utils.ec2_id_to_id(volume_id) + instance_id = ec2utils.ec2_id_to_id(instance_id) msg = _("Attach volume %(volume_id)s to instance %(instance_id)s" " at %(device)s") % locals() LOG.audit(msg, context=context) @@ -626,22 +616,22 @@ class CloudController(object): volume = self.volume_api.get(context, volume_id) return {'attachTime': volume['attach_time'], 'device': volume['mountpoint'], - 'instanceId': id_to_ec2_id(instance_id), + 'instanceId': ec2utils.id_to_ec2_id(instance_id), 'requestId': context.request_id, 'status': volume['attach_status'], - 'volumeId': id_to_ec2_id(volume_id, 'vol-%08x')} + 'volumeId': ec2utils.id_to_ec2_id(volume_id, 'vol-%08x')} def detach_volume(self, context, volume_id, **kwargs): - volume_id = ec2_id_to_id(volume_id) + volume_id = ec2utils.ec2_id_to_id(volume_id) LOG.audit(_("Detach volume %s"), volume_id, context=context) volume = self.volume_api.get(context, volume_id) instance = self.compute_api.detach_volume(context, volume_id=volume_id) return {'attachTime': volume['attach_time'], 'device': volume['mountpoint'], - 'instanceId': id_to_ec2_id(instance['id']), + 'instanceId': ec2utils.id_to_ec2_id(instance['id']), 'requestId': context.request_id, 'status': volume['attach_status'], - 'volumeId': id_to_ec2_id(volume_id, 'vol-%08x')} + 'volumeId': ec2utils.id_to_ec2_id(volume_id, 'vol-%08x')} def _convert_to_set(self, lst, label): if lst == None or lst == []: @@ -675,7 +665,7 @@ class CloudController(object): if instance_id: instances = [] for ec2_id in instance_id: - internal_id = ec2_id_to_id(ec2_id) + internal_id = ec2utils.ec2_id_to_id(ec2_id) instance = self.compute_api.get(context, instance_id=internal_id) instances.append(instance) @@ -687,7 +677,7 @@ class CloudController(object): continue i = {} instance_id = instance['id'] - ec2_id = id_to_ec2_id(instance_id) + ec2_id = ec2utils.id_to_ec2_id(instance_id) i['instanceId'] = ec2_id i['imageId'] = instance['image_id'] i['instanceState'] = { @@ -755,7 +745,7 @@ class CloudController(object): if (floating_ip_ref['fixed_ip'] and floating_ip_ref['fixed_ip']['instance']): instance_id = floating_ip_ref['fixed_ip']['instance']['id'] - ec2_id = id_to_ec2_id(instance_id) + ec2_id = ec2utils.id_to_ec2_id(instance_id) address_rv = {'public_ip': address, 'instance_id': ec2_id} if context.is_admin: @@ -778,7 +768,7 @@ class CloudController(object): def associate_address(self, context, instance_id, public_ip, **kwargs): LOG.audit(_("Associate address %(public_ip)s to" " instance %(instance_id)s") % locals(), context=context) - instance_id = ec2_id_to_id(instance_id) + instance_id = ec2utils.ec2_id_to_id(instance_id) self.compute_api.associate_floating_ip(context, instance_id=instance_id, address=public_ip) @@ -791,13 +781,17 @@ class CloudController(object): def run_instances(self, context, **kwargs): max_count = int(kwargs.get('max_count', 1)) + if kwargs.get('kernel_id'): + kwargs['kernel_id'] = ec2utils.ec2_id_to_id(kwargs['kernel_id']) + if kwargs.get('ramdisk_id'): + kwargs['ramdisk_id'] = ec2utils.ec2_id_to_id(kwargs['ramdisk_id']) instances = self.compute_api.create(context, instance_type=instance_types.get_by_type( kwargs.get('instance_type', None)), - image_id=kwargs['image_id'], + image_id=ec2utils.ec2_id_to_id(kwargs['image_id']), min_count=int(kwargs.get('min_count', max_count)), max_count=max_count, - kernel_id=kwargs.get('kernel_id', None), + kernel_id=kwargs.get('kernel_id'), ramdisk_id=kwargs.get('ramdisk_id'), display_name=kwargs.get('display_name'), display_description=kwargs.get('display_description'), @@ -814,7 +808,7 @@ class CloudController(object): instance_id is a kwarg so its name cannot be modified.""" LOG.debug(_("Going to start terminating instances")) for ec2_id in instance_id: - instance_id = ec2_id_to_id(ec2_id) + instance_id = ec2utils.ec2_id_to_id(ec2_id) self.compute_api.delete(context, instance_id=instance_id) return True @@ -822,19 +816,19 @@ class CloudController(object): """instance_id is a list of instance ids""" LOG.audit(_("Reboot instance %r"), instance_id, context=context) for ec2_id in instance_id: - instance_id = ec2_id_to_id(ec2_id) + instance_id = ec2utils.ec2_id_to_id(ec2_id) self.compute_api.reboot(context, instance_id=instance_id) return True def rescue_instance(self, context, instance_id, **kwargs): """This is an extension to the normal ec2_api""" - instance_id = ec2_id_to_id(instance_id) + instance_id = ec2utils.ec2_id_to_id(instance_id) self.compute_api.rescue(context, instance_id=instance_id) return True def unrescue_instance(self, context, instance_id, **kwargs): """This is an extension to the normal ec2_api""" - instance_id = ec2_id_to_id(instance_id) + instance_id = ec2utils.ec2_id_to_id(instance_id) self.compute_api.unrescue(context, instance_id=instance_id) return True @@ -845,41 +839,50 @@ class CloudController(object): if field in kwargs: changes[field] = kwargs[field] if changes: - instance_id = ec2_id_to_id(ec2_id) + instance_id = ec2utils.ec2_id_to_id(ec2_id) self.compute_api.update(context, instance_id=instance_id, **kwargs) return True - def _format_image(self, context, image): + def _format_image(self, image): """Convert from format defined by BaseImageService to S3 format.""" i = {} i['imageId'] = image.get('id') - i['kernelId'] = image.get('kernel_id') - i['ramdiskId'] = image.get('ramdisk_id') - i['imageOwnerId'] = image.get('owner_id') - i['imageLocation'] = image.get('location') - i['imageState'] = image.get('status') + i['kernelId'] = image['properties'].get('kernel_id') + i['ramdiskId'] = image['properties'].get('ramdisk_id') + i['imageOwnerId'] = image['properties'].get('owner_id') + i['imageLocation'] = image['properties'].get('image_location') + i['imageState'] = image['properties'].get('image_state') i['type'] = image.get('type') - i['isPublic'] = image.get('is_public') - i['architecture'] = image.get('architecture') + i['isPublic'] = image['properties'].get('is_public') == 'True' + i['architecture'] = image['properties'].get('architecture') return i def describe_images(self, context, image_id=None, **kwargs): # NOTE: image_id is a list! - images = self.image_service.index(context) if image_id: - images = filter(lambda x: x['id'] in image_id, images) - images = [self._format_image(context, i) for i in images] + images = [] + for ec2_id in image_id: + try: + image = self.image_service.show(context, ec2_id) + except exception.NotFound: + raise exception.NotFound(_('Image %s not found') % + ec2_id) + images.append(image) + else: + images = self.image_service.detail(context) + images = [self._format_image(i) for i in images] return {'imagesSet': images} def deregister_image(self, context, image_id, **kwargs): LOG.audit(_("De-registering image %s"), image_id, context=context) - self.image_service.deregister(context, image_id) + self.image_service.delete(context, image_id) return {'imageId': image_id} def register_image(self, context, image_location=None, **kwargs): if image_location is None and 'name' in kwargs: image_location = kwargs['name'] - image_id = self.image_service.register(context, image_location) + image = {"image_location": image_location} + image_id = self.image_service.create(context, image) msg = _("Registered image %(image_location)s with" " id %(image_id)s") % locals() LOG.audit(msg, context=context) @@ -890,11 +893,10 @@ class CloudController(object): raise exception.ApiError(_('attribute not supported: %s') % attribute) try: - image = self._format_image(context, - self.image_service.show(context, + image = self._format_image(self.image_service.show(context, image_id)) - except IndexError: - raise exception.ApiError(_('invalid id: %s') % image_id) + except (IndexError, exception.NotFound): + raise exception.NotFound(_('Image %s not found') % image_id) result = {'image_id': image_id, 'launchPermission': []} if image['isPublic']: result['launchPermission'].append({'group': 'all'}) @@ -913,7 +915,14 @@ class CloudController(object): if not operation_type in ['add', 'remove']: raise exception.ApiError(_('operation_type must be add or remove')) LOG.audit(_("Updating image %s publicity"), image_id, context=context) - return self.image_service.modify(context, image_id, operation_type) + + try: + metadata = self.image_service.show(context, image_id) + except exception.NotFound: + raise exception.NotFound(_('Image %s not found') % image_id) + del(metadata['id']) + metadata['properties']['is_public'] = (operation_type == 'add') + return self.image_service.update(context, image_id, metadata) def update_image(self, context, image_id, **kwargs): result = self.image_service.update(context, image_id, dict(kwargs)) diff --git a/nova/flags.py b/nova/flags.py index 8cf199b2f..f01a4d096 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -346,7 +346,7 @@ DEFINE_string('scheduler_manager', 'nova.scheduler.manager.SchedulerManager', 'Manager for scheduler') # The service to use for image search and retrieval -DEFINE_string('image_service', 'nova.image.s3.S3ImageService', +DEFINE_string('image_service', 'nova.image.glance.GlanceImageService', 'The service to use for retrieving and searching for images.') DEFINE_string('host', socket.gethostname(), diff --git a/nova/image/glance.py b/nova/image/glance.py index 593c4bce6..7db94c0d4 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -21,6 +21,8 @@ import httplib import json import urlparse +from glance.common import exception as glance_exception + from nova import exception from nova import flags from nova import log as logging @@ -57,27 +59,32 @@ class GlanceImageService(service.BaseImageService): """ Returns a dict containing image data for the given opaque image id. """ - image = self.client.get_image_meta(id) - if image: - return image - raise exception.NotFound + try: + image = self.client.get_image_meta(id) + except glance_exception.NotFound: + raise exception.NotFound + return image - def create(self, context, data): + def create(self, context, metadata, data=None): """ Store the image data and return the new image id. :raises AlreadyExists if the image already exist. """ - return self.client.add_image(image_meta=data) + return self.client.add_image(metadata, data) - def update(self, context, image_id, data): + def update(self, context, image_id, metadata, data=None): """Replace the contents of the given image with the new data. :raises NotFound if the image does not exist. """ - return self.client.update_image(image_id, data) + try: + result = self.client.update_image(image_id, metadata, data) + except glance_exception.NotFound: + raise exception.NotFound + return result def delete(self, context, image_id): """ @@ -86,7 +93,11 @@ class GlanceImageService(service.BaseImageService): :raises NotFound if the image does not exist. """ - return self.client.delete_image(image_id) + try: + result = self.client.delete_image(image_id) + except glance_exception.NotFound: + raise exception.NotFound + return result def delete_all(self): """ diff --git a/nova/image/s3.py b/nova/image/s3.py index 14135a1ee..a740b010c 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -21,8 +21,12 @@ Proxy AMI-related calls from the cloud controller, to the running objectstore service. """ -import json -import urllib +import binascii +import os +import shutil +import tarfile +import tempfile +from xml.etree import ElementTree import boto.s3.connection @@ -31,84 +35,89 @@ from nova import flags from nova import utils from nova.auth import manager from nova.image import service +from nova.api.ec2 import ec2utils FLAGS = flags.FLAGS +flags.DEFINE_string('image_decryption_dir', '/tmp', + 'parent dir for tempdir used for image decryption') -def map_s3_to_base(image): - """Convert from S3 format to format defined by BaseImageService.""" - i = {} - i['id'] = image.get('imageId') - i['name'] = image.get('imageId') - i['kernel_id'] = image.get('kernelId') - i['ramdisk_id'] = image.get('ramdiskId') - i['location'] = image.get('imageLocation') - i['owner_id'] = image.get('imageOwnerId') - i['status'] = image.get('imageState') - i['type'] = image.get('type') - i['is_public'] = image.get('isPublic') - i['architecture'] = image.get('architecture') - return i +_type_prefix_map = {'machine': 'ami', + 'kernel': 'aki', + 'ramdisk': 'ari'} + + +def image_ec2_id(image_id, image_type): + prefix = _type_prefix_map[image_type] + template = prefix + '-%08x' + return ec2utils.id_to_ec2_id(int(image_id), template=template) class S3ImageService(service.BaseImageService): + def __init__(self, service=None, *args, **kwargs): + if service == None: + service = utils.import_object(FLAGS.image_service) + self.service = service + self.service.__init__(*args, **kwargs) + + def create(self, context, properties, data=None): + """image should contain image_location""" + image_id, metadata = self._s3_create(context, properties) + return image_ec2_id(image_id, metadata['type']) + + def delete(self, context, image_id): + # FIXME(vish): call to show is to check filter + self.show(context, image_id) + image_id = ec2utils.ec2_id_to_id(image_id) + self.service.delete(context, image_id) - def modify(self, context, image_id, operation): - self._conn(context).make_request( - method='POST', - bucket='_images', - query_args=self._qs({'image_id': image_id, - 'operation': operation})) - return True - - def update(self, context, image_id, attributes): - """update an image's attributes / info.json""" - attributes.update({"image_id": image_id}) - self._conn(context).make_request( - method='POST', - bucket='_images', - query_args=self._qs(attributes)) - return True - - def register(self, context, image_location): - """ rpc call to register a new image based from a manifest """ - image_id = utils.generate_uid('ami') - self._conn(context).make_request( - method='PUT', - bucket='_images', - query_args=self._qs({'image_location': image_location, - 'image_id': image_id})) - return image_id + def update(self, context, image_id, metadata, data=None): + # FIXME(vish): call to show is to check filter + self.show(context, image_id) + image_id = ec2utils.ec2_id_to_id(image_id) + image = self.service.update(context, image_id, metadata, data) + image['id'] = image_ec2_id(image['id'], image['type']) + return image def index(self, context): - """Return a list of all images that a user can see.""" - response = self._conn(context).make_request( - method='GET', - bucket='_images') - images = json.loads(response.read()) - return [map_s3_to_base(i) for i in images] + images = self.service.index(context) + # FIXME(vish): index doesn't filter so we do it manually + return self._filter(context, images) + + def detail(self, context): + images = self.service.detail(context) + # FIXME(vish): detail doesn't filter so we do it manually + return self._filter(context, images) + + @staticmethod + def _is_visible(context, image): + return (context.is_admin + or context.project_id == image['properties']['owner_id'] + or image['properties']['is_public'] == 'True') + + @staticmethod + def _filter(context, images): + filtered = [] + for image in images: + if not S3ImageService._is_visible(context, image): + continue + image['id'] = image_ec2_id(image['id'], image['type']) + filtered.append(image) + return filtered def show(self, context, image_id): - """return a image object if the context has permissions""" - if FLAGS.connection_type == 'fake': - return {'imageId': 'bar'} - result = self.index(context) - result = [i for i in result if i['id'] == image_id] - if not result: - raise exception.NotFound(_('Image %s could not be found') - % image_id) - image = result[0] + image_id = ec2utils.ec2_id_to_id(image_id) + image = self.service.show(context, image_id) + if not self._is_visible(context, image): + raise exception.NotFound + image['id'] = image_ec2_id(image['id'], image['type']) return image - def deregister(self, context, image_id): - """ unregister an image """ - self._conn(context).make_request( - method='DELETE', - bucket='_images', - query_args=self._qs({'image_id': image_id})) - - def _conn(self, context): + @staticmethod + def _conn(context): + # TODO(vish): is there a better way to get creds to sign + # for the user? access = manager.AuthManager().get_access_key(context.user, context.project) secret = str(context.user.secret) @@ -120,8 +129,139 @@ class S3ImageService(service.BaseImageService): port=FLAGS.s3_port, host=FLAGS.s3_host) - def _qs(self, params): - pairs = [] - for key in params.keys(): - pairs.append(key + '=' + urllib.quote(params[key])) - return '&'.join(pairs) + @staticmethod + def _download_file(bucket, filename, local_dir): + key = bucket.get_key(filename) + local_filename = os.path.join(local_dir, filename) + key.get_contents_to_filename(local_filename) + return local_filename + + def _s3_create(self, context, properties): + image_path = tempfile.mkdtemp(dir=FLAGS.image_decryption_dir) + + image_location = properties['image_location'] + bucket_name = image_location.split("/")[0] + manifest_path = image_location[len(bucket_name) + 1:] + bucket = self._conn(context).get_bucket(bucket_name) + key = bucket.get_key(manifest_path) + manifest = key.get_contents_as_string() + + manifest = ElementTree.fromstring(manifest) + image_type = 'machine' + + try: + kernel_id = manifest.find("machine_configuration/kernel_id").text + if kernel_id == 'true': + image_type = 'kernel' + kernel_id = None + except: + kernel_id = None + + try: + ramdisk_id = manifest.find("machine_configuration/ramdisk_id").text + if ramdisk_id == 'true': + image_type = 'ramdisk' + ramdisk_id = None + except: + ramdisk_id = None + + try: + arch = manifest.find("machine_configuration/architecture").text + except: + arch = 'x86_64' + + properties.update({'owner_id': context.project_id, + 'architecture': arch}) + + if kernel_id: + properties['kernel_id'] = kernel_id + + if ramdisk_id: + properties['ramdisk_id'] = ramdisk_id + + properties['is_public'] = False + metadata = {'type': image_type, + 'status': 'queued', + 'is_public': True, + 'properties': properties} + metadata['properties']['image_state'] = 'pending' + image = self.service.create(context, metadata) + image_id = image['id'] + + parts = [] + for fn_element in manifest.find("image").getiterator("filename"): + part = self._download_file(bucket, fn_element.text, image_path) + parts.append(part) + + # NOTE(vish): this may be suboptimal, should we use cat? + encrypted_filename = os.path.join(image_path, 'image.encrypted') + with open(encrypted_filename, 'w') as combined: + for filename in parts: + with open(filename) as part: + shutil.copyfileobj(part, combined) + + metadata['properties']['image_state'] = 'decrypting' + self.service.update(context, image_id, metadata) + + hex_key = manifest.find("image/ec2_encrypted_key").text + encrypted_key = binascii.a2b_hex(hex_key) + hex_iv = manifest.find("image/ec2_encrypted_iv").text + encrypted_iv = binascii.a2b_hex(hex_iv) + + # FIXME(vish): grab key from common service so this can run on + # any host. + cloud_private_key = os.path.join(FLAGS.ca_path, "private/cakey.pem") + + decrypted_filename = os.path.join(image_path, 'image.tar.gz') + self._decrypt_image(encrypted_filename, encrypted_key, encrypted_iv, + cloud_private_key, decrypted_filename) + + metadata['properties']['image_state'] = 'untarring' + self.service.update(context, image_id, metadata) + + unz_filename = self._untarzip_image(image_path, decrypted_filename) + + metadata['properties']['image_state'] = 'uploading' + with open(unz_filename) as image_file: + self.service.update(context, image_id, metadata, image_file) + metadata['properties']['image_state'] = 'available' + self.service.update(context, image_id, metadata) + + shutil.rmtree(image_path) + return image_id, metadata + + @staticmethod + def _decrypt_image(encrypted_filename, encrypted_key, encrypted_iv, + cloud_private_key, decrypted_filename): + key, err = utils.execute( + 'openssl rsautl -decrypt -inkey %s' % cloud_private_key, + process_input=encrypted_key, + check_exit_code=False) + if err: + raise exception.Error(_("Failed to decrypt private key: %s") + % err) + iv, err = utils.execute( + 'openssl rsautl -decrypt -inkey %s' % cloud_private_key, + process_input=encrypted_iv, + check_exit_code=False) + if err: + raise exception.Error(_("Failed to decrypt initialization " + "vector: %s") % err) + + _out, err = utils.execute( + 'openssl enc -d -aes-128-cbc -in %s -K %s -iv %s -out %s' + % (encrypted_filename, key, iv, decrypted_filename), + check_exit_code=False) + if err: + raise exception.Error(_("Failed to decrypt image file " + "%(image_file)s: %(err)s") % + {'image_file': encrypted_filename, + 'err': err}) + + @staticmethod + def _untarzip_image(path, filename): + tar_file = tarfile.open(filename, "r|gz") + tar_file.extractall(path) + image_file = tar_file.getnames()[0] + tar_file.close() + return os.path.join(path, image_file) diff --git a/nova/image/service.py b/nova/image/service.py index ebee2228d..e429955f4 100644 --- a/nova/image/service.py +++ b/nova/image/service.py @@ -76,7 +76,7 @@ class BaseImageService(object): """ raise NotImplementedError - def create(self, context, data): + def create(self, context, metadata, data=None): """ Store the image data and return the new image id. @@ -85,7 +85,7 @@ class BaseImageService(object): """ raise NotImplementedError - def update(self, context, image_id, data): + def update(self, context, image_id, metadata, data=None): """Replace the contents of the given image with the new data. :raises NotFound if the image does not exist. diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 49ce8c1b5..7b016db08 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -25,6 +25,7 @@ import webob.dec from paste import urlmap from glance import client as glance_client +from glance.common import exception as glance_exc from nova import auth from nova import context @@ -149,25 +150,25 @@ def stub_out_glance(stubs, initial_fixtures=None): for f in self.fixtures: if f['id'] == image_id: return f - return None + raise glance_exc.NotFound - def fake_add_image(self, image_meta): + def fake_add_image(self, image_meta, data=None): id = ''.join(random.choice(string.letters) for _ in range(20)) image_meta['id'] = id self.fixtures.append(image_meta) return id - def fake_update_image(self, image_id, image_meta): + def fake_update_image(self, image_id, image_meta, data=None): f = self.fake_get_image_meta(image_id) if not f: - raise exc.NotFound + raise glance_exc.NotFound f.update(image_meta) def fake_delete_image(self, image_id): f = self.fake_get_image_meta(image_id) if not f: - raise exc.NotFound + raise glance_exc.NotFound self.fixtures.remove(f) diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index cbd949477..5d7ca98b5 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -32,6 +32,7 @@ flags.DECLARE('fake_network', 'nova.network.manager') FLAGS.network_size = 8 FLAGS.num_networks = 2 FLAGS.fake_network = True +FLAGS.image_service = 'nova.image.local.LocalImageService' flags.DECLARE('num_shelves', 'nova.volume.driver') flags.DECLARE('blades_per_shelf', 'nova.volume.driver') flags.DECLARE('iscsi_num_targets', 'nova.volume.driver') diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 061910013..7d7b91658 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -38,6 +38,8 @@ from nova import test from nova.auth import manager from nova.compute import power_state from nova.api.ec2 import cloud +from nova.api.ec2 import ec2utils +from nova.image import local from nova.objectstore import image @@ -76,6 +78,11 @@ class CloudTestCase(test.TestCase): project=self.project) host = self.network.get_network_host(self.context.elevated()) + def fake_image_show(meh, context, id): + return dict(kernelId=1, ramdiskId=1) + + self.stubs.Set(local.LocalImageService, 'show', fake_image_show) + def tearDown(self): network_ref = db.project_get_network(self.context, self.project.id) @@ -122,7 +129,7 @@ class CloudTestCase(test.TestCase): self.cloud.allocate_address(self.context) inst = db.instance_create(self.context, {'host': self.compute.host}) fixed = self.network.allocate_fixed_ip(self.context, inst['id']) - ec2_id = cloud.id_to_ec2_id(inst['id']) + ec2_id = ec2utils.id_to_ec2_id(inst['id']) self.cloud.associate_address(self.context, instance_id=ec2_id, public_ip=address) @@ -158,12 +165,12 @@ class CloudTestCase(test.TestCase): vol2 = db.volume_create(self.context, {}) result = self.cloud.describe_volumes(self.context) self.assertEqual(len(result['volumeSet']), 2) - volume_id = cloud.id_to_ec2_id(vol2['id'], 'vol-%08x') + volume_id = ec2utils.id_to_ec2_id(vol2['id'], 'vol-%08x') result = self.cloud.describe_volumes(self.context, volume_id=[volume_id]) self.assertEqual(len(result['volumeSet']), 1) self.assertEqual( - cloud.ec2_id_to_id(result['volumeSet'][0]['volumeId']), + ec2utils.ec2_id_to_id(result['volumeSet'][0]['volumeId']), vol2['id']) db.volume_destroy(self.context, vol1['id']) db.volume_destroy(self.context, vol2['id']) @@ -200,7 +207,7 @@ class CloudTestCase(test.TestCase): result = self.cloud.describe_instances(self.context) result = result['reservationSet'][0] self.assertEqual(len(result['instancesSet']), 2) - instance_id = cloud.id_to_ec2_id(inst2['id']) + instance_id = ec2utils.id_to_ec2_id(inst2['id']) result = self.cloud.describe_instances(self.context, instance_id=[instance_id]) result = result['reservationSet'][0] @@ -216,6 +223,7 @@ class CloudTestCase(test.TestCase): def test_console_output(self): image_id = FLAGS.default_image + print image_id instance_type = FLAGS.default_instance_type max_count = 1 kwargs = {'image_id': image_id, @@ -347,7 +355,7 @@ class CloudTestCase(test.TestCase): def test_update_of_instance_display_fields(self): inst = db.instance_create(self.context, {}) - ec2_id = cloud.id_to_ec2_id(inst['id']) + ec2_id = ec2utils.id_to_ec2_id(inst['id']) self.cloud.update_instance(self.context, ec2_id, display_name='c00l 1m4g3') inst = db.instance_get(self.context, inst['id']) @@ -365,7 +373,7 @@ class CloudTestCase(test.TestCase): def test_update_of_volume_display_fields(self): vol = db.volume_create(self.context, {}) self.cloud.update_volume(self.context, - cloud.id_to_ec2_id(vol['id'], 'vol-%08x'), + ec2utils.id_to_ec2_id(vol['id'], 'vol-%08x'), display_name='c00l v0lum3') vol = db.volume_get(self.context, vol['id']) self.assertEqual('c00l v0lum3', vol['display_name']) @@ -374,7 +382,7 @@ class CloudTestCase(test.TestCase): def test_update_of_volume_wont_update_private_fields(self): vol = db.volume_create(self.context, {}) self.cloud.update_volume(self.context, - cloud.id_to_ec2_id(vol['id'], 'vol-%08x'), + ec2utils.id_to_ec2_id(vol['id'], 'vol-%08x'), mountpoint='/not/here') vol = db.volume_get(self.context, vol['id']) self.assertEqual(None, vol['mountpoint']) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 949b5e6eb..1f49baaf6 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -31,7 +31,7 @@ from nova import test from nova import utils from nova.auth import manager from nova.compute import instance_types - +from nova.image import local LOG = logging.getLogger('nova.tests.compute') FLAGS = flags.FLAGS @@ -47,6 +47,11 @@ class ComputeTestCase(test.TestCase): network_manager='nova.network.manager.FlatManager') self.compute = utils.import_object(FLAGS.compute_manager) self.compute_api = compute.API() + + def fake_image_show(meh, context, id): + return dict(kernelId=1, ramdiskId=1) + + self.stubs.Set(local.LocalImageService, 'show', fake_image_show) self.manager = manager.AuthManager() self.user = self.manager.create_user('fake', 'fake', 'fake') self.project = self.manager.create_project('fake', 'fake', 'fake') diff --git a/nova/tests/test_direct.py b/nova/tests/test_direct.py index b6bfab534..b130e3f53 100644 --- a/nova/tests/test_direct.py +++ b/nova/tests/test_direct.py @@ -90,8 +90,7 @@ class DirectTestCase(test.TestCase): class DirectCloudTestCase(test_cloud.CloudTestCase): def setUp(self): super(DirectCloudTestCase, self).setUp() - compute_handle = compute.API(image_service=self.cloud.image_service, - network_api=self.cloud.network_api, + compute_handle = compute.API(network_api=self.cloud.network_api, volume_api=self.cloud.volume_api) direct.register_service('compute', compute_handle) self.router = direct.JsonParamsMiddleware(direct.Router()) diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 4ecb36b54..ca8abdb36 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -57,7 +57,7 @@ class QuotaTestCase(test.TestCase): def _create_instance(self, cores=2): """Create a test instance""" inst = {} - inst['image_id'] = 'ami-test' + inst['image_id'] = 'ami-1' inst['reservation_id'] = 'r-fakeres' inst['user_id'] = self.user.id inst['project_id'] = self.project.id @@ -123,7 +123,7 @@ class QuotaTestCase(test.TestCase): min_count=1, max_count=1, instance_type='m1.small', - image_id='fake') + image_id='ami-1') for instance_id in instance_ids: db.instance_destroy(self.context, instance_id) @@ -136,7 +136,7 @@ class QuotaTestCase(test.TestCase): min_count=1, max_count=1, instance_type='m1.small', - image_id='fake') + image_id='ami-1') for instance_id in instance_ids: db.instance_destroy(self.context, instance_id) -- cgit From bc94ec23100de9f07e04b0348823d4f103a9daa5 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Fri, 4 Mar 2011 02:49:12 +0000 Subject: use LocalImageServiceByDefault --- nova/flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/flags.py b/nova/flags.py index f01a4d096..cb47ca8d1 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -346,7 +346,7 @@ DEFINE_string('scheduler_manager', 'nova.scheduler.manager.SchedulerManager', 'Manager for scheduler') # The service to use for image search and retrieval -DEFINE_string('image_service', 'nova.image.glance.GlanceImageService', +DEFINE_string('image_service', 'nova.image.local.LocalImageService', 'The service to use for retrieving and searching for images.') DEFINE_string('host', socket.gethostname(), -- cgit From 13307e02258a5a08bedb1ed933a107668aac6457 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Fri, 4 Mar 2011 05:04:49 +0000 Subject: make local image service work --- nova/api/ec2/cloud.py | 2 +- nova/image/local.py | 64 ++++++++++++++++++++++++++++++++--------------- nova/objectstore/image.py | 3 +-- 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 8c2e77d86..aa1dcbe33 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -853,7 +853,7 @@ class CloudController(object): i['imageLocation'] = image['properties'].get('image_location') i['imageState'] = image['properties'].get('image_state') i['type'] = image.get('type') - i['isPublic'] = image['properties'].get('is_public') == 'True' + i['isPublic'] = str(image['properties'].get('is_public', '')) == 'True' i['architecture'] = image['properties'].get('architecture') return i diff --git a/nova/image/local.py b/nova/image/local.py index f78b9aa89..b4616729a 100644 --- a/nova/image/local.py +++ b/nova/image/local.py @@ -15,57 +15,81 @@ # License for the specific language governing permissions and limitations # under the License. -import cPickle as pickle +import json import os.path import random -import tempfile +import shutil +from nova import flags from nova import exception from nova.image import service -class LocalImageService(service.BaseImageService): +FLAGS = flags.FLAGS +flags.DEFINE_string('images_path', '$state_path/images', + 'path to decrypted images') +class LocalImageService(service.BaseImageService): """Image service storing images to local disk. + It assumes that image_ids are integers. """ def __init__(self): - self._path = tempfile.mkdtemp() + self._path = FLAGS.images_path - def _path_to(self, image_id): + def _path_to(self, image_id, fname='info.json'): + if fname: + return os.path.join(self._path, str(image_id), fname) return os.path.join(self._path, str(image_id)) def _ids(self): """The list of all image ids.""" - return [int(i) for i in os.listdir(self._path)] + return [int(i) for i in os.listdir(self._path) + if unicode(i).isnumeric()] def index(self, context): - return [dict(id=i['id'], name=i['name']) for i in self.detail(context)] + return [dict(image_id=i['id'], name=i.get('name')) + for i in self.detail(context)] def detail(self, context): - return [self.show(context, id) for id in self._ids()] - - def show(self, context, id): + images = [] + for image_id in self._ids(): + try: + image = self.show(context, image_id) + images.append(image) + except exception.NotFound: + continue + return images + + def show(self, context, image_id): try: - return pickle.load(open(self._path_to(id))) + with open(self._path_to(image_id)) as metadata_file: + return json.load(metadata_file) except IOError: raise exception.NotFound - def create(self, context, data): + def create(self, context, metadata, data=None): """Store the image data and return the new image id.""" - id = random.randint(0, 2 ** 31 - 1) - data['id'] = id - self.update(context, id, data) - return id + image_id = random.randint(0, 2 ** 31 - 1) + image_path = self._path_to(image_id, None) + if not os.path.exists(image_path): + os.mkdir(image_path) + return self.update(context, image_id, metadata, data) - def update(self, context, image_id, data): + def update(self, context, image_id, metadata, data=None): """Replace the contents of the given image with the new data.""" + metadata['id'] = image_id try: - pickle.dump(data, open(self._path_to(image_id), 'w')) + with open(self._path_to(image_id), 'w') as metadata_file: + json.dump(metadata, metadata_file) + if data: + with open(self._path_to(image_id, 'image'), 'w') as image_file: + shutil.copyfileobj(data, image_file) except IOError: raise exception.NotFound + return metadata def delete(self, context, image_id): """Delete the given image. @@ -79,8 +103,8 @@ class LocalImageService(service.BaseImageService): def delete_all(self): """Clears out all images in local directory.""" - for id in self._ids(): - os.unlink(self._path_to(id)) + for image_id in self._ids(): + os.unlink(self._path_to(image_id)) def delete_imagedir(self): """Deletes the local directory. diff --git a/nova/objectstore/image.py b/nova/objectstore/image.py index 27227e2ca..8013cbd9c 100644 --- a/nova/objectstore/image.py +++ b/nova/objectstore/image.py @@ -37,8 +37,7 @@ from nova.objectstore import bucket FLAGS = flags.FLAGS -flags.DEFINE_string('images_path', '$state_path/images', - 'path to decrypted images') +flags.DECLARE('images_path', 'nova.image.local') class Image(object): -- cgit From cf9bc248f0fc318c4a9fb5087f257216312e39d1 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Fri, 4 Mar 2011 05:21:28 +0000 Subject: fix a couple issues with local, update the glance fake to actually return the same types as the real client, fix the image tests --- nova/image/local.py | 12 +++--------- nova/tests/api/openstack/fakes.py | 3 ++- nova/tests/api/openstack/test_images.py | 15 +++++++++------ 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/nova/image/local.py b/nova/image/local.py index b4616729a..6fa648b6b 100644 --- a/nova/image/local.py +++ b/nova/image/local.py @@ -29,6 +29,7 @@ FLAGS = flags.FLAGS flags.DEFINE_string('images_path', '$state_path/images', 'path to decrypted images') + class LocalImageService(service.BaseImageService): """Image service storing images to local disk. @@ -97,18 +98,11 @@ class LocalImageService(service.BaseImageService): """ try: - os.unlink(self._path_to(image_id)) + shutil.rmtree(self._path_to(image_id, None)) except IOError: raise exception.NotFound def delete_all(self): """Clears out all images in local directory.""" for image_id in self._ids(): - os.unlink(self._path_to(image_id)) - - def delete_imagedir(self): - """Deletes the local directory. - Raises OSError if directory is not empty. - - """ - os.rmdir(self._path) + shutil.rmtree(self._path_to(image_id, None)) diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 7b016db08..2c4e57246 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -156,7 +156,7 @@ def stub_out_glance(stubs, initial_fixtures=None): id = ''.join(random.choice(string.letters) for _ in range(20)) image_meta['id'] = id self.fixtures.append(image_meta) - return id + return image_meta def fake_update_image(self, image_id, image_meta, data=None): f = self.fake_get_image_meta(image_id) @@ -164,6 +164,7 @@ def stub_out_glance(stubs, initial_fixtures=None): raise glance_exc.NotFound f.update(image_meta) + return f def fake_delete_image(self, image_id): f = self.fake_get_image_meta(image_id) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index e232bc3d5..eb5039bdb 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -22,6 +22,8 @@ and as a WSGI layer import json import datetime +import shutil +import tempfile import stubout import webob @@ -54,7 +56,7 @@ class BaseImageServiceTests(object): num_images = len(self.service.index(self.context)) - id = self.service.create(self.context, fixture) + id = self.service.create(self.context, fixture)['id'] self.assertNotEquals(None, id) self.assertEquals(num_images + 1, @@ -71,7 +73,7 @@ class BaseImageServiceTests(object): num_images = len(self.service.index(self.context)) - id = self.service.create(self.context, fixture) + id = self.service.create(self.context, fixture)['id'] self.assertNotEquals(None, id) @@ -89,7 +91,7 @@ class BaseImageServiceTests(object): 'instance_id': None, 'progress': None} - id = self.service.create(self.context, fixture) + id = self.service.create(self.context, fixture)['id'] fixture['status'] = 'in progress' @@ -118,7 +120,7 @@ class BaseImageServiceTests(object): ids = [] for fixture in fixtures: - new_id = self.service.create(self.context, fixture) + new_id = self.service.create(self.context, fixture)['id'] ids.append(new_id) num_images = len(self.service.index(self.context)) @@ -137,14 +139,15 @@ class LocalImageServiceTest(test.TestCase, def setUp(self): super(LocalImageServiceTest, self).setUp() + self.tempdir = tempfile.mkdtemp() + self.flags(images_path=self.tempdir) self.stubs = stubout.StubOutForTesting() service_class = 'nova.image.local.LocalImageService' self.service = utils.import_object(service_class) self.context = context.RequestContext(None, None) def tearDown(self): - self.service.delete_all() - self.service.delete_imagedir() + shutil.rmtree(self.tempdir) self.stubs.UnsetAll() super(LocalImageServiceTest, self).tearDown() -- cgit From e2c95e198f1982bc50bc95bc61ef3211b17937a2 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Fri, 4 Mar 2011 05:55:41 +0000 Subject: spawn a greenthread for image registration because it is slow --- nova/image/s3.py | 67 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/nova/image/s3.py b/nova/image/s3.py index a740b010c..e9542c7bd 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -22,6 +22,7 @@ objectstore service. """ import binascii +import eventlet import os import shutil import tarfile @@ -188,46 +189,50 @@ class S3ImageService(service.BaseImageService): image = self.service.create(context, metadata) image_id = image['id'] - parts = [] - for fn_element in manifest.find("image").getiterator("filename"): - part = self._download_file(bucket, fn_element.text, image_path) - parts.append(part) + def delayed_create(): + parts = [] + for fn_element in manifest.find("image").getiterator("filename"): + part = self._download_file(bucket, fn_element.text, image_path) + parts.append(part) - # NOTE(vish): this may be suboptimal, should we use cat? - encrypted_filename = os.path.join(image_path, 'image.encrypted') - with open(encrypted_filename, 'w') as combined: - for filename in parts: - with open(filename) as part: - shutil.copyfileobj(part, combined) + # NOTE(vish): this may be suboptimal, should we use cat? + encrypted_filename = os.path.join(image_path, 'image.encrypted') + with open(encrypted_filename, 'w') as combined: + for filename in parts: + with open(filename) as part: + shutil.copyfileobj(part, combined) - metadata['properties']['image_state'] = 'decrypting' - self.service.update(context, image_id, metadata) + metadata['properties']['image_state'] = 'decrypting' + self.service.update(context, image_id, metadata) - hex_key = manifest.find("image/ec2_encrypted_key").text - encrypted_key = binascii.a2b_hex(hex_key) - hex_iv = manifest.find("image/ec2_encrypted_iv").text - encrypted_iv = binascii.a2b_hex(hex_iv) + hex_key = manifest.find("image/ec2_encrypted_key").text + encrypted_key = binascii.a2b_hex(hex_key) + hex_iv = manifest.find("image/ec2_encrypted_iv").text + encrypted_iv = binascii.a2b_hex(hex_iv) - # FIXME(vish): grab key from common service so this can run on - # any host. - cloud_private_key = os.path.join(FLAGS.ca_path, "private/cakey.pem") + # FIXME(vish): grab key from common service so this can run on + # any host. + cloud_pk = os.path.join(FLAGS.ca_path, "private/cakey.pem") - decrypted_filename = os.path.join(image_path, 'image.tar.gz') - self._decrypt_image(encrypted_filename, encrypted_key, encrypted_iv, - cloud_private_key, decrypted_filename) + decrypted_filename = os.path.join(image_path, 'image.tar.gz') + self._decrypt_image(encrypted_filename, encrypted_key, + encrypted_iv, cloud_pk, decrypted_filename) - metadata['properties']['image_state'] = 'untarring' - self.service.update(context, image_id, metadata) + metadata['properties']['image_state'] = 'untarring' + self.service.update(context, image_id, metadata) - unz_filename = self._untarzip_image(image_path, decrypted_filename) + unz_filename = self._untarzip_image(image_path, decrypted_filename) - metadata['properties']['image_state'] = 'uploading' - with open(unz_filename) as image_file: - self.service.update(context, image_id, metadata, image_file) - metadata['properties']['image_state'] = 'available' - self.service.update(context, image_id, metadata) + metadata['properties']['image_state'] = 'uploading' + with open(unz_filename) as image_file: + self.service.update(context, image_id, metadata, image_file) + metadata['properties']['image_state'] = 'available' + self.service.update(context, image_id, metadata) + + shutil.rmtree(image_path) + + eventlet.spawn_n(delayed_create) - shutil.rmtree(image_path) return image_id, metadata @staticmethod -- cgit From 517a571f8905c32efd45f7b3410fb263ad705545 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Fri, 4 Mar 2011 05:58:49 +0000 Subject: add the ec2utils file i forgot --- nova/api/ec2/ec2utils.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 nova/api/ec2/ec2utils.py diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py new file mode 100644 index 000000000..0ea22c0e6 --- /dev/null +++ b/nova/api/ec2/ec2utils.py @@ -0,0 +1,27 @@ +# 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. + + +def ec2_id_to_id(ec2_id): + """Convert an ec2 ID (i-[base 16 number]) to an instance id (int)""" + return int(ec2_id.split('-')[-1], 16) + + +def id_to_ec2_id(instance_id, template='i-%08x'): + """Convert an instance ID (int) to an ec2 ID (i-[base 16 number])""" + return template % instance_id -- cgit From 1d8914fc752f7182f942cdd40f2ba18baedeed0c Mon Sep 17 00:00:00 2001 From: Cerberus <matt.dietz@rackspace.com> Date: Fri, 4 Mar 2011 11:19:35 -0600 Subject: More fixes --- nova/api/openstack/servers.py | 4 ++-- nova/compute/api.py | 8 ++++---- nova/compute/manager.py | 2 +- nova/tests/xenapi/stubs.py | 2 +- nova/virt/xenapi/vm_utils.py | 16 ++++++++-------- nova/virt/xenapi/vmops.py | 11 ++++++----- 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index ceb17c9e4..c2bf42b72 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -203,8 +203,8 @@ class Controller(wsgi.Controller): return exc.HTTPNoContent() def action(self, req, id): - """ Multi-purpose method used to reboot, rebuild, or - resize a server """ + """Multi-purpose method used to reboot, rebuild, or + resize a server""" actions = { 'reboot': self._action_reboot, diff --git a/nova/compute/api.py b/nova/compute/api.py index bfa5c7dba..ce9da727d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -430,8 +430,8 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.NotFound(_("No finished migrations found for \ - instance")) + raise exception.NotFound(_("No finished migrations found for " + "instance")) params = {'migration_id': migration_ref['id']} self._cast_compute_message('revert_resize', context, instance_id, @@ -444,8 +444,8 @@ class API(base.Base): migration_ref = self.db.migration_get_by_instance_and_status(context, instance_id, 'finished') if not migration_ref: - raise exception.NotFound(_("No finished migrations found for \ - instance")) + raise exception.NotFound(_("No finished migrations found for " + "instance")) instance_ref = self.db.instance_get(context, instance_id) params = {'migration_id': migration_ref['id']} self._cast_compute_message('confirm_resize', context, instance_id, diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 1c42b383c..b3e864154 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -414,7 +414,7 @@ class ComputeManager(manager.Manager): @exception.wrap_exception @checks_instance_lock def confirm_resize(self, context, instance_id, migration_id): - """ Destroys the source instance """ + """Destroys the source instance""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) migration_ref = self.db.migration_get(context, migration_id) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index d17951b81..caefcff34 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -221,7 +221,7 @@ class FakeSessionForVolumeFailedTests(FakeSessionForVolumeTests): class FakeSessionForMigrationTests(fake.SessionBase): - """ Stubs out a XenAPISession for Migration tests """ + """Stubs out a XenAPISession for Migration tests""" def __init__(self, uri): super(FakeSessionForMigrationTests, self).__init__(uri) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index ca2d634f1..eff207a51 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -270,8 +270,8 @@ class VMHelper(HelperBase): @classmethod def create_snapshot(cls, session, instance_id, vm_ref, label): - """ Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, - Snapshot VHD """ + """Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, + Snapshot VHD""" #TODO(sirp): Add quiesce and VSS locking support when Windows support # is added LOG.debug(_("Snapshotting VM %(vm_ref)s with label '%(label)s'...") @@ -284,7 +284,7 @@ class VMHelper(HelperBase): original_parent_uuid = get_vhd_parent_uuid(session, vm_vdi_ref) task = session.call_xenapi('Async.VM.snapshot', vm_ref, label) - template_vm_ref = session.wait_for_task(instance_id, task) + template_vm_ref = session.wait_for_task(task, instance_id) template_vdi_rec = cls.get_vdi_for_vm_safely(session, template_vm_ref)[1] template_vdi_uuid = template_vdi_rec["uuid"] @@ -302,14 +302,14 @@ class VMHelper(HelperBase): @classmethod def get_sr(cls, session, sr_label='slices'): - """ Finds the SR named by the given name label and returns - the UUID """ + """Finds the SR named by the given name label and returns + the UUID""" return session.call_xenapi('SR.get_by_name_label', sr_label)[0] @classmethod def get_sr_path(cls, session, sr_label='slices'): - """ Finds the SR and then coerces it into a path on the dom0 file - system """ + """Finds the SR and then coerces it into a path on the dom0 file + system""" return FLAGS.xenapi_sr_base_path + cls.get_sr(session, sr_label) @classmethod @@ -643,7 +643,7 @@ class VMHelper(HelperBase): if sr_ref: LOG.debug(_("Re-scanning SR %s"), sr_ref) task = session.call_xenapi('Async.SR.scan', sr_ref) - session.wait_for_task(instance_id, task) + session.wait_for_task(task, instance_id) @classmethod def scan_default_sr(cls, session): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 60ce51f4a..01bfa2dc5 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -233,7 +233,7 @@ class VMOps(object): "start") def snapshot(self, instance, image_id): - """ Create snapshot from a running VM instance + """Create snapshot from a running VM instance :param instance: instance to be snapshotted :param image_id: id of image to upload to @@ -285,7 +285,7 @@ class VMOps(object): return def migrate_disk_and_power_off(self, instance, dest): - """ Copies a VHD from one host machine to another + """Copies a VHD from one host machine to another :param instance: the instance that owns the VHD in question :param dest: the destination host machine @@ -314,7 +314,7 @@ class VMOps(object): task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) # Now power down the instance and transfer the COW VHD self._shutdown(instance, vm_ref, method='clean') @@ -326,7 +326,7 @@ class VMOps(object): task = self._session.async_call_plugin('migration', 'transfer_vhd', {'params': pickle.dumps(params)}) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) finally: if template_vm_ref: @@ -338,6 +338,7 @@ class VMOps(object): return {'base_copy': base_copy_uuid, 'cow': cow_uuid} def attach_disk(self, instance, disk_info): + """Links the base copy VHD to the COW via the XAPI plugin""" vm_ref = VMHelper.lookup(self._session, instance.name) new_base_copy_uuid = str(uuid.uuid4()) new_cow_uuid = str(uuid.uuid4()) @@ -350,7 +351,7 @@ class VMOps(object): task = self._session.async_call_plugin('migration', 'move_vhds_into_sr', {'params': pickle.dumps(params)}) - self._session.wait_for_task(instance.id, task) + self._session.wait_for_task(task, instance.id) # Now we rescan the SR so we find the VHDs VMHelper.scan_default_sr(self._session) -- cgit From 831f398653cc99253bfeeb232165d3f9c043bd0b Mon Sep 17 00:00:00 2001 From: Todd Willey <todd@ansolabs.com> Date: Fri, 4 Mar 2011 14:01:25 -0500 Subject: Fix renaming of instance fields using update_instance method. --- nova/api/ec2/apirequest.py | 18 +++++++++++++++++- nova/api/ec2/cloud.py | 4 ++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py index 2b1acba5a..d7ad08d2f 100644 --- a/nova/api/ec2/apirequest.py +++ b/nova/api/ec2/apirequest.py @@ -52,7 +52,23 @@ def _database_to_isoformat(datetimeobj): def _try_convert(value): - """Return a non-string if possible""" + """Return a non-string from a string or unicode, if possible. + + ============= ===================================================== + When value is returns + ============= ===================================================== + zero-length '' + 'None' None + 'True' True + 'False' False + '0', '-0' 0 + 0xN, -0xN int from hex (postitive) (N is any number) + 0bN, -0bN int from binary (positive) (N is any number) + * try conversion to int, float, complex, fallback value + + """ + if len(value) == 0: + return '' if value == 'None': return None if value == 'True': diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index c6309f03c..0d22a3f46 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -838,14 +838,14 @@ class CloudController(object): self.compute_api.unrescue(context, instance_id=instance_id) return True - def update_instance(self, context, ec2_id, **kwargs): + def update_instance(self, context, instance_id, **kwargs): updatable_fields = ['display_name', 'display_description'] changes = {} for field in updatable_fields: if field in kwargs: changes[field] = kwargs[field] if changes: - instance_id = ec2_id_to_id(ec2_id) + instance_id = ec2_id_to_id(instance_id) self.compute_api.update(context, instance_id=instance_id, **kwargs) return True -- cgit From f3c1c99ca0f6f3164430b33f46772ef8bdc87b70 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Fri, 4 Mar 2011 19:54:19 +0000 Subject: move the id wrapping into cloud layer instead of image_service --- nova/api/ec2/cloud.py | 33 ++++++++++++++++++++++++--------- nova/image/s3.py | 28 +++++----------------------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index aa1dcbe33..3ea3fa07e 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -843,10 +843,19 @@ class CloudController(object): self.compute_api.update(context, instance_id=instance_id, **kwargs) return True + _type_prefix_map = {'machine': 'ami', + 'kernel': 'aki', + 'ramdisk': 'ari'} + + def _image_ec2_id(self, image_id, image_type): + prefix = self._type_prefix_map[image_type] + template = prefix + '-%08x' + return ec2utils.id_to_ec2_id(int(image_id), template=template) + def _format_image(self, image): """Convert from format defined by BaseImageService to S3 format.""" i = {} - i['imageId'] = image.get('id') + i['imageId'] = self._image_ec2_id(image.get('id'), image.get('type')) i['kernelId'] = image['properties'].get('kernel_id') i['ramdiskId'] = image['properties'].get('ramdisk_id') i['imageOwnerId'] = image['properties'].get('owner_id') @@ -863,7 +872,8 @@ class CloudController(object): images = [] for ec2_id in image_id: try: - image = self.image_service.show(context, ec2_id) + internal_id = ec2utils.ec2_id_to_id(ec2_id) + image = self.image_service.show(context, internal_id) except exception.NotFound: raise exception.NotFound(_('Image %s not found') % ec2_id) @@ -875,14 +885,16 @@ class CloudController(object): def deregister_image(self, context, image_id, **kwargs): LOG.audit(_("De-registering image %s"), image_id, context=context) - self.image_service.delete(context, image_id) + internal_id = ec2utils.ec2_id_to_id(image_id) + self.image_service.delete(context, internal_id) return {'imageId': image_id} def register_image(self, context, image_location=None, **kwargs): if image_location is None and 'name' in kwargs: image_location = kwargs['name'] - image = {"image_location": image_location} - image_id = self.image_service.create(context, image) + metadata = {"image_location": image_location} + image = self.image_service.create(context, metadata) + image_id = self._image_ec2_id(image['id'], image['type']) msg = _("Registered image %(image_location)s with" " id %(image_id)s") % locals() LOG.audit(msg, context=context) @@ -893,8 +905,9 @@ class CloudController(object): raise exception.ApiError(_('attribute not supported: %s') % attribute) try: + internal_id = ec2utils.ec2_id_to_id(image_id) image = self._format_image(self.image_service.show(context, - image_id)) + internal_id)) except (IndexError, exception.NotFound): raise exception.NotFound(_('Image %s not found') % image_id) result = {'image_id': image_id, 'launchPermission': []} @@ -917,13 +930,15 @@ class CloudController(object): LOG.audit(_("Updating image %s publicity"), image_id, context=context) try: - metadata = self.image_service.show(context, image_id) + internal_id = ec2utils.ec2_id_to_id(image_id) + metadata = self.image_service.show(context, internal_id) except exception.NotFound: raise exception.NotFound(_('Image %s not found') % image_id) del(metadata['id']) metadata['properties']['is_public'] = (operation_type == 'add') - return self.image_service.update(context, image_id, metadata) + return self.image_service.update(context, internal_id, metadata) def update_image(self, context, image_id, **kwargs): - result = self.image_service.update(context, image_id, dict(kwargs)) + internal_id = ec2utils.ec2_id_to_id(image_id) + result = self.image_service.update(context, internal_id, dict(kwargs)) return result diff --git a/nova/image/s3.py b/nova/image/s3.py index e9542c7bd..c7446f4b0 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -36,7 +36,6 @@ from nova import flags from nova import utils from nova.auth import manager from nova.image import service -from nova.api.ec2 import ec2utils FLAGS = flags.FLAGS @@ -44,17 +43,6 @@ flags.DEFINE_string('image_decryption_dir', '/tmp', 'parent dir for tempdir used for image decryption') -_type_prefix_map = {'machine': 'ami', - 'kernel': 'aki', - 'ramdisk': 'ari'} - - -def image_ec2_id(image_id, image_type): - prefix = _type_prefix_map[image_type] - template = prefix + '-%08x' - return ec2utils.id_to_ec2_id(int(image_id), template=template) - - class S3ImageService(service.BaseImageService): def __init__(self, service=None, *args, **kwargs): if service == None: @@ -62,23 +50,20 @@ class S3ImageService(service.BaseImageService): self.service = service self.service.__init__(*args, **kwargs) - def create(self, context, properties, data=None): - """image should contain image_location""" - image_id, metadata = self._s3_create(context, properties) - return image_ec2_id(image_id, metadata['type']) + def create(self, context, metadata, data=None): + """metadata should contain image_location""" + image = self._s3_create(context, metadata) + return image def delete(self, context, image_id): # FIXME(vish): call to show is to check filter self.show(context, image_id) - image_id = ec2utils.ec2_id_to_id(image_id) self.service.delete(context, image_id) def update(self, context, image_id, metadata, data=None): # FIXME(vish): call to show is to check filter self.show(context, image_id) - image_id = ec2utils.ec2_id_to_id(image_id) image = self.service.update(context, image_id, metadata, data) - image['id'] = image_ec2_id(image['id'], image['type']) return image def index(self, context): @@ -103,16 +88,13 @@ class S3ImageService(service.BaseImageService): for image in images: if not S3ImageService._is_visible(context, image): continue - image['id'] = image_ec2_id(image['id'], image['type']) filtered.append(image) return filtered def show(self, context, image_id): - image_id = ec2utils.ec2_id_to_id(image_id) image = self.service.show(context, image_id) if not self._is_visible(context, image): raise exception.NotFound - image['id'] = image_ec2_id(image['id'], image['type']) return image @staticmethod @@ -233,7 +215,7 @@ class S3ImageService(service.BaseImageService): eventlet.spawn_n(delayed_create) - return image_id, metadata + return image @staticmethod def _decrypt_image(encrypted_filename, encrypted_key, encrypted_iv, -- cgit From 1eed366b7508c0f225b2c9691e1f62a6f88ee3f8 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz <emaildericky@gmail.com> Date: Fri, 4 Mar 2011 21:07:03 +0100 Subject: Added initial support to delete networks nova-manage --- bin/nova-manage | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index 9bf3a1bb3..9557f2423 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -546,6 +546,16 @@ class NetworkCommands(object): network.dns) + def delete(self, fixed_range): + """Deletes a network""" + try: + network = [n for n in db.network_get_all(context.get_admin_context()) + if n.cidr == fixed_range][0] + + print network.id, network.cidr, network.project_id + except IndexError: + raise ValueError(_("Network does not exist")) + class ServiceCommands(object): """Enable and disable running services""" -- cgit From a5bee00af4d6ec3eed6ed0abd866948f4510f041 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 7 Mar 2011 01:25:01 +0000 Subject: make compute get the new images properly, fix a bunch of tests, and provide conversion commands --- bin/nova-manage | 131 ++++++++++++++++++++++++++++++++++++++++++- nova/api/ec2/cloud.py | 74 +++++++++++++++--------- nova/api/ec2/ec2utils.py | 6 +- nova/compute/api.py | 4 +- nova/image/glance.py | 35 ++++++++++-- nova/image/local.py | 52 +++++++++++++---- nova/image/s3.py | 33 +++++++---- nova/image/service.py | 18 ++++-- nova/tests/test_cloud.py | 16 +++--- nova/tests/test_compute.py | 12 ++-- nova/tests/test_console.py | 2 +- nova/tests/test_quota.py | 32 ++++++----- nova/tests/test_scheduler.py | 4 +- nova/tests/test_volume.py | 2 +- nova/virt/images.py | 25 +++++---- nova/virt/libvirt_conn.py | 8 ++- 16 files changed, 345 insertions(+), 109 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 0f7604aeb..ebeda05a0 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -55,6 +55,8 @@ import datetime import gettext +import glob +import json import os import re import sys @@ -81,7 +83,7 @@ from nova import log as logging from nova import quota from nova import rpc from nova import utils -from nova.api.ec2.ec2utils import ec2_id_to_id +from nova.api.ec2 import ec2utils from nova.auth import manager from nova.cloudpipe import pipelib from nova.compute import instance_types @@ -104,7 +106,7 @@ def param2id(object_id): args: [object_id], e.g. 'vol-0000000a' or 'volume-0000000a' or '10' """ if '-' in object_id: - return ec2_id_to_id(object_id) + return ec2utils.ec2_id_to_id(object_id) else: return int(object_id) @@ -735,6 +737,130 @@ class InstanceTypeCommands(object): self._print_instance_types(name, inst_types) +class ImageCommands(object): + """Methods for dealing with a cloud in an odd state""" + + def __init__(self, *args, **kwargs): + self.image_service = utils.import_object(FLAGS.image_service) + + def _register(self, image_type, path, owner, name=None, + is_public='T', architecture='x86_64', + kernel_id=None, ramdisk_id=None): + meta = {'type': image_type, + 'is_public': True, + 'name': name, + 'properties': {'image_state': 'available', + 'owner': owner, + 'architecture': architecture, + 'image_location': 'local', + 'is_public': (is_public == 'T')}} + if kernel_id: + meta['properties']['kernel_id'] = int(kernel_id) + if ramdisk_id: + meta['properties']['ramdisk_id'] = int(ramdisk_id) + elevated = context.get_admin_context() + try: + with open(path) as ifile: + image = self.image_service.create(elevated, meta, ifile) + new = image['id'] + print _("Image registered to %(new)s (%(new)08x).") % locals() + return new + except Exception as exc: + print _("Failed to register %(path)s: %(exc)s") % locals() + + def register_all(self, image, kernel, ramdisk, owner, name=None, + is_public='T', architecture='x86_64'): + """Uploads an image, kernel, and ramdisk into the image_service + arguments: image kernel ramdisk owner [name] [is_public='T'] + [architecture='x86_64']""" + kernel_id = self._register('kernel', kernel, owner, None, + is_public, architecture) + ramdisk_id = self._register('ramdisk', ramdisk, owner, None, + is_public, architecture) + self._register('machine', image, owner, name, is_public, + architecture, kernel_id, ramdisk_id) + + def image_register(self, path, owner, name=None, is_public='T', + architecture='x86_64', kernel_id=None, ramdisk_id=None): + """Uploads an image into the image_service + arguments: path owner [name] [is_public='T'] [architecture='x86_64'] + [kernel_id] [ramdisk_id]""" + self._register('machine', path, owner, name, is_public, + architecture, kernel_id, ramdisk_id) + + def kernel_register(self, path, owner, name=None, is_public='T', + architecture='x86_64'): + """Uploads a kernel into the image_service + arguments: path owner [name] [is_public='T'] [architecture='x86_64'] + """ + self._register('kernel', path, owner, name, is_public, architecture) + + def ramdisk_register(self, path, owner, name=None, is_public='T', + architecture='x86_64'): + """Uploads a ramdisk into the image_service + arguments: path owner [name] [is_public='T'] [architecture='x86_64'] + """ + self._register('ramdisk', path, owner, name, is_public, architecture) + + def _lookup(self, old_image_id): + try: + internal_id = ec2utils.ec2_id_to_id(old_image_id) + image = self.image_service.show(context, internal_id) + except exception.NotFound: + image = self.image_service.show_by_name(context, old_image_id) + return image['id'] + + def _old_to_new(self, old): + new = {'type': old['type'], + 'is_public': True, + 'name': old['imageId'], + 'properties': {'image_state': old['imageState'], + 'owner': old['imageOwnerId'], + 'architecture': old['architecture'], + 'image_location': old['imageLocation'], + 'is_public': old['isPublic']}} + if old.get('kernelId'): + new['properties']['kernel_id'] = self._lookup(old['kernelId']) + if old.get('ramdiskId'): + new['properties']['ramdisk_id'] = self._lookup(old['ramdiskId']) + return new + + def _convert_images(self, images): + elevated = context.get_admin_context() + for image_path, image_metadata in images.iteritems(): + meta = self._old_to_new(image_metadata) + old = meta['name'] + try: + with open(image_path) as ifile: + image = self.image_service.create(elevated, meta, ifile) + new = image['id'] + print _("Image %(old)s converted to " \ + "%(new)s (%(new)08x).") % locals() + except Exception as exc: + print _("Failed to convert %(old)s: %(exc)s") % locals() + + + def convert(self, directory): + """Uploads old objectstore images in directory to new service + arguments: directory""" + machine_images = {} + other_images = {} + for fn in glob.glob("%s/*/info.json" % directory): + try: + image_path = os.path.join(fn.rpartition('/')[0], 'image') + with open(fn) as metadata_file: + image_metadata = json.load(metadata_file) + if image_metadata['type'] == 'machine': + machine_images[image_path] = image_metadata + else: + other_images[image_path] = image_metadata + except Exception as exc: + print _("Failed to load %(fn)s.") % locals() + # NOTE(vish): do kernels and ramdisks first so images + self._convert_images(other_images) + self._convert_images(machine_images) + + CATEGORIES = [ ('user', UserCommands), ('project', ProjectCommands), @@ -749,6 +875,7 @@ CATEGORIES = [ ('db', DbCommands), ('volume', VolumeCommands), ('instance_type', InstanceTypeCommands), + ('image', ImageCommands), ('flavor', InstanceTypeCommands)] diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 3ea3fa07e..496e944fe 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -146,10 +146,13 @@ class CloudController(object): floating_ip = db.instance_get_floating_address(ctxt, instance_ref['id']) ec2_id = ec2utils.id_to_ec2_id(instance_ref['id']) + image_ec2_id = self._image_ec2_id(instance_ref['image_id'], 'machine') + k_ec2_id = self._image_ec2_id(instance_ref['kernel_id'], 'kernel') + r_ec2_id = self._image_ec2_id(instance_ref['ramdisk_id'], 'ramdisk') data = { 'user-data': base64.b64decode(instance_ref['user_data']), 'meta-data': { - 'ami-id': instance_ref['image_id'], + 'ami-id': image_ec2_id, 'ami-launch-index': instance_ref['launch_index'], 'ami-manifest-path': 'FIXME', 'block-device-mapping': { @@ -164,12 +167,12 @@ class CloudController(object): 'instance-type': instance_ref['instance_type'], 'local-hostname': hostname, 'local-ipv4': address, - 'kernel-id': instance_ref['kernel_id'], + 'kernel-id': k_ec2_id, + 'ramdisk-id': r_ec2_id, 'placement': {'availability-zone': availability_zone}, 'public-hostname': hostname, 'public-ipv4': floating_ip or '', 'public-keys': keys, - 'ramdisk-id': instance_ref['ramdisk_id'], 'reservation-id': instance_ref['reservation_id'], 'security-groups': '', 'mpi': mpi}} @@ -679,7 +682,7 @@ class CloudController(object): instance_id = instance['id'] ec2_id = ec2utils.id_to_ec2_id(instance_id) i['instanceId'] = ec2_id - i['imageId'] = instance['image_id'] + i['imageId'] = self._image_ec2_id(instance['image_id']) i['instanceState'] = { 'code': instance['state'], 'name': instance['state_description']} @@ -782,13 +785,15 @@ class CloudController(object): def run_instances(self, context, **kwargs): max_count = int(kwargs.get('max_count', 1)) if kwargs.get('kernel_id'): - kwargs['kernel_id'] = ec2utils.ec2_id_to_id(kwargs['kernel_id']) + kernel = self._get_image(context, kwargs['kernel_id']) + kwargs['kernel_id'] = kernel['id'] if kwargs.get('ramdisk_id'): - kwargs['ramdisk_id'] = ec2utils.ec2_id_to_id(kwargs['ramdisk_id']) + ramdisk = self._get_image(context, kwargs['ramdisk_id']) + kwargs['ramdisk_id'] = ramdisk['id'] instances = self.compute_api.create(context, instance_type=instance_types.get_by_type( kwargs.get('instance_type', None)), - image_id=ec2utils.ec2_id_to_id(kwargs['image_id']), + image_id=self._get_image(context, kwargs['image_id'])['id'], min_count=int(kwargs.get('min_count', max_count)), max_count=max_count, kernel_id=kwargs.get('kernel_id'), @@ -847,17 +852,34 @@ class CloudController(object): 'kernel': 'aki', 'ramdisk': 'ari'} - def _image_ec2_id(self, image_id, image_type): + def _image_ec2_id(self, image_id, image_type='machine'): prefix = self._type_prefix_map[image_type] template = prefix + '-%08x' return ec2utils.id_to_ec2_id(int(image_id), template=template) + def _get_image(self, context, ec2_id): + try: + internal_id = ec2utils.ec2_id_to_id(ec2_id) + return self.image_service.show(context, internal_id) + except exception.NotFound: + return self.image_service.show_by_name(context, ec2_id) + + def _format_image(self, image): """Convert from format defined by BaseImageService to S3 format.""" i = {} - i['imageId'] = self._image_ec2_id(image.get('id'), image.get('type')) - i['kernelId'] = image['properties'].get('kernel_id') - i['ramdiskId'] = image['properties'].get('ramdisk_id') + ec2_id = self._image_ec2_id(image.get('id'), image.get('type')) + name = image.get('name') + if name: + i['imageId'] = "%s (%s)" % (ec2_id, name) + else: + i['imageId'] = ec2_id + kernel_id = image['properties'].get('kernel_id') + if kernel_id: + i['kernelId'] = self._image_ec2_id(kernel_id, 'kernel') + ramdisk_id = image['properties'].get('ramdisk_id') + if ramdisk_id: + i['ramdiskId'] = self._image_ec2_id(ramdisk_id, 'ramdisk') i['imageOwnerId'] = image['properties'].get('owner_id') i['imageLocation'] = image['properties'].get('image_location') i['imageState'] = image['properties'].get('image_state') @@ -872,8 +894,7 @@ class CloudController(object): images = [] for ec2_id in image_id: try: - internal_id = ec2utils.ec2_id_to_id(ec2_id) - image = self.image_service.show(context, internal_id) + image = self._get_image(context, ec2_id) except exception.NotFound: raise exception.NotFound(_('Image %s not found') % ec2_id) @@ -885,16 +906,17 @@ class CloudController(object): def deregister_image(self, context, image_id, **kwargs): LOG.audit(_("De-registering image %s"), image_id, context=context) - internal_id = ec2utils.ec2_id_to_id(image_id) + image = self._get_image(context, image_id) + internal_id = image['id'] self.image_service.delete(context, internal_id) return {'imageId': image_id} def register_image(self, context, image_location=None, **kwargs): if image_location is None and 'name' in kwargs: image_location = kwargs['name'] - metadata = {"image_location": image_location} + metadata = {'properties': {'image_location': image_location}} image = self.image_service.create(context, metadata) - image_id = self._image_ec2_id(image['id'], image['type']) + image_id = self._image_ec2_id(image['id'], image['type']) msg = _("Registered image %(image_location)s with" " id %(image_id)s") % locals() LOG.audit(msg, context=context) @@ -905,13 +927,11 @@ class CloudController(object): raise exception.ApiError(_('attribute not supported: %s') % attribute) try: - internal_id = ec2utils.ec2_id_to_id(image_id) - image = self._format_image(self.image_service.show(context, - internal_id)) - except (IndexError, exception.NotFound): + image = self._get_image(context, image_id) + except exception.NotFound: raise exception.NotFound(_('Image %s not found') % image_id) - result = {'image_id': image_id, 'launchPermission': []} - if image['isPublic']: + result = {'imageId': image_id, 'launchPermission': []} + if image['properties']['is_public']: result['launchPermission'].append({'group': 'all'}) return result @@ -930,13 +950,13 @@ class CloudController(object): LOG.audit(_("Updating image %s publicity"), image_id, context=context) try: - internal_id = ec2utils.ec2_id_to_id(image_id) - metadata = self.image_service.show(context, internal_id) + image = self._get_image(context, image_id) except exception.NotFound: raise exception.NotFound(_('Image %s not found') % image_id) - del(metadata['id']) - metadata['properties']['is_public'] = (operation_type == 'add') - return self.image_service.update(context, internal_id, metadata) + internal_id = image['id'] + del(image['id']) + image['properties']['is_public'] = (operation_type == 'add') + return self.image_service.update(context, internal_id, image) def update_image(self, context, image_id, **kwargs): internal_id = ec2utils.ec2_id_to_id(image_id) diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py index 0ea22c0e6..e4df80cf8 100644 --- a/nova/api/ec2/ec2utils.py +++ b/nova/api/ec2/ec2utils.py @@ -16,10 +16,14 @@ # License for the specific language governing permissions and limitations # under the License. +from nova import exception def ec2_id_to_id(ec2_id): """Convert an ec2 ID (i-[base 16 number]) to an instance id (int)""" - return int(ec2_id.split('-')[-1], 16) + try: + return int(ec2_id.split('-')[-1], 16) + except ValueError: + raise exception.NotFound(_("Id %s Not Found") % ec2_id) def id_to_ec2_id(instance_id, template='i-%08x'): diff --git a/nova/compute/api.py b/nova/compute/api.py index 35a7d7bc0..58118121a 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -126,9 +126,9 @@ class API(base.Base): image = self.image_service.show(context, image_id) if kernel_id is None: - kernel_id = image.get('kernel_id', None) + kernel_id = image['properties'].get('kernel_id', None) if ramdisk_id is None: - ramdisk_id = image.get('ramdisk_id', None) + ramdisk_id = image['properties'].get('ramdisk_id', None) # FIXME(sirp): is there a way we can remove null_kernel? # No kernel and ramdisk for raw images if kernel_id == str(FLAGS.null_kernel): diff --git a/nova/image/glance.py b/nova/image/glance.py index 7db94c0d4..fb383f5e6 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -17,9 +17,6 @@ """Implementation of an image service that uses Glance as the backend""" from __future__ import absolute_import -import httplib -import json -import urlparse from glance.common import exception as glance_exception @@ -55,16 +52,44 @@ class GlanceImageService(service.BaseImageService): """ return self.client.get_images_detailed() - def show(self, context, id): + def show(self, context, image_id): """ Returns a dict containing image data for the given opaque image id. """ try: - image = self.client.get_image_meta(id) + image = self.client.get_image_meta(image_id) except glance_exception.NotFound: raise exception.NotFound return image + def show_by_name(self, context, name): + """ + Returns a dict containing image data for the given name. + """ + # TODO(vish): replace this with more efficient call when glance + # supports it. + images = self.detail(context) + image = None + for cantidate in images: + if name == cantidate.get('name'): + image = cantidate + break + if image == None: + raise exception.NotFound + return image + + def get(self, context, image_id, data): + """ + Calls out to Glance for metadata and data and writes data. + """ + try: + metadata, image_chunks = self.client.get_image(image_id) + except glance_exception.NotFound: + raise exception.NotFound + for chunk in image_chunks: + data.write(chunk) + return metadata + def create(self, context, metadata, data=None): """ Store the image data and return the new image id. diff --git a/nova/image/local.py b/nova/image/local.py index 6fa648b6b..c4ac3baaa 100644 --- a/nova/image/local.py +++ b/nova/image/local.py @@ -42,13 +42,12 @@ class LocalImageService(service.BaseImageService): def _path_to(self, image_id, fname='info.json'): if fname: - return os.path.join(self._path, str(image_id), fname) - return os.path.join(self._path, str(image_id)) + return os.path.join(self._path, '%08x' % int(image_id), fname) + return os.path.join(self._path, '%08x' % int(image_id)) def _ids(self): """The list of all image ids.""" - return [int(i) for i in os.listdir(self._path) - if unicode(i).isnumeric()] + return [int(i, 16) for i in os.listdir(self._path)] def index(self, context): return [dict(image_id=i['id'], name=i.get('name')) @@ -68,27 +67,56 @@ class LocalImageService(service.BaseImageService): try: with open(self._path_to(image_id)) as metadata_file: return json.load(metadata_file) - except IOError: + except (IOError, ValueError): raise exception.NotFound + def show_by_name(self, context, name): + """Returns a dict containing image data for the given name.""" + # NOTE(vish): Not very efficient, but the local image service + # is for testing so it should be fine. + images = self.detail(context) + image = None + for cantidate in images: + if name == cantidate.get('name'): + image = cantidate + break + if image == None: + raise exception.NotFound + return image + + def get(self, context, image_id, data): + """Get image and metadata.""" + try: + with open(self._path_to(image_id)) as metadata_file: + metadata = json.load(metadata_file) + with open(self._path_to(image_id, 'image')) as image_file: + shutil.copyfileobj(image_file, data) + except (IOError, ValueError): + raise exception.NotFound + return metadata + def create(self, context, metadata, data=None): - """Store the image data and return the new image id.""" + """Store the image data and return the new image.""" image_id = random.randint(0, 2 ** 31 - 1) image_path = self._path_to(image_id, None) if not os.path.exists(image_path): os.mkdir(image_path) - return self.update(context, image_id, metadata, data) + return self.update(context, image_id, metadata, data) def update(self, context, image_id, metadata, data=None): """Replace the contents of the given image with the new data.""" metadata['id'] = image_id try: - with open(self._path_to(image_id), 'w') as metadata_file: - json.dump(metadata, metadata_file) if data: - with open(self._path_to(image_id, 'image'), 'w') as image_file: + location = self._path_to(image_id, 'image') + with open(location, 'w') as image_file: shutil.copyfileobj(data, image_file) - except IOError: + # NOTE(vish): update metadata similarly to glance + metadata['status'] = 'active' + metadata['location'] = location + with open(self._path_to(image_id), 'w') as metadata_file: + json.dump(metadata, metadata_file) + except (IOError, ValueError): raise exception.NotFound return metadata @@ -99,7 +127,7 @@ class LocalImageService(service.BaseImageService): """ try: shutil.rmtree(self._path_to(image_id, None)) - except IOError: + except (IOError, ValueError): raise exception.NotFound def delete_all(self): diff --git a/nova/image/s3.py b/nova/image/s3.py index c7446f4b0..ab6eea8cf 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -36,6 +36,7 @@ from nova import flags from nova import utils from nova.auth import manager from nova.image import service +from nova.api.ec2 import ec2utils FLAGS = flags.FLAGS @@ -51,7 +52,7 @@ class S3ImageService(service.BaseImageService): self.service.__init__(*args, **kwargs) def create(self, context, metadata, data=None): - """metadata should contain image_location""" + """metadata['properties'] should contain image_location""" image = self._s3_create(context, metadata) return image @@ -97,6 +98,12 @@ class S3ImageService(service.BaseImageService): raise exception.NotFound return image + def show_by_name(self, context, name): + image = self.service.show_by_name(context, name) + if not self._is_visible(context, image): + raise exception.NotFound + return image + @staticmethod def _conn(context): # TODO(vish): is there a better way to get creds to sign @@ -119,10 +126,12 @@ class S3ImageService(service.BaseImageService): key.get_contents_to_filename(local_filename) return local_filename - def _s3_create(self, context, properties): + def _s3_create(self, context, metadata): + """Gets a manifext from s3 and makes an image""" + image_path = tempfile.mkdtemp(dir=FLAGS.image_decryption_dir) - image_location = properties['image_location'] + image_location = metadata['properties']['image_location'] bucket_name = image_location.split("/")[0] manifest_path = image_location[len(bucket_name) + 1:] bucket = self._conn(context).get_bucket(bucket_name) @@ -153,25 +162,27 @@ class S3ImageService(service.BaseImageService): except: arch = 'x86_64' - properties.update({'owner_id': context.project_id, - 'architecture': arch}) + properties = metadata['properties'] + properties['owner_id'] = context.project_id + properties['architecture'] = arch if kernel_id: - properties['kernel_id'] = kernel_id + properties['kernel_id'] = ec2utils.ec2_id_to_id(kernel_id) if ramdisk_id: - properties['ramdisk_id'] = ramdisk_id + properties['ramdisk_id'] = ec2utils.ec2_id_to_id(ramdisk_id) properties['is_public'] = False - metadata = {'type': image_type, - 'status': 'queued', - 'is_public': True, - 'properties': properties} + metadata.update({'type': image_type, + 'status': 'queued', + 'is_public': True, + 'properties': properties}) metadata['properties']['image_state'] = 'pending' image = self.service.create(context, metadata) image_id = image['id'] def delayed_create(): + """This handles the fetching and decrypting of the part files.""" parts = [] for fn_element in manifest.find("image").getiterator("filename"): part = self._download_file(bucket, fn_element.text, image_path) diff --git a/nova/image/service.py b/nova/image/service.py index e429955f4..c09052cab 100644 --- a/nova/image/service.py +++ b/nova/image/service.py @@ -56,9 +56,9 @@ class BaseImageService(object): """ raise NotImplementedError - def show(self, context, id): + def show(self, context, image_id): """ - Returns a dict containing image data for the given opaque image id. + Returns a dict containing image metadata for the given opaque image id. :retval a mapping with the following signature: @@ -76,9 +76,19 @@ class BaseImageService(object): """ raise NotImplementedError + def get(self, context, data): + """ + Returns a dict containing image metadata and writes image data to data. + + :param data: a file-like object to hold binary image data + + :raises NotFound if the image does not exist + """ + raise NotImplementedError + def create(self, context, metadata, data=None): """ - Store the image data and return the new image id. + Store the image metadata and data and return the new image id. :raises AlreadyExists if the image already exist. @@ -86,7 +96,7 @@ class BaseImageService(object): raise NotImplementedError def update(self, context, image_id, metadata, data=None): - """Replace the contents of the given image with the new data. + """Update the given image with the new metadata and data. :raises NotFound if the image does not exist. diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 7d7b91658..8d2cd9573 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -78,10 +78,11 @@ class CloudTestCase(test.TestCase): project=self.project) host = self.network.get_network_host(self.context.elevated()) - def fake_image_show(meh, context, id): - return dict(kernelId=1, ramdiskId=1) + def fake_show(meh, context, id): + return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}} - self.stubs.Set(local.LocalImageService, 'show', fake_image_show) + self.stubs.Set(local.LocalImageService, 'show', fake_show) + self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show) def tearDown(self): network_ref = db.project_get_network(self.context, @@ -195,8 +196,10 @@ class CloudTestCase(test.TestCase): def test_describe_instances(self): """Makes sure describe_instances works and filters results.""" inst1 = db.instance_create(self.context, {'reservation_id': 'a', + 'image_id': 1, 'host': 'host1'}) inst2 = db.instance_create(self.context, {'reservation_id': 'a', + 'image_id': 1, 'host': 'host2'}) comp1 = db.service_create(self.context, {'host': 'host1', 'availability_zone': 'zone1', @@ -222,11 +225,9 @@ class CloudTestCase(test.TestCase): db.service_destroy(self.context, comp2['id']) def test_console_output(self): - image_id = FLAGS.default_image - print image_id instance_type = FLAGS.default_instance_type max_count = 1 - kwargs = {'image_id': image_id, + kwargs = {'image_id': 'ami-1', 'instance_type': instance_type, 'max_count': max_count} rv = self.cloud.run_instances(self.context, **kwargs) @@ -242,8 +243,7 @@ class CloudTestCase(test.TestCase): greenthread.sleep(0.3) def test_ajax_console(self): - image_id = FLAGS.default_image - kwargs = {'image_id': image_id} + kwargs = {'image_id': 'ami-1'} rv = self.cloud.run_instances(self.context, **kwargs) instance_id = rv['instancesSet'][0]['instanceId'] greenthread.sleep(0.3) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 1f49baaf6..8c18fafc6 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -47,16 +47,16 @@ class ComputeTestCase(test.TestCase): network_manager='nova.network.manager.FlatManager') self.compute = utils.import_object(FLAGS.compute_manager) self.compute_api = compute.API() - - def fake_image_show(meh, context, id): - return dict(kernelId=1, ramdiskId=1) - - self.stubs.Set(local.LocalImageService, 'show', fake_image_show) self.manager = manager.AuthManager() self.user = self.manager.create_user('fake', 'fake', 'fake') self.project = self.manager.create_project('fake', 'fake', 'fake') self.context = context.RequestContext('fake', 'fake', False) + def fake_show(meh, context, id): + return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}} + + self.stubs.Set(local.LocalImageService, 'show', fake_show) + def tearDown(self): self.manager.delete_user(self.user) self.manager.delete_project(self.project) @@ -65,7 +65,7 @@ class ComputeTestCase(test.TestCase): def _create_instance(self): """Create a test instance""" inst = {} - inst['image_id'] = 'ami-test' + inst['image_id'] = 1 inst['reservation_id'] = 'r-fakeres' inst['launch_time'] = '10' inst['user_id'] = self.user.id diff --git a/nova/tests/test_console.py b/nova/tests/test_console.py index 49ff24413..d47c70d88 100644 --- a/nova/tests/test_console.py +++ b/nova/tests/test_console.py @@ -57,7 +57,7 @@ class ConsoleTestCase(test.TestCase): inst = {} #inst['host'] = self.host #inst['name'] = 'instance-1234' - inst['image_id'] = 'ami-test' + inst['image_id'] = 1 inst['reservation_id'] = 'r-fakeres' inst['launch_time'] = '10' inst['user_id'] = self.user.id diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index ca8abdb36..45b544753 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -20,11 +20,12 @@ from nova import compute from nova import context from nova import db from nova import flags +from nova import network from nova import quota from nova import test from nova import utils +from nova import volume from nova.auth import manager -from nova.api.ec2 import cloud from nova.compute import instance_types @@ -41,7 +42,6 @@ class QuotaTestCase(test.TestCase): quota_gigabytes=20, quota_floating_ips=1) - self.cloud = cloud.CloudController() self.manager = manager.AuthManager() self.user = self.manager.create_user('admin', 'admin', 'admin', True) self.project = self.manager.create_project('admin', 'admin', 'admin') @@ -57,7 +57,7 @@ class QuotaTestCase(test.TestCase): def _create_instance(self, cores=2): """Create a test instance""" inst = {} - inst['image_id'] = 'ami-1' + inst['image_id'] = 1 inst['reservation_id'] = 'r-fakeres' inst['user_id'] = self.user.id inst['project_id'] = self.project.id @@ -118,12 +118,12 @@ class QuotaTestCase(test.TestCase): for i in range(FLAGS.quota_instances): instance_id = self._create_instance() instance_ids.append(instance_id) - self.assertRaises(quota.QuotaError, self.cloud.run_instances, + self.assertRaises(quota.QuotaError, compute.API().create, self.context, min_count=1, max_count=1, instance_type='m1.small', - image_id='ami-1') + image_id=1) for instance_id in instance_ids: db.instance_destroy(self.context, instance_id) @@ -131,12 +131,12 @@ class QuotaTestCase(test.TestCase): instance_ids = [] instance_id = self._create_instance(cores=4) instance_ids.append(instance_id) - self.assertRaises(quota.QuotaError, self.cloud.run_instances, + self.assertRaises(quota.QuotaError, compute.API().create, self.context, min_count=1, max_count=1, instance_type='m1.small', - image_id='ami-1') + image_id=1) for instance_id in instance_ids: db.instance_destroy(self.context, instance_id) @@ -145,9 +145,12 @@ class QuotaTestCase(test.TestCase): for i in range(FLAGS.quota_volumes): volume_id = self._create_volume() volume_ids.append(volume_id) - self.assertRaises(quota.QuotaError, self.cloud.create_volume, - self.context, - size=10) + self.assertRaises(quota.QuotaError, + volume.API().create, + self.context, + size=10, + name='', + description='') for volume_id in volume_ids: db.volume_destroy(self.context, volume_id) @@ -156,9 +159,11 @@ class QuotaTestCase(test.TestCase): volume_id = self._create_volume(size=20) volume_ids.append(volume_id) self.assertRaises(quota.QuotaError, - self.cloud.create_volume, + volume.API().create, self.context, - size=10) + size=10, + name='', + description='') for volume_id in volume_ids: db.volume_destroy(self.context, volume_id) @@ -172,7 +177,8 @@ class QuotaTestCase(test.TestCase): # make an rpc.call, the test just finishes with OK. It # appears to be something in the magic inline callbacks # that is breaking. - self.assertRaises(quota.QuotaError, self.cloud.allocate_address, + self.assertRaises(quota.QuotaError, + network.API().allocate_floating_ip, self.context) db.floating_ip_destroy(context.get_admin_context(), address) diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py index b6888c4d2..bb279ac4b 100644 --- a/nova/tests/test_scheduler.py +++ b/nova/tests/test_scheduler.py @@ -155,7 +155,7 @@ class SimpleDriverTestCase(test.TestCase): def _create_instance(self, **kwargs): """Create a test instance""" inst = {} - inst['image_id'] = 'ami-test' + inst['image_id'] = 1 inst['reservation_id'] = 'r-fakeres' inst['user_id'] = self.user.id inst['project_id'] = self.project.id @@ -169,8 +169,6 @@ class SimpleDriverTestCase(test.TestCase): def _create_volume(self): """Create a test volume""" vol = {} - vol['image_id'] = 'ami-test' - vol['reservation_id'] = 'r-fakeres' vol['size'] = 1 vol['availability_zone'] = 'test' return db.volume_create(self.context, vol)['id'] diff --git a/nova/tests/test_volume.py b/nova/tests/test_volume.py index b40ca004b..f698c85b5 100644 --- a/nova/tests/test_volume.py +++ b/nova/tests/test_volume.py @@ -99,7 +99,7 @@ class VolumeTestCase(test.TestCase): def test_run_attach_detach_volume(self): """Make sure volume can be attached and detached from instance.""" inst = {} - inst['image_id'] = 'ami-test' + inst['image_id'] = 1 inst['reservation_id'] = 'r-fakeres' inst['launch_time'] = '10' inst['user_id'] = 'fake' diff --git a/nova/virt/images.py b/nova/virt/images.py index 7a6fef330..06b3a8ecd 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -28,29 +28,32 @@ import time import urllib2 import urlparse +from nova import context from nova import flags from nova import log as logging from nova import utils from nova.auth import manager from nova.auth import signer -from nova.objectstore import image FLAGS = flags.FLAGS -flags.DEFINE_bool('use_s3', True, - 'whether to get images from s3 or use local copy') - LOG = logging.getLogger('nova.virt.images') -def fetch(image, path, user, project): - if FLAGS.use_s3: - f = _fetch_s3_image - else: - f = _fetch_local_image - return f(image, path, user, project) +def fetch(image_id, path, _user, _project): + # TODO(vish): Improve context handling and add owner and auth data + # when it is added to glance. Right now there is no + # auth checking in glance, so we assume that access was + # checked before we got here. + image_service = utils.import_object(FLAGS.image_service) + with open(path, "wb") as image_file: + elevated = context.get_admin_context() + metadata = image_service.get(elevated, image_id, image_file) + return metadata +# NOTE(vish): The methods below should be unnecessary, but I'm leaving +# them in case the glance client does not work on windows. def _fetch_image_no_curl(url, path, headers): request = urllib2.Request(url) for (k, v) in headers.iteritems(): @@ -110,6 +113,8 @@ def _image_path(path): return os.path.join(FLAGS.images_path, path) +# TODO(vish): xenapi should use the glance client code directly instead +# of retrieving the image using this method. def image_url(image): if FLAGS.image_service == "nova.image.glance.GlanceImageService": return "http://%s:%s/images/%s" % (FLAGS.glance_host, diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9f7315c17..02a8208d5 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -579,21 +579,23 @@ class LibvirtConnection(object): 'ramdisk_id': inst['ramdisk_id']} if disk_images['kernel_id']: + fname = '%08x' % int(disk_images['kernel_id']) self._cache_image(fn=self._fetch_image, target=basepath('kernel'), - fname=disk_images['kernel_id'], + fname=fname, image_id=disk_images['kernel_id'], user=user, project=project) if disk_images['ramdisk_id']: + fname = '%08x' % int(disk_images['ramdisk_id']) self._cache_image(fn=self._fetch_image, target=basepath('ramdisk'), - fname=disk_images['ramdisk_id'], + fname=fname, image_id=disk_images['ramdisk_id'], user=user, project=project) - root_fname = disk_images['image_id'] + root_fname = '%08x' % int(disk_images['image_id']) size = FLAGS.minimum_root_size if inst['instance_type'] == 'm1.tiny' or suffix == '.rescue': size = None -- cgit From ac681cdddac29b973b107d6ee06f0fc2039a1d7e Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Sun, 6 Mar 2011 19:21:14 -0800 Subject: zipfile needs to be extracted after nova is running --- contrib/nova.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/contrib/nova.sh b/contrib/nova.sh index 1187f2728..028184605 100755 --- a/contrib/nova.sh +++ b/contrib/nova.sh @@ -166,9 +166,6 @@ NOVA_CONF_EOF $NOVA_DIR/bin/nova-manage user admin admin admin admin # create a project called 'admin' with project manager of 'admin' $NOVA_DIR/bin/nova-manage project create admin admin - # export environment variables for project 'admin' and user 'admin' - $NOVA_DIR/bin/nova-manage project zipfile admin admin $NOVA_DIR/nova.zip - unzip -o $NOVA_DIR/nova.zip -d $NOVA_DIR/ # create a small network $NOVA_DIR/bin/nova-manage network create 10.0.0.0/8 1 32 @@ -184,6 +181,11 @@ NOVA_CONF_EOF screen_it scheduler "$NOVA_DIR/bin/nova-scheduler" screen_it volume "$NOVA_DIR/bin/nova-volume" screen_it ajax_console_proxy "$NOVA_DIR/bin/nova-ajax-console-proxy" + + # export environment variables for project 'admin' and user 'admin' + $NOVA_DIR/bin/nova-manage project zipfile admin admin $NOVA_DIR/nova.zip + unzip -o $NOVA_DIR/nova.zip -d $NOVA_DIR/ + screen_it test ". $NOVA_DIR/novarc" screen -S nova -x fi -- cgit From b3d3366b8fd4eaf81bb9e03ad808c1a139e5b5b0 Mon Sep 17 00:00:00 2001 From: Dan Prince <dan.prince@rackspace.com> Date: Mon, 7 Mar 2011 12:07:23 -0500 Subject: Generate 'adminPass' and call set_password when creating servers. --- nova/api/openstack/servers.py | 10 +++++++--- nova/tests/api/openstack/test_servers.py | 8 +++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 08b95b46a..6cd8bb451 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -84,13 +84,11 @@ def _translate_detail_keys(inst): return dict(server=inst_dict) - def _translate_keys(inst): """ Coerces into dictionary format, excluding all model attributes save for id and name """ return dict(server=dict(id=inst['id'], name=inst['display_name'])) - class Controller(wsgi.Controller): """ The Server API controller for the OpenStack API """ @@ -178,7 +176,13 @@ class Controller(wsgi.Controller): key_data=key_pair['public_key'], metadata=metadata, onset_files=env.get('onset_files', [])) - return _translate_keys(instances[0]) + + server = _translate_keys(instances[0]) + password = "%s%s" % (server['server']['name'][:4], + utils.generate_password(12)) + server['server']['adminPass'] = password + self.compute_api.set_admin_password(context, server['server']['id']) + return server def update(self, req, id): """ Updates the server name or password """ diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 78beb7df9..16b48a13b 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -186,7 +186,7 @@ class ServersTest(test.TestCase): def test_create_instance(self): def instance_create(context, inst): - return {'id': '1', 'display_name': ''} + return {'id': '1', 'display_name': 'server_test'} def server_update(context, id, params): return instance_create(context, id) @@ -230,6 +230,12 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) + server = json.loads(res.body)['server'] + self.assertEqual('serv', server['adminPass'][:4]) + self.assertEqual(16, len(server['adminPass'])) + self.assertEqual('server_test', server['name']) + self.assertEqual('1', server['id']) + self.assertEqual(res.status_int, 200) def test_update_no_body(self): -- cgit From a775c4eee279e11268a6cc447aee24c452e4665a Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Mon, 7 Mar 2011 17:17:41 +0000 Subject: Merge prop changes and test fixes --- nova/tests/xenapi/stubs.py | 26 +++++++++++++------------- nova/virt/xenapi/vm_utils.py | 30 ++++++++++++------------------ nova/virt/xenapi/vmops.py | 4 ++-- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index caefcff34..11e89c9b4 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -227,18 +227,8 @@ class FakeSessionForMigrationTests(fake.SessionBase): def stub_out_migration_methods(stubs): - class FakeSnapshot(object): - def __getattr__(self, key): - return str(key) - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - pass - def fake_get_snapshot(self, instance): - return FakeSnapshot() + return 'foo', 'bar' @classmethod def fake_get_vdi(cls, session, vm_ref): @@ -251,11 +241,21 @@ def stub_out_migration_methods(stubs): pass @classmethod - def fake_scan_sr(cls, session): + def fake_sr(cls, session, *args): + pass + + @classmethod + def fake_get_sr_path(cls, *args): + return "fake" + + def fake_destroy(*args, **kwargs): pass - stubs.Set(vm_utils.VMHelper, 'scan_sr', fake_scan_sr) + stubs.Set(vmops.VMOps, '_destroy', fake_destroy) + stubs.Set(vm_utils.VMHelper, 'scan_default_sr', fake_sr) + stubs.Set(vm_utils.VMHelper, 'scan_sr', fake_sr) stubs.Set(vmops.VMOps, '_get_snapshot', fake_get_snapshot) stubs.Set(vm_utils.VMHelper, 'get_vdi_for_vm_safely', fake_get_vdi) stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', lambda x, y, z: None) + stubs.Set(vm_utils.VMHelper, 'get_sr_path', fake_get_sr_path) stubs.Set(vmops.VMOps, '_shutdown', fake_shutdown) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index eff207a51..80b7540d4 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -307,10 +307,16 @@ class VMHelper(HelperBase): return session.call_xenapi('SR.get_by_name_label', sr_label)[0] @classmethod - def get_sr_path(cls, session, sr_label='slices'): - """Finds the SR and then coerces it into a path on the dom0 file - system""" - return FLAGS.xenapi_sr_base_path + cls.get_sr(session, sr_label) + def get_sr_path(cls, session): + """Return the path to our storage repository + + This is used when we're dealing with VHDs directly, either by taking + snapshots or by restoring an image in the DISK_VHD format. + """ + sr_ref = safe_find_sr(session) + sr_rec = session.get_xenapi().SR.get_record(sr_ref) + sr_uuid = sr_rec["uuid"] + return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid) @classmethod def upload_image(cls, session, instance_id, vdi_uuids, image_id): @@ -326,7 +332,7 @@ class VMHelper(HelperBase): 'image_id': image_id, 'glance_host': FLAGS.glance_host, 'glance_port': FLAGS.glance_port, - 'sr_path': get_sr_path(session)} + 'sr_path': cls.get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'upload_vhd', kwargs) @@ -369,7 +375,7 @@ class VMHelper(HelperBase): 'glance_host': FLAGS.glance_host, 'glance_port': FLAGS.glance_port, 'uuid_stack': uuid_stack, - 'sr_path': get_sr_path(session)} + 'sr_path': cls.get_sr_path(session)} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'download_vhd', kwargs) @@ -775,18 +781,6 @@ def find_sr(session): return None -def get_sr_path(session): - """Return the path to our storage repository - - This is used when we're dealing with VHDs directly, either by taking - snapshots or by restoring an image in the DISK_VHD format. - """ - sr_ref = safe_find_sr(session) - sr_rec = session.get_xenapi().SR.get_record(sr_ref) - sr_uuid = sr_rec["uuid"] - return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid) - - def remap_vbd_dev(dev): """Return the appropriate location for a plugged-in VBD device diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 01bfa2dc5..b862c9de9 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -262,7 +262,7 @@ class VMOps(object): self._session, instance.id, template_vdi_uuids, image_id) finally: if template_vm_ref: - self.virt._destroy(self.instance, template_vm_ref, + self._destroy(instance, template_vm_ref, shutdown=False, destroy_kernel_ramdisk=False) logging.debug(_("Finished snapshot and upload for VM %s"), instance) @@ -330,7 +330,7 @@ class VMOps(object): finally: if template_vm_ref: - self.virt._destroy(self.instance, template_vm_ref, + self._destroy(instance, template_vm_ref, shutdown=False, destroy_kernel_ramdisk=False) # TODO(mdietz): we could also consider renaming these to something -- cgit From f72366f007239656d3d5e3fc80cd277758eedf9b Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" <kevin.mitchell@rackspace.com> Date: Mon, 7 Mar 2011 19:33:24 +0000 Subject: Create --paste_config flag defaulting to api-paste.ini and mv etc/nova-api.conf to match --- bin/nova-api | 7 +++-- etc/api-paste.ini | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ etc/nova-api.conf | 91 ------------------------------------------------------- 3 files changed, 96 insertions(+), 93 deletions(-) create mode 100644 etc/api-paste.ini delete mode 100644 etc/nova-api.conf diff --git a/bin/nova-api b/bin/nova-api index 14be4b841..0b2a44c88 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -43,6 +43,8 @@ from nova import wsgi LOG = logging.getLogger('nova.api') FLAGS = flags.FLAGS +flags.DEFINE_string('paste_config', "api-paste.ini", + 'File name for the paste.deploy config for nova-api') flags.DEFINE_string('ec2_listen', "0.0.0.0", 'IP address for EC2 API to listen') flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen') @@ -90,8 +92,9 @@ if __name__ == '__main__': for flag in FLAGS: flag_get = FLAGS.get(flag, None) LOG.debug("%(flag)s : %(flag_get)s" % locals()) - conf = wsgi.paste_config_file('nova-api.conf') + conf = wsgi.paste_config_file(FLAGS.paste_config) if conf: run_app(conf) else: - LOG.error(_("No paste configuration found for: %s"), 'nova-api.conf') + LOG.error(_("No paste configuration found for: %s"), + FLAGS.paste_config) diff --git a/etc/api-paste.ini b/etc/api-paste.ini new file mode 100644 index 000000000..9f7e93d4c --- /dev/null +++ b/etc/api-paste.ini @@ -0,0 +1,91 @@ +####### +# EC2 # +####### + +[composite:ec2] +use = egg:Paste#urlmap +/: ec2versions +/services/Cloud: ec2cloud +/services/Admin: ec2admin +/latest: ec2metadata +/2007-01-19: ec2metadata +/2007-03-01: ec2metadata +/2007-08-29: ec2metadata +/2007-10-10: ec2metadata +/2007-12-15: ec2metadata +/2008-02-01: ec2metadata +/2008-09-01: ec2metadata +/2009-04-04: ec2metadata +/1.0: ec2metadata + +[pipeline:ec2cloud] +pipeline = logrequest authenticate cloudrequest authorizer ec2executor +#pipeline = logrequest ec2lockout authenticate cloudrequest authorizer ec2executor + +[pipeline:ec2admin] +pipeline = logrequest authenticate adminrequest authorizer ec2executor + +[pipeline:ec2metadata] +pipeline = logrequest ec2md + +[pipeline:ec2versions] +pipeline = logrequest ec2ver + +[filter:logrequest] +paste.filter_factory = nova.api.ec2:RequestLogging.factory + +[filter:ec2lockout] +paste.filter_factory = nova.api.ec2:Lockout.factory + +[filter:authenticate] +paste.filter_factory = nova.api.ec2:Authenticate.factory + +[filter:cloudrequest] +controller = nova.api.ec2.cloud.CloudController +paste.filter_factory = nova.api.ec2:Requestify.factory + +[filter:adminrequest] +controller = nova.api.ec2.admin.AdminController +paste.filter_factory = nova.api.ec2:Requestify.factory + +[filter:authorizer] +paste.filter_factory = nova.api.ec2:Authorizer.factory + +[app:ec2executor] +paste.app_factory = nova.api.ec2:Executor.factory + +[app:ec2ver] +paste.app_factory = nova.api.ec2:Versions.factory + +[app:ec2md] +paste.app_factory = nova.api.ec2.metadatarequesthandler:MetadataRequestHandler.factory + +############# +# Openstack # +############# + +[composite:osapi] +use = egg:Paste#urlmap +/: osversions +/v1.0: openstackapi + +[pipeline:openstackapi] +pipeline = faultwrap auth ratelimit osapiapp + +[filter:faultwrap] +paste.filter_factory = nova.api.openstack:FaultWrapper.factory + +[filter:auth] +paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory + +[filter:ratelimit] +paste.filter_factory = nova.api.openstack.ratelimiting:RateLimitingMiddleware.factory + +[app:osapiapp] +paste.app_factory = nova.api.openstack:APIRouter.factory + +[pipeline:osversions] +pipeline = faultwrap osversionapp + +[app:osversionapp] +paste.app_factory = nova.api.openstack:Versions.factory diff --git a/etc/nova-api.conf b/etc/nova-api.conf deleted file mode 100644 index 9f7e93d4c..000000000 --- a/etc/nova-api.conf +++ /dev/null @@ -1,91 +0,0 @@ -####### -# EC2 # -####### - -[composite:ec2] -use = egg:Paste#urlmap -/: ec2versions -/services/Cloud: ec2cloud -/services/Admin: ec2admin -/latest: ec2metadata -/2007-01-19: ec2metadata -/2007-03-01: ec2metadata -/2007-08-29: ec2metadata -/2007-10-10: ec2metadata -/2007-12-15: ec2metadata -/2008-02-01: ec2metadata -/2008-09-01: ec2metadata -/2009-04-04: ec2metadata -/1.0: ec2metadata - -[pipeline:ec2cloud] -pipeline = logrequest authenticate cloudrequest authorizer ec2executor -#pipeline = logrequest ec2lockout authenticate cloudrequest authorizer ec2executor - -[pipeline:ec2admin] -pipeline = logrequest authenticate adminrequest authorizer ec2executor - -[pipeline:ec2metadata] -pipeline = logrequest ec2md - -[pipeline:ec2versions] -pipeline = logrequest ec2ver - -[filter:logrequest] -paste.filter_factory = nova.api.ec2:RequestLogging.factory - -[filter:ec2lockout] -paste.filter_factory = nova.api.ec2:Lockout.factory - -[filter:authenticate] -paste.filter_factory = nova.api.ec2:Authenticate.factory - -[filter:cloudrequest] -controller = nova.api.ec2.cloud.CloudController -paste.filter_factory = nova.api.ec2:Requestify.factory - -[filter:adminrequest] -controller = nova.api.ec2.admin.AdminController -paste.filter_factory = nova.api.ec2:Requestify.factory - -[filter:authorizer] -paste.filter_factory = nova.api.ec2:Authorizer.factory - -[app:ec2executor] -paste.app_factory = nova.api.ec2:Executor.factory - -[app:ec2ver] -paste.app_factory = nova.api.ec2:Versions.factory - -[app:ec2md] -paste.app_factory = nova.api.ec2.metadatarequesthandler:MetadataRequestHandler.factory - -############# -# Openstack # -############# - -[composite:osapi] -use = egg:Paste#urlmap -/: osversions -/v1.0: openstackapi - -[pipeline:openstackapi] -pipeline = faultwrap auth ratelimit osapiapp - -[filter:faultwrap] -paste.filter_factory = nova.api.openstack:FaultWrapper.factory - -[filter:auth] -paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory - -[filter:ratelimit] -paste.filter_factory = nova.api.openstack.ratelimiting:RateLimitingMiddleware.factory - -[app:osapiapp] -paste.app_factory = nova.api.openstack:APIRouter.factory - -[pipeline:osversions] -pipeline = faultwrap osversionapp - -[app:osversionapp] -paste.app_factory = nova.api.openstack:Versions.factory -- cgit From bcb18ee3d0d095b616c0909c92a151a599d4e17f Mon Sep 17 00:00:00 2001 From: Naveed Massjouni <naveedm9@gmail.com> Date: Mon, 7 Mar 2011 15:05:07 -0500 Subject: Invalid values for offset and limit params in http requests now return a 400 response with a useful message in the body. Also added and updated tests. --- nova/api/openstack/common.py | 11 +++++++---- nova/tests/api/openstack/test_common.py | 20 ++++---------------- nova/tests/api/openstack/test_servers.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 9f85c5c8a..f7a9cc3f0 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -36,15 +36,18 @@ def limited(items, request, max_limit=1000): try: offset = int(request.GET.get('offset', 0)) except ValueError: - offset = 0 + raise webob.exc.HTTPBadRequest(_('offset param must be an integer')) try: limit = int(request.GET.get('limit', max_limit)) except ValueError: - limit = max_limit + raise webob.exc.HTTPBadRequest(_('limit param must be an integer')) - if offset < 0 or limit < 0: - raise webob.exc.HTTPBadRequest() + if limit < 0: + raise webob.exc.HTTPBadRequest(_('limit param must be positive')) + + if offset < 0: + raise webob.exc.HTTPBadRequest(_('offset param must be positive')) limit = min(max_limit, limit or max_limit) range_end = offset + limit diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py index 92023362c..8f57c5b67 100644 --- a/nova/tests/api/openstack/test_common.py +++ b/nova/tests/api/openstack/test_common.py @@ -79,20 +79,14 @@ class LimiterTest(test.TestCase): Test offset key works with a blank offset. """ req = Request.blank('/?offset=') - self.assertEqual(limited(self.tiny, req), self.tiny) - self.assertEqual(limited(self.small, req), self.small) - self.assertEqual(limited(self.medium, req), self.medium) - self.assertEqual(limited(self.large, req), self.large[:1000]) + self.assertRaises(webob.exc.HTTPBadRequest, limited, self.tiny, req) def test_limiter_offset_bad(self): """ Test offset key works with a BAD offset. """ req = Request.blank(u'/?offset=\u0020aa') - self.assertEqual(limited(self.tiny, req), self.tiny) - self.assertEqual(limited(self.small, req), self.small) - self.assertEqual(limited(self.medium, req), self.medium) - self.assertEqual(limited(self.large, req), self.large[:1000]) + self.assertRaises(webob.exc.HTTPBadRequest, limited, self.tiny, req) def test_limiter_nothing(self): """ @@ -166,18 +160,12 @@ class LimiterTest(test.TestCase): """ Test a negative limit. """ - def _limit_large(): - limited(self.large, req, max_limit=2000) - req = Request.blank('/?limit=-3000') - self.assertRaises(webob.exc.HTTPBadRequest, _limit_large) + self.assertRaises(webob.exc.HTTPBadRequest, limited, self.tiny, req) def test_limiter_negative_offset(self): """ Test a negative offset. """ - def _limit_large(): - limited(self.large, req, max_limit=2000) - req = Request.blank('/?offset=-30') - self.assertRaises(webob.exc.HTTPBadRequest, _limit_large) + self.assertRaises(webob.exc.HTTPBadRequest, limited, self.tiny, req) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 78beb7df9..10fb2bafb 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -184,6 +184,34 @@ class ServersTest(test.TestCase): self.assertEqual(s.get('imageId', None), None) i += 1 + def test_get_servers_with_limit(self): + req = webob.Request.blank('/v1.0/servers?limit=3') + res = req.get_response(fakes.wsgi_app()) + servers = json.loads(res.body)['servers'] + self.assertEqual([s['id'] for s in servers], [0, 1, 2]) + + req = webob.Request.blank('/v1.0/servers?limit=aaa') + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) + self.assertTrue('limit' in res.body) + + def test_get_servers_with_offset(self): + req = webob.Request.blank('/v1.0/servers?offset=2') + res = req.get_response(fakes.wsgi_app()) + servers = json.loads(res.body)['servers'] + self.assertEqual([s['id'] for s in servers], [2, 3, 4]) + + req = webob.Request.blank('/v1.0/servers?offset=aaa') + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) + self.assertTrue('offset' in res.body) + + def test_get_servers_with_limit_and_offset(self): + req = webob.Request.blank('/v1.0/servers?limit=2&offset=1') + res = req.get_response(fakes.wsgi_app()) + servers = json.loads(res.body)['servers'] + self.assertEqual([s['id'] for s in servers], [1, 2]) + def test_create_instance(self): def instance_create(context, inst): return {'id': '1', 'display_name': ''} -- cgit From 8c3bc15c96c6a6f1c99d829337921f2645608410 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Mon, 7 Mar 2011 21:43:31 +0100 Subject: Make iptables rules class __ne__ just be inverted __eq__. --- nova/network/linux_net.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 8832f7c68..57e77f8d5 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -85,10 +85,7 @@ class IptablesRule(object): (self.wrap == other.wrap)) def __ne__(self, other): - return ((self.chain != other.chain) or - (self.rule != other.rule) or - (self.top != other.top) or - (self.wrap != other.wrap)) + return not self == other def __str__(self): if self.wrap: -- cgit From 7b7abe7e7a25c0cd07c64c34f69ce050c669cfc3 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Mon, 7 Mar 2011 21:54:25 +0100 Subject: Log failed command execution if there are more retry attempts left. --- nova/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/utils.py b/nova/utils.py index 829adfb9e..80e5b6bbe 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -166,6 +166,7 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, if not attempts: raise else: + LOG.debug(_("%r failed. Retrying."), cmd) greenthread.sleep(random.randint(20, 200) / 100.0) -- cgit From 4e9c570fbf8b3987d556da085b61f159f32c16f1 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Mon, 7 Mar 2011 21:59:05 +0100 Subject: Use IptablesManager.semapahore from securitygroups driver to ensure we don't apply half a rule set. --- nova/virt/libvirt_conn.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index b900cb8eb..825bcb0d7 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1372,8 +1372,12 @@ class IptablesFirewallDriver(FirewallDriver): def refresh_security_group_rules(self, security_group): for instance in self.instances.values(): - self.remove_filters_for_instance(instance) - self.add_filters_for_instance(instance) + # We use the semaphore to make sure noone applies the rule set + # after we've yanked the existing rules but before we've put in + # the new ones. + with self.iptables.semaphore: + self.remove_filters_for_instance(instance) + self.add_filters_for_instance(instance) self.iptables.apply() def _security_group_chain_name(self, security_group_id): -- cgit From 0abd5bfecd279272e5fe1b0de04478909cd77010 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz <emaildericky@gmail.com> Date: Mon, 7 Mar 2011 22:18:15 +0100 Subject: added network_get_by_cidr method to nova.db api --- bin/nova-manage | 9 +-------- nova/db/api.py | 7 +++++++ nova/db/sqlalchemy/api.py | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 9557f2423..b274c5bd1 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -545,16 +545,9 @@ class NetworkCommands(object): network.dhcp_start, network.dns) - def delete(self, fixed_range): """Deletes a network""" - try: - network = [n for n in db.network_get_all(context.get_admin_context()) - if n.cidr == fixed_range][0] - - print network.id, network.cidr, network.project_id - except IndexError: - raise ValueError(_("Network does not exist")) + print db.network_get_by_cidr(context.get_admin_context(), fixed_range) class ServiceCommands(object): """Enable and disable running services""" diff --git a/nova/db/api.py b/nova/db/api.py index d23f14a3c..c73796487 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -459,6 +459,10 @@ def network_associate(context, project_id): """Associate a free network to a project.""" return IMPL.network_associate(context, project_id) +def network_is_associated(context, project_id): + """Returns true the the network is associated to a project""" + return IMPL.network_is_associated(context, project_id) + def network_count(context): """Return the number of networks.""" @@ -525,6 +529,9 @@ def network_get_by_bridge(context, bridge): """Get a network by bridge or raise if it does not exist.""" return IMPL.network_get_by_bridge(context, bridge) +def network_get_by_cidr(context, cidr): + """Get a network by cidr or raise if it does not exist""" + return IMPL.network_get_by_cidr(context, cidr) def network_get_by_instance(context, instance_id): """Get a network by instance id or raise if it does not exist.""" diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 919dda118..bd2de70c7 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -993,6 +993,13 @@ def network_associate(context, project_id): return network_ref +@require_admin_context +def network_is_associated(context, project_id): + session = get_session() + network = session.query(models.Network.project_id).filter(project_id=1).first() + print network + + @require_admin_context def network_count(context): session = get_session() @@ -1116,6 +1123,17 @@ def network_get_by_bridge(context, bridge): return result +@require_admin_context +def network_get_by_cidr(context, cidr): + session = get_session() + result = session.query(models.Network).\ + filter_by(cidr=cidr).first() + + if not result: + raise exception.NotFound(_('Network with cidr %s does not exist') % + cidr) + return result.id + @require_admin_context def network_get_by_instance(_context, instance_id): session = get_session() -- cgit From c944e902aa68d170c0d97a1d50e28fe5e59c572b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 7 Mar 2011 21:20:32 +0000 Subject: rework register commands based on review --- bin/nova-manage | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index ebeda05a0..b61a5d412 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -768,16 +768,16 @@ class ImageCommands(object): except Exception as exc: print _("Failed to register %(path)s: %(exc)s") % locals() - def register_all(self, image, kernel, ramdisk, owner, name=None, + def all_register(self, image, kernel, ramdisk, owner, name=None, is_public='T', architecture='x86_64'): """Uploads an image, kernel, and ramdisk into the image_service arguments: image kernel ramdisk owner [name] [is_public='T'] [architecture='x86_64']""" - kernel_id = self._register('kernel', kernel, owner, None, + kernel_id = self.kernel_register(kernel, owner, None, is_public, architecture) - ramdisk_id = self._register('ramdisk', ramdisk, owner, None, + ramdisk_id = self.ramdisk_register(ramdisk, owner, None, is_public, architecture) - self._register('machine', image, owner, name, is_public, + self.image_register(image, owner, name, is_public, architecture, kernel_id, ramdisk_id) def image_register(self, path, owner, name=None, is_public='T', @@ -785,7 +785,7 @@ class ImageCommands(object): """Uploads an image into the image_service arguments: path owner [name] [is_public='T'] [architecture='x86_64'] [kernel_id] [ramdisk_id]""" - self._register('machine', path, owner, name, is_public, + return self._register('machine', path, owner, name, is_public, architecture, kernel_id, ramdisk_id) def kernel_register(self, path, owner, name=None, is_public='T', @@ -793,14 +793,16 @@ class ImageCommands(object): """Uploads a kernel into the image_service arguments: path owner [name] [is_public='T'] [architecture='x86_64'] """ - self._register('kernel', path, owner, name, is_public, architecture) + return self._register('kernel', path, owner, name, is_public, + architecture) def ramdisk_register(self, path, owner, name=None, is_public='T', architecture='x86_64'): """Uploads a ramdisk into the image_service arguments: path owner [name] [is_public='T'] [architecture='x86_64'] """ - self._register('ramdisk', path, owner, name, is_public, architecture) + return self._register('ramdisk', path, owner, name, is_public, + architecture) def _lookup(self, old_image_id): try: -- cgit From fd95523689b80f53972c59c3738e6b786a7160ff Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 7 Mar 2011 21:22:06 +0000 Subject: pep8 --- bin/nova-manage | 1 - nova/api/ec2/cloud.py | 1 - nova/api/ec2/ec2utils.py | 1 + 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index b61a5d412..f8cc6e68e 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -841,7 +841,6 @@ class ImageCommands(object): except Exception as exc: print _("Failed to convert %(old)s: %(exc)s") % locals() - def convert(self, directory): """Uploads old objectstore images in directory to new service arguments: directory""" diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 496e944fe..6479c9445 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -864,7 +864,6 @@ class CloudController(object): except exception.NotFound: return self.image_service.show_by_name(context, ec2_id) - def _format_image(self, image): """Convert from format defined by BaseImageService to S3 format.""" i = {} diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py index e4df80cf8..3b34f6ea5 100644 --- a/nova/api/ec2/ec2utils.py +++ b/nova/api/ec2/ec2utils.py @@ -18,6 +18,7 @@ from nova import exception + def ec2_id_to_id(ec2_id): """Convert an ec2 ID (i-[base 16 number]) to an instance id (int)""" try: -- cgit From 02e6a17bec06beee5dbffe085073c97281abb586 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 7 Mar 2011 21:30:20 +0000 Subject: move the images_dir out of the way when converting --- bin/nova-manage | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index f8cc6e68e..b97d8b81d 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -846,6 +846,16 @@ class ImageCommands(object): arguments: directory""" machine_images = {} other_images = {} + directory = os.path.abspath(directory) + # NOTE(vish): If we're importing from the images path dir, attempt + # to move the files out of the way before importing + # so we aren't writing to the same directory. This + # may fail if the dir was a mointpoint. + if directory == os.path.abspath(FLAGS.images_path): + new_dir = "%s_bak" % directory + os.move(directory, new_dir) + os.mkdir(directory) + directory = new_dir for fn in glob.glob("%s/*/info.json" % directory): try: image_path = os.path.join(fn.rpartition('/')[0], 'image') -- cgit From 56ee811efd52d0971d7fea4c232a904b3ee78ac6 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz <emaildericky@gmail.com> Date: Mon, 7 Mar 2011 22:37:26 +0100 Subject: deleted network_is_associated from nova.db api --- bin/nova-manage | 4 ++-- nova/db/api.py | 5 ----- nova/db/sqlalchemy/api.py | 9 +-------- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index b274c5bd1..94b0d5946 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -547,8 +547,8 @@ class NetworkCommands(object): def delete(self, fixed_range): """Deletes a network""" - print db.network_get_by_cidr(context.get_admin_context(), fixed_range) - + network = db.network_get_by_cidr(context.get_admin_context(), fixed_range) + class ServiceCommands(object): """Enable and disable running services""" diff --git a/nova/db/api.py b/nova/db/api.py index c73796487..04f5fd72f 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -459,11 +459,6 @@ def network_associate(context, project_id): """Associate a free network to a project.""" return IMPL.network_associate(context, project_id) -def network_is_associated(context, project_id): - """Returns true the the network is associated to a project""" - return IMPL.network_is_associated(context, project_id) - - def network_count(context): """Return the number of networks.""" return IMPL.network_count(context) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index bd2de70c7..c8f42425d 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -993,13 +993,6 @@ def network_associate(context, project_id): return network_ref -@require_admin_context -def network_is_associated(context, project_id): - session = get_session() - network = session.query(models.Network.project_id).filter(project_id=1).first() - print network - - @require_admin_context def network_count(context): session = get_session() @@ -1132,7 +1125,7 @@ def network_get_by_cidr(context, cidr): if not result: raise exception.NotFound(_('Network with cidr %s does not exist') % cidr) - return result.id + return result @require_admin_context def network_get_by_instance(_context, instance_id): -- cgit From f79220a1f6a12621463b410d26e31e29a9e6ea3e Mon Sep 17 00:00:00 2001 From: Trey Morris <trey.morris@rackspace.com> Date: Mon, 7 Mar 2011 15:41:37 -0600 Subject: cleaned up virt.xenapi.vmops._get_vm_opaque_ref. more reliable approach to checking if param is an opaque ref. code is cleaner --- nova/virt/xenapi/vmops.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index b862c9de9..b1671fde4 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -188,30 +188,32 @@ class VMOps(object): """Refactored out the common code of many methods that receive either a vm name or a vm instance, and want a vm instance in return. """ - vm = None - try: - if instance_or_vm.startswith("OpaqueRef:"): - # Got passed an opaque ref; return it + # if instance_or_vm is a string it must be opaque ref or instance name + if isinstance(instance_or_vm, str): + vm_rec = self._session.get_xenapi().VM.get_record(instance_or_vm) + if vm_rec != None: + # an opaque ref was passed in, return it return instance_or_vm else: - # Must be the instance name + # it must be an instance name instance_name = instance_or_vm - except (AttributeError, KeyError): - # Note the the KeyError will only happen with fakes.py - # Not a string; must be an ID or a vm instance - if isinstance(instance_or_vm, (int, long)): - ctx = context.get_admin_context() - try: - instance_obj = db.instance_get(ctx, instance_or_vm) - instance_name = instance_obj.name - except exception.NotFound: - # The unit tests screw this up, as they use an integer for - # the vm name. I'd fix that up, but that's a matter for - # another bug report. So for now, just try with the passed - # value - instance_name = instance_or_vm - else: - instance_name = instance_or_vm.name + + # if instance_or_vm is an int/long it must be instance id + elif isinstance(instance_or_vm, (int, long)): + ctx = context.get_admin_context() + try: + instance_obj = db.instance_get(ctx, instance_or_vm) + instance_name = instance_obj.name + except exception.NotFound: + # The unit tests screw this up, as they use an integer for + # the vm name. I'd fix that up, but that's a matter for + # another bug report. So for now, just try with the passed + # value + instance_name = instance_or_vm + + # otherwise instance_or_vm is an instance object + else: + instance_name = instance_or_vm.name vm = VMHelper.lookup(self._session, instance_name) if vm is None: raise exception.NotFound( -- cgit From 59f73e3180731cec644b590d448e0da74711ae03 Mon Sep 17 00:00:00 2001 From: Trey Morris <trey.morris@rackspace.com> Date: Mon, 7 Mar 2011 16:11:10 -0600 Subject: virt.xenapi.vmops._get_vm_opaque_ref exception caught properly --- nova/virt/xenapi/vmops.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index b1671fde4..ae4609418 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -190,13 +190,16 @@ class VMOps(object): """ # if instance_or_vm is a string it must be opaque ref or instance name if isinstance(instance_or_vm, str): - vm_rec = self._session.get_xenapi().VM.get_record(instance_or_vm) - if vm_rec != None: - # an opaque ref was passed in, return it - return instance_or_vm - else: - # it must be an instance name - instance_name = instance_or_vm + ref = None + try: + ref = self._session.get_xenapi().VM.get_record(instance_or_vm) + if ref != None: + # an opaque ref was passed in, return it + return instance_or_vm + except: + pass + # wasn't an opaque ref, must be an instance name + instance_name = instance_or_vm # if instance_or_vm is an int/long it must be instance id elif isinstance(instance_or_vm, (int, long)): -- cgit From 3fc6b8cbbd1be5baffc300112a0e39a807209c36 Mon Sep 17 00:00:00 2001 From: Trey Morris <trey.morris@rackspace.com> Date: Mon, 7 Mar 2011 16:34:59 -0600 Subject: virt.xenapi.vmops._get_vm_opaque_ref checks for basestring instance instead of str --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ae4609418..30fa5bdd7 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -189,7 +189,7 @@ class VMOps(object): a vm name or a vm instance, and want a vm instance in return. """ # if instance_or_vm is a string it must be opaque ref or instance name - if isinstance(instance_or_vm, str): + if isinstance(instance_or_vm, basestring): ref = None try: ref = self._session.get_xenapi().VM.get_record(instance_or_vm) -- cgit From 88c5555e867c730065c18541a35b161eb861b502 Mon Sep 17 00:00:00 2001 From: Cerberus <matt.dietz@rackspace.com> Date: Mon, 7 Mar 2011 16:40:19 -0600 Subject: First part of the bug fix --- nova/compute/manager.py | 4 +--- nova/virt/xenapi/vmops.py | 8 +++++++- nova/virt/xenapi_conn.py | 9 +++++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index b3e864154..b35216dd3 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -510,9 +510,7 @@ class ComputeManager(manager.Manager): instance_ref = self.db.instance_get(context, migration_ref['instance_id']) - # this may get passed into the following spawn instead - new_disk_info = self.driver.attach_disk(instance_ref, disk_info) - self.driver.spawn(instance_ref, disk=new_disk_info) + self.driver.finish_resize(instance_ref, disk_info) self.db.migration_update(context, migration_id, {'status': 'finished', }) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index b862c9de9..37f513599 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -72,7 +72,13 @@ class VMOps(object): LOG.debug(_("Starting instance %s"), instance.name) self._session.call_xenapi('VM.start', vm_ref, False, False) - def spawn(self, instance, disk): + def spawn(self, instance): + self._spawn(instance, disk=None) + + def spawn_with_disk(self, instance, disk): + self._spawn(instance, disk=disk) + + def _spawn(self, instance, disk): """Create VM instance""" instance_name = instance.name vm = VMHelper.lookup(self._session, instance_name) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 62e17e851..7e8f825e9 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -154,9 +154,14 @@ class XenAPIConnection(object): """List VM instances""" return self._vmops.list_instances() - def spawn(self, instance, disk=None): + def spawn(self, instance): """Create VM instance""" - self._vmops.spawn(instance, disk) + self._vmops.spawn(instance) + + def finish_resize(self, instance, disk_info) + """Completes a resize, turning on the migrated instance""" + new_disk_info = self.attach_disk(instance, disk_info) + self._vmops.spawn_with_disk(instance, new_disk_info) def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance """ -- cgit From ede88283729663f11d913cc54bcf8ee08028d98f Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Mon, 7 Mar 2011 14:42:36 -0800 Subject: A few formatting niceties --- nova/service.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/nova/service.py b/nova/service.py index 8fdaca0a5..389a6b2df 100644 --- a/nova/service.py +++ b/nova/service.py @@ -42,6 +42,7 @@ from nova import utils from nova import version from nova import wsgi + FLAGS = flags.FLAGS flags.DEFINE_integer('report_interval', 10, 'seconds between nodes reporting state to datastore', @@ -271,6 +272,11 @@ def serve(*services): x.start() +def wait(): + while True: + greenthread.sleep(5) + + def serve_wsgi(cls, conf): try: service = cls.create(conf) @@ -290,11 +296,6 @@ def serve_wsgi(cls, conf): return service -def wait(): - while True: - greenthread.sleep(5) - - def _run_wsgi(paste_config_file, apis): logging.debug(_("Using paste.deploy config at: %s"), paste_config_file) apps = [] -- cgit From 5c7ee13b058fb954fd9bbc4a3550716b8faa0b97 Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Mon, 7 Mar 2011 22:50:35 +0000 Subject: And unit tests --- nova/tests/test_xenapi.py | 5 +++++ nova/tests/xenapi/stubs.py | 4 ++++ nova/virt/xenapi_conn.py | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 7f437c2b8..6e458558d 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -383,6 +383,11 @@ class XenAPIMigrateInstance(test.TestCase): conn = xenapi_conn.get_connection(False) conn.attach_disk(instance, {'base_copy': 'hurr', 'cow': 'durr'}) + def test_finish_resize(self): + instance = db.instance_create(self.values) + stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) + conn = xenapi_conn.get_connection(False) + conn.finish_resize(instance, dict(base_copy='hurr', cow='durr')) class XenAPIDetermineDiskImageTestCase(test.TestCase): """ diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 11e89c9b4..28037c2ba 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -251,6 +251,9 @@ def stub_out_migration_methods(stubs): def fake_destroy(*args, **kwargs): pass + def fake_spawn_with_disk(*args, **kwargs): + pass + stubs.Set(vmops.VMOps, '_destroy', fake_destroy) stubs.Set(vm_utils.VMHelper, 'scan_default_sr', fake_sr) stubs.Set(vm_utils.VMHelper, 'scan_sr', fake_sr) @@ -259,3 +262,4 @@ def stub_out_migration_methods(stubs): stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', lambda x, y, z: None) stubs.Set(vm_utils.VMHelper, 'get_sr_path', fake_get_sr_path) stubs.Set(vmops.VMOps, '_shutdown', fake_shutdown) + stubs.Set(vmops.VMOps, 'spawn_with_disk', fake_spawn_with_disk) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 7e8f825e9..3991496b2 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -158,7 +158,7 @@ class XenAPIConnection(object): """Create VM instance""" self._vmops.spawn(instance) - def finish_resize(self, instance, disk_info) + def finish_resize(self, instance, disk_info): """Completes a resize, turning on the migrated instance""" new_disk_info = self.attach_disk(instance, disk_info) self._vmops.spawn_with_disk(instance, new_disk_info) -- cgit From 2f0845b7b80081d18ee268b94fe38326f3c5401e Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Mon, 7 Mar 2011 23:07:05 +0000 Subject: A few more changes --- nova/tests/test_xenapi.py | 6 ------ nova/virt/xenapi/vmops.py | 10 +++++----- nova/virt/xenapi_conn.py | 9 +++------ 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 6e458558d..f5b154a51 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -377,12 +377,6 @@ class XenAPIMigrateInstance(test.TestCase): conn = xenapi_conn.get_connection(False) conn.migrate_disk_and_power_off(instance, '127.0.0.1') - def test_attach_disk(self): - instance = db.instance_create(self.values) - stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) - conn = xenapi_conn.get_connection(False) - conn.attach_disk(instance, {'base_copy': 'hurr', 'cow': 'durr'}) - def test_finish_resize(self): instance = db.instance_create(self.values) stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 37f513599..e658de7f3 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -75,8 +75,8 @@ class VMOps(object): def spawn(self, instance): self._spawn(instance, disk=None) - def spawn_with_disk(self, instance, disk): - self._spawn(instance, disk=disk) + def spawn_with_disk(self, instance, vdi_uuid): + self._spawn(instance, disk=vdi_uuid) def _spawn(self, instance, disk): """Create VM instance""" @@ -343,14 +343,14 @@ class VMOps(object): # sensible so we don't need to blindly pass around dictionaries return {'base_copy': base_copy_uuid, 'cow': cow_uuid} - def attach_disk(self, instance, disk_info): + def attach_disk(self, instance, base_copy_uuid, cow_uuid): """Links the base copy VHD to the COW via the XAPI plugin""" vm_ref = VMHelper.lookup(self._session, instance.name) new_base_copy_uuid = str(uuid.uuid4()) new_cow_uuid = str(uuid.uuid4()) params = {'instance_id': instance.id, - 'old_base_copy_uuid': disk_info['base_copy'], - 'old_cow_uuid': disk_info['cow'], + 'old_base_copy_uuid': base_copy_uuid, + 'old_cow_uuid': cow_uuid, 'new_base_copy_uuid': new_base_copy_uuid, 'new_cow_uuid': new_cow_uuid, 'sr_path': VMHelper.get_sr_path(self._session), } diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 3991496b2..9965accad 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -160,8 +160,9 @@ class XenAPIConnection(object): def finish_resize(self, instance, disk_info): """Completes a resize, turning on the migrated instance""" - new_disk_info = self.attach_disk(instance, disk_info) - self._vmops.spawn_with_disk(instance, new_disk_info) + cow_uuid = self._vmops.attach_disk(instance, disk_info['base_copy'], + disk_info['cow']) + self._vmops.spawn_with_disk(instance, cow_uuid) def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance """ @@ -202,10 +203,6 @@ class XenAPIConnection(object): off the instance copies over the COW disk""" return self._vmops.migrate_disk_and_power_off(instance, dest) - def attach_disk(self, instance, disk_info): - """Moves the copied VDIs into the SR""" - return self._vmops.attach_disk(instance, disk_info) - def suspend(self, instance, callback): """suspend the specified instance""" self._vmops.suspend(instance, callback) -- cgit From 8e0fd37ddfbe88df296cf45583f0b3e4fa4d7a75 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Mon, 7 Mar 2011 15:22:59 -0800 Subject: Converted tabs to spaces in bin/nova-api --- bin/nova-api | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-api b/bin/nova-api index 2d2ef6d0c..c921ec45c 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -62,5 +62,5 @@ if __name__ == '__main__': LOG.error(_("No paste configuration found for: %s"), 'nova-api.conf') sys.exit(1) else: - service = service.serve_wsgi(service.ApiService, conf) + service = service.serve_wsgi(service.ApiService, conf) service.wait() -- cgit From e69c802aaf40f3b90789aeef8bf3ef5dcbbcb2f3 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Mon, 7 Mar 2011 15:36:04 -0800 Subject: Moved FLAGS.paste_config to its re-usable location --- bin/nova-api | 14 +++----------- nova/service.py | 10 +++++++--- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/bin/nova-api b/bin/nova-api index f48dbe5a5..85ca4eefd 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -45,9 +45,6 @@ from nova import wsgi LOG = logging.getLogger('nova.api') FLAGS = flags.FLAGS -flags.DEFINE_string('paste_config', "api-paste.ini", - 'File name for the paste.deploy config for nova-api') - if __name__ == '__main__': utils.default_flagfile() @@ -59,11 +56,6 @@ if __name__ == '__main__': for flag in FLAGS: flag_get = FLAGS.get(flag, None) LOG.debug("%(flag)s : %(flag_get)s" % locals()) - conf = wsgi.paste_config_file(FLAGS.paste_config) - if not conf: - LOG.error(_("No paste configuration found for: %s"), - FLAGS.paste_config) - sys.exit(1) - else: - service = service.serve_wsgi(service.ApiService, conf) - service.wait() + + service = service.serve_wsgi(service.ApiService) + service.wait() diff --git a/nova/service.py b/nova/service.py index 389a6b2df..5a8d58695 100644 --- a/nova/service.py +++ b/nova/service.py @@ -56,6 +56,8 @@ flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen') flags.DEFINE_string('osapi_listen', "0.0.0.0", 'IP address for OpenStack API to listen') flags.DEFINE_integer('osapi_listen_port', 8774, 'port for os api to listen') +flags.DEFINE_string('paste_config', "api-paste.ini", + 'File name for the paste.deploy config for nova-api') class Service(object): @@ -238,9 +240,11 @@ class ApiService(WsgiService): @classmethod def create(cls, conf=None): if not conf: - conf = wsgi.paste_config_file('nova-api.conf') + conf = wsgi.paste_config_file(FLAGS.paste_config) if not conf: - raise exception.Error(_("Cannot load nova-api.conf")) + message = (_("No paste configuration found for: %s"), + FLAGS.paste_config) + raise exception.Error(message) api_endpoints = ['ec2', 'osapi'] service = cls(conf, api_endpoints) return service @@ -277,7 +281,7 @@ def wait(): greenthread.sleep(5) -def serve_wsgi(cls, conf): +def serve_wsgi(cls, conf=None): try: service = cls.create(conf) except Exception: -- cgit From e39995def6a2a11cdd430b0e6f603b493be5542b Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Mon, 7 Mar 2011 23:51:20 +0000 Subject: Some more refactoring and a tighter unit test --- nova/tests/test_xenapi.py | 14 ++++++++++---- nova/tests/xenapi/stubs.py | 15 +++++++++++++-- nova/virt/xenapi/vmops.py | 30 ++++++++++++++---------------- nova/virt/xenapi_conn.py | 4 ++-- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index f5b154a51..919a38c06 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -360,16 +360,22 @@ class XenAPIMigrateInstance(test.TestCase): db_fakes.stub_out_db_instance_api(self.stubs) stubs.stub_out_get_target(self.stubs) xenapi_fake.reset() + self.manager = manager.AuthManager() + self.user = self.manager.create_user('fake', 'fake', 'fake', + admin=True) + self.project = self.manager.create_project('fake', 'fake', 'fake') self.values = {'name': 1, 'id': 1, - 'project_id': 'fake', - 'user_id': 'fake', + 'project_id': self.project.id, + 'user_id': self.user.id, 'image_id': 1, - 'kernel_id': 2, - 'ramdisk_id': 3, + 'kernel_id': None, + 'ramdisk_id': None, 'instance_type': 'm1.large', 'mac_address': 'aa:bb:cc:dd:ee:ff', } stubs.stub_out_migration_methods(self.stubs) + glance_stubs.stubout_glance_client(self.stubs, + glance_stubs.FakeGlance) def test_migrate_disk_and_power_off(self): instance = db.instance_create(self.values) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 28037c2ba..d8e358611 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -225,6 +225,17 @@ class FakeSessionForMigrationTests(fake.SessionBase): def __init__(self, uri): super(FakeSessionForMigrationTests, self).__init__(uri) + def VDI_get_by_uuid(*args): + return 'hurr' + + def VM_start(self, _1, ref, _2, _3): + vm = fake.get_record('VM', ref) + if vm['power_state'] != 'Halted': + raise fake.Failure(['VM_BAD_POWER_STATE', ref, 'Halted', + vm['power_state']]) + vm['power_state'] = 'Running' + vm['is_a_template'] = False + vm['is_control_domain'] = False def stub_out_migration_methods(stubs): def fake_get_snapshot(self, instance): @@ -251,7 +262,7 @@ def stub_out_migration_methods(stubs): def fake_destroy(*args, **kwargs): pass - def fake_spawn_with_disk(*args, **kwargs): + def fake_reset_network(*args, **kwargs): pass stubs.Set(vmops.VMOps, '_destroy', fake_destroy) @@ -261,5 +272,5 @@ def stub_out_migration_methods(stubs): stubs.Set(vm_utils.VMHelper, 'get_vdi_for_vm_safely', fake_get_vdi) stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', lambda x, y, z: None) stubs.Set(vm_utils.VMHelper, 'get_sr_path', fake_get_sr_path) + stubs.Set(vmops.VMOps, 'reset_network', fake_reset_network) stubs.Set(vmops.VMOps, '_shutdown', fake_shutdown) - stubs.Set(vmops.VMOps, 'spawn_with_disk', fake_spawn_with_disk) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index e658de7f3..7fe1f6ff0 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -72,13 +72,19 @@ class VMOps(object): LOG.debug(_("Starting instance %s"), instance.name) self._session.call_xenapi('VM.start', vm_ref, False, False) - def spawn(self, instance): - self._spawn(instance, disk=None) - - def spawn_with_disk(self, instance, vdi_uuid): - self._spawn(instance, disk=vdi_uuid) + def create_disk(self, instance): + user = AuthManager().get_user(instance.user_id) + project = AuthManager().get_project(instance.project_id) + disk_image_type = VMHelper.determine_disk_image_type(instance) + vdi_uuid = VMHelper.fetch_image(self._session, instance.id, + instance.image_id, user, project, disk_image_type) + return vdi_uuid - def _spawn(self, instance, disk): + def spawn(self, instance): + vdi_uuid = self.create_disk(instance) + self._spawn_with_disk(instance, vdi_uuid=vdi_uuid) + + def _spawn_with_disk(self, instance, vdi_uuid): """Create VM instance""" instance_name = instance.name vm = VMHelper.lookup(self._session, instance_name) @@ -101,17 +107,9 @@ class VMOps(object): vdi_ref = kernel = ramdisk = pv_kernel = None # Are we building from a pre-existing disk? - if not disk: - #if kernel is not present we must download a raw disk - - disk_image_type = VMHelper.determine_disk_image_type(instance) - vdi_uuid = VMHelper.fetch_image(self._session, instance.id, - instance.image_id, user, project, disk_image_type) - vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) - - else: - vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', disk) + vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) + disk_image_type = VMHelper.determine_disk_image_type(instance) if disk_image_type == ImageType.DISK_RAW: # Have a look at the VDI and see if it has a PV kernel pv_kernel = VMHelper.lookup_image(self._session, instance.id, diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 9965accad..b63a5f8c3 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -160,9 +160,9 @@ class XenAPIConnection(object): def finish_resize(self, instance, disk_info): """Completes a resize, turning on the migrated instance""" - cow_uuid = self._vmops.attach_disk(instance, disk_info['base_copy'], + vdi_uuid = self._vmops.attach_disk(instance, disk_info['base_copy'], disk_info['cow']) - self._vmops.spawn_with_disk(instance, cow_uuid) + self._vmops._spawn_with_disk(instance, vdi_uuid) def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance """ -- cgit From ecc6bce311ce85b05802cf04dd2b03a3b91d178d Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Mon, 7 Mar 2011 16:01:43 -0800 Subject: add a delay before grabbing zipfile --- contrib/nova.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/nova.sh b/contrib/nova.sh index 028184605..d6c9b1081 100755 --- a/contrib/nova.sh +++ b/contrib/nova.sh @@ -181,7 +181,7 @@ NOVA_CONF_EOF screen_it scheduler "$NOVA_DIR/bin/nova-scheduler" screen_it volume "$NOVA_DIR/bin/nova-volume" screen_it ajax_console_proxy "$NOVA_DIR/bin/nova-ajax-console-proxy" - + sleep 2 # export environment variables for project 'admin' and user 'admin' $NOVA_DIR/bin/nova-manage project zipfile admin admin $NOVA_DIR/nova.zip unzip -o $NOVA_DIR/nova.zip -d $NOVA_DIR/ -- cgit From 5ec9cbcdee3de3868a47ca5ec351a9a2594ceea2 Mon Sep 17 00:00:00 2001 From: Trey Morris <trey.morris@rackspace.com> Date: Mon, 7 Mar 2011 18:05:27 -0600 Subject: virt.xenapi.vmops._get_vm_opaque_ref assumes VM.get_record raises --- nova/virt/xenapi/vmops.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 30fa5bdd7..c0fbf96fc 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -192,14 +192,12 @@ class VMOps(object): if isinstance(instance_or_vm, basestring): ref = None try: + # check for opaque ref ref = self._session.get_xenapi().VM.get_record(instance_or_vm) - if ref != None: - # an opaque ref was passed in, return it - return instance_or_vm - except: - pass - # wasn't an opaque ref, must be an instance name - instance_name = instance_or_vm + return instance_or_vm + except self.XenAPI.Failure: + # wasn't an opaque ref, must be an instance name + instance_name = instance_or_vm # if instance_or_vm is an int/long it must be instance id elif isinstance(instance_or_vm, (int, long)): -- cgit From 4e8b6a14324ef2d1f550233cbcfc94c6363533d8 Mon Sep 17 00:00:00 2001 From: Trey Morris <trey.morris@rackspace.com> Date: Mon, 7 Mar 2011 18:46:44 -0600 Subject: virt.xenapi.vmops._get_vm_opaque_ref changed vm to vm_ref and ref to obj --- nova/virt/xenapi/vmops.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c0fbf96fc..0adabe7f5 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -190,10 +190,10 @@ class VMOps(object): """ # if instance_or_vm is a string it must be opaque ref or instance name if isinstance(instance_or_vm, basestring): - ref = None + obj = None try: # check for opaque ref - ref = self._session.get_xenapi().VM.get_record(instance_or_vm) + obj = self._session.get_xenapi().VM.get_record(instance_or_vm) return instance_or_vm except self.XenAPI.Failure: # wasn't an opaque ref, must be an instance name @@ -215,11 +215,11 @@ class VMOps(object): # otherwise instance_or_vm is an instance object else: instance_name = instance_or_vm.name - vm = VMHelper.lookup(self._session, instance_name) - if vm is None: + vm_ref = VMHelper.lookup(self._session, instance_name) + if vm_ref is None: raise exception.NotFound( _('Instance not present %s') % instance_name) - return vm + return vm_ref def _acquire_bootlock(self, vm): """Prevent an instance from booting""" -- cgit From cac5881eaa35f94e004c18dd34ca78014f067976 Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Tue, 8 Mar 2011 01:01:41 -0500 Subject: execvp --- nova/crypto.py | 32 +-- nova/network/linux_net.py | 222 +++++++++++---------- nova/tests/test_network.py | 16 +- nova/utils.py | 16 +- nova/virt/disk.py | 44 ++-- nova/virt/images.py | 5 +- nova/virt/libvirt_conn.py | 36 ++-- nova/virt/xenapi/vm_utils.py | 11 +- nova/volume/driver.py | 71 +++---- .../networking/etc/xensource/scripts/vif_rules.py | 91 ++++++--- 10 files changed, 296 insertions(+), 248 deletions(-) diff --git a/nova/crypto.py b/nova/crypto.py index b240a3958..dd24723b8 100644 --- a/nova/crypto.py +++ b/nova/crypto.py @@ -105,8 +105,10 @@ def generate_key_pair(bits=1024): tmpdir = tempfile.mkdtemp() keyfile = os.path.join(tmpdir, 'temp') - utils.execute('ssh-keygen','-q','-b',"%d" % bits,'-N','""','-f',keyfile) - (out, err) = utils.execute('ssh-keygen','-q','-l','-f',"%s.pub" % (keyfile)) + utils.execute('ssh-keygen', '-q', '-b', '%d' % bits, '-N', '', + '-f', keyfile) + (out, err) = utils.execute('ssh-keygen', '-q', '-l', '-f', + '%s.pub' % (keyfile)) fingerprint = out.split(' ')[1] private_key = open(keyfile).read() public_key = open(keyfile + '.pub').read() @@ -118,7 +120,7 @@ def generate_key_pair(bits=1024): # bio = M2Crypto.BIO.MemoryBuffer() # key.save_pub_key_bio(bio) # public_key = bio.read() - # public_key, err = execute('ssh-keygen','-y','-f','/dev/stdin', private_key) + # public_key, err = execute('ssh-keygen', '-y', '-f', '/dev/stdin', private_key) return (private_key, public_key, fingerprint) @@ -143,9 +145,10 @@ def revoke_cert(project_id, file_name): start = os.getcwd() os.chdir(ca_folder(project_id)) # NOTE(vish): potential race condition here - utils.execute('openssl','ca','-config','./openssl.cnf','-revoke',"'%s'" % file_name) - utils.execute('openssl','ca','-gencrl','-config','./openssl.cnf','-out',"'%s'" % - FLAGS.crl_file) + utils.execute('openssl', 'ca', '-config', './openssl.cnf', '-revoke', + '%s' % file_name) + utils.execute('openssl', 'ca', '-gencrl', '-config', './openssl.cnf', + '-out', '%s' % FLAGS.crl_file) os.chdir(start) @@ -193,8 +196,9 @@ def generate_x509_cert(user_id, project_id, bits=1024): tmpdir = tempfile.mkdtemp() keyfile = os.path.abspath(os.path.join(tmpdir, 'temp.key')) csrfile = os.path.join(tmpdir, 'temp.csr') - utils.execute('openssl','genrsa','-out',keyfile,bits) - utils.execute('openssl','req','-new','-key',keyfile,'-out',csrfile,'-batch','-subj',subject) + utils.execute('openssl', 'genrsa', '-out', keyfile, bits) + utils.execute('openssl', 'req', '-new', '-key', keyfile, '-out', csrfile, + '-batch', '-subj', subject) private_key = open(keyfile).read() csr = open(csrfile).read() shutil.rmtree(tmpdir) @@ -211,7 +215,8 @@ def _ensure_project_folder(project_id): if not os.path.exists(ca_path(project_id)): start = os.getcwd() os.chdir(ca_folder()) - utils.execute('sh','geninter.sh',project_id, _project_cert_subject(project_id)) + utils.execute('sh', 'geninter.sh', project_id, + _project_cert_subject(project_id)) os.chdir(start) @@ -226,7 +231,7 @@ def generate_vpn_files(project_id): start = os.getcwd() os.chdir(ca_folder()) # TODO(vish): the shell scripts could all be done in python - utils.execute('sh','genvpn.sh', + utils.execute('sh', 'genvpn.sh', project_id, _vpn_cert_subject(project_id)) with open(csr_fn, "r") as csrfile: csr_text = csrfile.read() @@ -257,9 +262,10 @@ def _sign_csr(csr_text, ca_folder): start = os.getcwd() # Change working dir to CA os.chdir(ca_folder) - utils.execute('openssl','ca','-batch','-out',outbound,'-config' - './openssl.cnf','-infiles',inbound) - out, _err = utils.execute('openssl','x509','-in',outbound','-serial','-noout') + utils.execute('openssl', 'ca', '-batch', '-out', outbound, '-config', + './openssl.cnf', '-infiles', inbound) + out, _err = utils.execute('openssl', 'x509', '-in', outbound, + '-serial', '-noout') serial = out.rpartition("=")[2] os.chdir(start) with open(outbound, "r") as crtfile: diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 535ce87bc..ad019a8c0 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -65,113 +65,117 @@ flags.DEFINE_string('dmz_cidr', '10.128.0.0/24', def metadata_forward(): """Create forwarding rule for metadata""" - _confirm_rule("PREROUTING", "-t nat -s 0.0.0.0/0 " - "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT " - "--to-destination %s:%s" % (FLAGS.ec2_dmz_host, FLAGS.ec2_port)) + _confirm_rule("PREROUTING", '-t', 'nat', '-s', '0.0.0.0/0', + '-d', '169.254.169.254/32', '-p', 'tcp', '-m', 'tcp', + '--dport', '80', '-j', 'DNAT', + '--to-destination', '%s:%s' % (FLAGS.ec2_dmz_host, FLAGS.ec2_port)) def init_host(): """Basic networking setup goes here""" if FLAGS.use_nova_chains: - _execute("sudo iptables -N nova_input", check_exit_code=False) - _execute("sudo iptables -D %s -j nova_input" % FLAGS.input_chain, + _execute('sudo', 'iptables', '-N', 'nova_input', check_exit_code=False) + _execute('sudo', 'iptables', '-D', FLAGS.input_chain, + '-j', 'nova_input', check_exit_code=False) - _execute("sudo iptables -A %s -j nova_input" % FLAGS.input_chain) - - _execute("sudo iptables -N nova_forward", check_exit_code=False) - _execute("sudo iptables -D FORWARD -j nova_forward", + _execute('sudo', 'iptables', '-A', FLAGS.input_chain, + '-j', 'nova_input') + _execute('sudo', 'iptables', '-N', 'nova_forward', check_exit_code=False) - _execute("sudo iptables -A FORWARD -j nova_forward") - - _execute("sudo iptables -N nova_output", check_exit_code=False) - _execute("sudo iptables -D OUTPUT -j nova_output", + _execute('sudo', 'iptables', '-D', 'FORWARD', '-j', 'nova_forward', check_exit_code=False) - _execute("sudo iptables -A OUTPUT -j nova_output") - - _execute("sudo iptables -t nat -N nova_prerouting", + _execute('sudo', 'iptables', '-A', 'FORWARD', '-j', 'nova_forward') + _execute('sudo', 'iptables', '-N', 'nova_output', check_exit_code=False) + _execute('sudo', 'iptables', '-D', 'OUTPUT', '-j', 'nova_output', check_exit_code=False) - _execute("sudo iptables -t nat -D PREROUTING -j nova_prerouting", + _execute('sudo', 'iptables', '-A', 'OUTPUT', '-j', 'nova_output') + _execute('sudo', 'iptables', '-t', 'nat', '-N', 'nova_prerouting', check_exit_code=False) - _execute("sudo iptables -t nat -A PREROUTING -j nova_prerouting") - - _execute("sudo iptables -t nat -N nova_postrouting", + _execute('sudo', 'iptables', '-t', 'nat', '-D', 'PREROUTING', + '-j', 'nova_prerouting', check_exit_code=False) + _execute('sudo', 'iptables', '-t', 'nat', '-A', 'PREROUTING', + '-j', 'nova_prerouting') + _execute('sudo', 'iptables', '-t', 'nat', '-N', 'nova_postrouting', check_exit_code=False) - _execute("sudo iptables -t nat -D POSTROUTING -j nova_postrouting", + _execute('sudo', 'iptables', '-t', 'nat', '-D', 'POSTROUTING', + '-j', 'nova_postrouting', check_exit_code=False) + _execute('sudo', 'iptables', '-t', 'nat', '-A', 'POSTROUTING', + '-j', 'nova_postrouting') + _execute('sudo', 'iptables', '-t', 'nat', '-N', 'nova_snatting', check_exit_code=False) - _execute("sudo iptables -t nat -A POSTROUTING -j nova_postrouting") - - _execute("sudo iptables -t nat -N nova_snatting", + _execute('sudo', 'iptables', '-t', 'nat', '-D', 'POSTROUTING', + '-j nova_snatting', check_exit_code=False) + _execute('sudo', 'iptables', '-t', 'nat', '-A', 'POSTROUTING', + '-j', 'nova_snatting') + _execute('sudo', 'iptables', '-t', 'nat', '-N', 'nova_output', check_exit_code=False) - _execute("sudo iptables -t nat -D POSTROUTING -j nova_snatting", - check_exit_code=False) - _execute("sudo iptables -t nat -A POSTROUTING -j nova_snatting") - - _execute("sudo iptables -t nat -N nova_output", check_exit_code=False) - _execute("sudo iptables -t nat -D OUTPUT -j nova_output", - check_exit_code=False) - _execute("sudo iptables -t nat -A OUTPUT -j nova_output") + _execute('sudo', 'iptables', '-t', 'nat', '-D', 'OUTPUT', + '-j nova_output', check_exit_code=False) + _execute('sudo', 'iptables', '-t', 'nat', '-A', 'OUTPUT', + '-j', 'nova_output') else: # NOTE(vish): This makes it easy to ensure snatting rules always # come after the accept rules in the postrouting chain - _execute("sudo iptables -t nat -N SNATTING", - check_exit_code=False) - _execute("sudo iptables -t nat -D POSTROUTING -j SNATTING", + _execute('sudo', 'iptables', '-t', 'nat', '-N', 'SNATTING', check_exit_code=False) - _execute("sudo iptables -t nat -A POSTROUTING -j SNATTING") + _execute('sudo', 'iptables', '-t', 'nat', '-D', 'POSTROUTING', + '-j', 'SNATTING', check_exit_code=False) + _execute('sudo', 'iptables', '-t', 'nat', '-A', 'POSTROUTING', + '-j', 'SNATTING') # NOTE(devcamcar): Cloud public SNAT entries and the default # SNAT rule for outbound traffic. - _confirm_rule("SNATTING", "-t nat -s %s " - "-j SNAT --to-source %s" - % (FLAGS.fixed_range, FLAGS.routing_source_ip), append=True) + _confirm_rule("SNATTING", '-t', 'nat', '-s', FLAGS.fixed_range, + '-j', 'SNAT', '--to-source', FLAGS.routing_source_ip, + append=True) - _confirm_rule("POSTROUTING", "-t nat -s %s -d %s -j ACCEPT" % - (FLAGS.fixed_range, FLAGS.dmz_cidr)) - _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" % - {'range': FLAGS.fixed_range}) + _confirm_rule("POSTROUTING", '-t', 'nat', '-s', FLAGS.fixed_range, + '-d', FLAGS.dmz_cidr, '-j', 'ACCEPT') + _confirm_rule("POSTROUTING", '-t', 'nat', '-s', FLAGS.fixed_range, + '-d', FLAGS.fixed_range, '-j', 'ACCEPT') def bind_floating_ip(floating_ip, check_exit_code=True): """Bind ip to public interface""" - _execute("sudo ip addr add %s dev %s" % (floating_ip, - FLAGS.public_interface), + _execute('sudo', 'ip', 'addr', 'add', floating_ip, + 'dev', FLAGS.public_interface), check_exit_code=check_exit_code) def unbind_floating_ip(floating_ip): """Unbind a public ip from public interface""" - _execute("sudo ip addr del %s dev %s" % (floating_ip, - FLAGS.public_interface)) + _execute('sudo', 'ip', 'addr', 'del', floating_ip, + 'dev', FLAGS.public_interface)) def ensure_vlan_forward(public_ip, port, private_ip): """Sets up forwarding rules for vlan""" - _confirm_rule("FORWARD", "-d %s -p udp --dport 1194 -j ACCEPT" % - private_ip) - _confirm_rule("PREROUTING", - "-t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" - % (public_ip, port, private_ip)) + _confirm_rule("FORWARD", '-d', private_ip, '-p', 'udp', + '--dport', '1194', '-j', 'ACCEPT') + _confirm_rule("PREROUTING", '-t', 'nat', '-d', public_ip, '-p', 'udp', + '--dport', port, '-j', 'DNAT', '--to', '%s:1194' + % private_ip) def ensure_floating_forward(floating_ip, fixed_ip): """Ensure floating ip forwarding rule""" - _confirm_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s" - % (floating_ip, fixed_ip)) - _confirm_rule("OUTPUT", "-t nat -d %s -j DNAT --to %s" - % (floating_ip, fixed_ip)) - _confirm_rule("SNATTING", "-t nat -s %s -j SNAT --to %s" - % (fixed_ip, floating_ip)) + _confirm_rule("PREROUTING", '-t', 'nat', '-d', floating_ip, '-j', 'DNAT', + '--to', fixed_ip) + _confirm_rule("OUTPUT", '-t', 'nat', '-d', floating_ip, '-j', 'DNAT', + '--to', fixed_ip) + _confirm_rule("SNATTING", '-t', 'nat', '-s', fixed_ip, '-j', 'SNAT', + '--to', floating_ip) def remove_floating_forward(floating_ip, fixed_ip): """Remove forwarding for floating ip""" - _remove_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s" - % (floating_ip, fixed_ip)) - _remove_rule("OUTPUT", "-t nat -d %s -j DNAT --to %s" - % (floating_ip, fixed_ip)) - _remove_rule("SNATTING", "-t nat -s %s -j SNAT --to %s" - % (fixed_ip, floating_ip)) + _remove_rule("PREROUTING", '-t', 'nat', '-d', floating_ip, '-j', 'DNAT', + '--to', fixed_ip) + _remove_rule("OUTPUT", '-t', 'nat', '-d', floating_ip, '-j', 'DNAT', + '--to', fixed_ip) + _remove_rule("SNATTING", '-t', 'nat', '-s', fixed_ip, '-j', 'SNAT', + '--to', floating_ip) def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): @@ -185,9 +189,9 @@ def ensure_vlan(vlan_num): interface = "vlan%s" % vlan_num if not _device_exists(interface): LOG.debug(_("Starting VLAN inteface %s"), interface) - _execute("sudo vconfig set_name_type VLAN_PLUS_VID_NO_PAD") - _execute("sudo vconfig add %s %s" % (FLAGS.vlan_interface, vlan_num)) - _execute("sudo ip link set %s up" % interface) + _execute('sudo', 'vconfig', 'set_name_type', 'VLAN_PLUS_VID_NO_PAD') + _execute('sudo', 'vconfig', 'add', FLAGS.vlan_interface, vlan_num) + _execute('sudo', 'ip', 'link', 'set', interface, 'up') return interface @@ -206,52 +210,54 @@ def ensure_bridge(bridge, interface, net_attrs=None): """ if not _device_exists(bridge): LOG.debug(_("Starting Bridge interface for %s"), interface) - _execute("sudo brctl addbr %s" % bridge) - _execute("sudo brctl setfd %s 0" % bridge) + _execute('sudo', 'brctl', 'addbr', bridge) + _execute('sudo', 'brctl', 'setfd', bridge, 0) # _execute("sudo brctl setageing %s 10" % bridge) - _execute("sudo brctl stp %s off" % bridge) - _execute("sudo ip link set %s up" % bridge) + _execute('sudo', 'brctl', 'stp', bridge', 'off') + _execute('sudo', 'ip', 'link', 'set', bridge, up) if net_attrs: # NOTE(vish): The ip for dnsmasq has to be the first address on the # bridge for it to respond to reqests properly suffix = net_attrs['cidr'].rpartition('/')[2] - out, err = _execute("sudo ip addr add %s/%s brd %s dev %s" % - (net_attrs['gateway'], - suffix, - net_attrs['broadcast'], - bridge), + out, err = _execute('sudo', 'ip', 'addr', 'add', + "%s/%s" % + (net_attrs['gateway'], suffix), + 'brd', + net-attrs['broadcast'], + 'dev', + bridge, check_exit_code=False) if err and err != "RTNETLINK answers: File exists\n": raise exception.Error("Failed to add ip: %s" % err) if(FLAGS.use_ipv6): - _execute("sudo ip -f inet6 addr change %s dev %s" % - (net_attrs['cidr_v6'], bridge)) + _execute('sudo', 'ip', '-f', 'inet6', 'addr', + 'change', net_attrs['cidr_v6'], + 'dev', bridge) # NOTE(vish): If the public interface is the same as the # bridge, then the bridge has to be in promiscuous # to forward packets properly. if(FLAGS.public_interface == bridge): - _execute("sudo ip link set dev %s promisc on" % bridge) + _execute('sudo', 'ip', 'link', 'set', 'dev', bridge, 'promisc', 'on') if interface: # NOTE(vish): This will break if there is already an ip on the # interface, so we move any ips to the bridge gateway = None - out, err = _execute("sudo route -n") + out, err = _execute('sudo', 'route', '-n') for line in out.split("\n"): fields = line.split() if fields and fields[0] == "0.0.0.0" and fields[-1] == interface: gateway = fields[1] - out, err = _execute("sudo ip addr show dev %s scope global" % - interface) + out, err = _execute('sudo', 'ip', 'addr', 'show', 'dev', interface, + 'scope', 'global') for line in out.split("\n"): fields = line.split() if fields and fields[0] == "inet": params = ' '.join(fields[1:-1]) - _execute("sudo ip addr del %s dev %s" % (params, fields[-1])) - _execute("sudo ip addr add %s dev %s" % (params, bridge)) + _execute('sudo', 'ip', 'addr', 'del', params, 'dev', fields[-1]) + _execute('sudo', 'ip', 'addr', 'add', params, 'dev', bridge) if gateway: - _execute("sudo route add 0.0.0.0 gw %s" % gateway) - out, err = _execute("sudo brctl addif %s %s" % - (bridge, interface), + _execute('sudo', 'route', 'add', '0.0.0.0', 'gw', gateway) + out, err = _execute('sudo', 'brctl', 'addif, bridge, interface, check_exit_code=False) if (err and err != "device %s is already a member of a bridge; can't " @@ -259,18 +265,18 @@ def ensure_bridge(bridge, interface, net_attrs=None): raise exception.Error("Failed to add interface: %s" % err) if FLAGS.use_nova_chains: - (out, err) = _execute("sudo iptables -N nova_forward", + (out, err) = _execute('sudo', 'iptables', '-N', 'nova_forward, check_exit_code=False) if err != 'iptables: Chain already exists.\n': # NOTE(vish): chain didn't exist link chain - _execute("sudo iptables -D FORWARD -j nova_forward", + _execute('sudo', 'iptables, '-D', 'FORWARD', '-j', 'nova_forward', check_exit_code=False) - _execute("sudo iptables -A FORWARD -j nova_forward") + _execute('sudo', 'iptables', '-A', 'FORWARD', '-j', 'nova_forward') - _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) - _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge) - _execute("sudo iptables -N nova-local", check_exit_code=False) - _confirm_rule("FORWARD", "-j nova-local") + _confirm_rule("FORWARD", '--in-interface', bridge, '-j', 'ACCEPT') + _confirm_rule("FORWARD", '--out-interface', bridge, '-j', 'ACCEPT') + _execute('sudo', 'iptables', '-N', 'nova-local', check_exit_code=False) + _confirm_rule("FORWARD", '-j', 'nova-local') def get_dhcp_hosts(context, network_id): @@ -304,11 +310,11 @@ def update_dhcp(context, network_id): # if dnsmasq is already running, then tell it to reload if pid: - out, _err = _execute('cat /proc/%d/cmdline' % pid, + out, _err = _execute('cat', "/proc/%d/cmdline" % pid, check_exit_code=False) if conffile in out: try: - _execute('sudo kill -HUP %d' % pid) + _execute('sudo', 'kill', '-HUP', pid) return except Exception as exc: # pylint: disable-msg=W0703 LOG.debug(_("Hupping dnsmasq threw %s"), exc) @@ -349,11 +355,11 @@ interface %s # if radvd is already running, then tell it to reload if pid: - out, _err = _execute('cat /proc/%d/cmdline' + out, _err = _execute('cat', "/proc/%d/cmdline' % pid, check_exit_code=False) if conffile in out: try: - _execute('sudo kill %d' % pid) + _execute('sudo', 'kill', pid) except Exception as exc: # pylint: disable-msg=W0703 LOG.debug(_("killing radvd threw %s"), exc) else: @@ -374,23 +380,23 @@ def _host_dhcp(fixed_ip_ref): fixed_ip_ref['address']) -def _execute(cmd, *args, **kwargs): +def _execute(*cmd, **kwargs): """Wrapper around utils._execute for fake_network""" if FLAGS.fake_network: - LOG.debug("FAKE NET: %s", cmd) + LOG.debug("FAKE NET: %s", ' '.join(cmd)) return "fake", 0 else: - return utils.execute(cmd, *args, **kwargs) + return utils.execute(*cmd, **kwargs) def _device_exists(device): """Check if ethernet device exists""" - (_out, err) = _execute("ip link show dev %s" % device, + (_out, err) = _execute('ip', 'link', 'show', 'dev', device, check_exit_code=False) return not err -def _confirm_rule(chain, cmd, append=False): +def _confirm_rule(chain, *cmd, append=False): """Delete and re-add iptables rule""" if FLAGS.use_nova_chains: chain = "nova_%s" % chain.lower() @@ -398,16 +404,16 @@ def _confirm_rule(chain, cmd, append=False): loc = "-A" else: loc = "-I" - _execute("sudo iptables --delete %s %s" % (chain, cmd), + _execute('sudo', 'iptables', '--delete', chain, *cmd, check_exit_code=False) - _execute("sudo iptables %s %s %s" % (loc, chain, cmd)) + _execute('sudo', 'iptables', loc, chain, *cmd) -def _remove_rule(chain, cmd): +def _remove_rule(chain, *cmd): """Remove iptables rule""" if FLAGS.use_nova_chains: chain = "%s" % chain.lower() - _execute("sudo iptables --delete %s %s" % (chain, cmd)) + _execute('sudo', 'iptables', '--delete', chain, *cmd) def _dnsmasq_cmd(net): @@ -444,7 +450,7 @@ def _stop_dnsmasq(network): if pid: try: - _execute('sudo kill -TERM %d' % pid) + _execute('sudo', 'kill', '-TERM', pid) except Exception as exc: # pylint: disable-msg=W0703 LOG.debug(_("Killing dnsmasq threw %s"), exc) diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index ce1c77210..6d2d8b771 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -343,13 +343,13 @@ def lease_ip(private_ip): private_ip) instance_ref = db.fixed_ip_get_instance(context.get_admin_context(), private_ip) - cmd = "%s add %s %s fake" % (binpath('nova-dhcpbridge'), - instance_ref['mac_address'], - private_ip) + cmd = (binpath('nova-dhcpbridge'), 'add' + instance_ref['mac_address'], + private_ip, 'fake') env = {'DNSMASQ_INTERFACE': network_ref['bridge'], 'TESTING': '1', 'FLAGFILE': FLAGS.dhcpbridge_flagfile} - (out, err) = utils.execute(cmd, addl_env=env) + (out, err) = utils.execute(*cmd, addl_env=env) LOG.debug("ISSUE_IP: %s, %s ", out, err) @@ -359,11 +359,11 @@ def release_ip(private_ip): private_ip) instance_ref = db.fixed_ip_get_instance(context.get_admin_context(), private_ip) - cmd = "%s del %s %s fake" % (binpath('nova-dhcpbridge'), - instance_ref['mac_address'], - private_ip) + cmd = (binpath('nova-dhcpbridge'), 'del', + instance_ref['mac_address'], + private_ip, 'fake') env = {'DNSMASQ_INTERFACE': network_ref['bridge'], 'TESTING': '1', 'FLAGFILE': FLAGS.dhcpbridge_flagfile} - (out, err) = utils.execute(cmd, addl_env=env) + (out, err) = utils.execute(*cmd, addl_env=env) LOG.debug("RELEASE_IP: %s, %s ", out, err) diff --git a/nova/utils.py b/nova/utils.py index 40a8d8d8c..c96b85294 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -125,15 +125,15 @@ def fetchfile(url, target): # c.perform() # c.close() # fp.close() - execute("curl","--fail",url,"-o",target) + execute("curl", "--fail", url, "-o", target) -def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): - LOG.debug(_("Running cmd (subprocess): %s"), cmd) +def execute(*cmd, process_input=None, addl_env=None, check_exit_code=True): + LOG.debug(_("Running cmd (subprocess): %s"), ' '.join(cmd)) env = os.environ.copy() if addl_env: env.update(addl_env) - obj = subprocess.Popen(cmd, stdin=subprocess.PIPE, + obj = subprocess.Popen(*cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) result = None if process_input != None: @@ -148,7 +148,7 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): raise ProcessExecutionError(exit_code=obj.returncode, stdout=stdout, stderr=stderr, - cmd=cmd) + cmd=' '.join(cmd)) # NOTE(termie): this appears to be necessary to let the subprocess call # clean something up in between calls, without it two # execute calls in a row hangs the second one @@ -158,7 +158,7 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): def ssh_execute(ssh, cmd, process_input=None, addl_env=None, check_exit_code=True): - LOG.debug(_("Running cmd (SSH): %s"), cmd) + LOG.debug(_("Running cmd (SSH): %s"), ' '.join(cmd)) if addl_env: raise exception.Error("Environment not supported over SSH") @@ -187,7 +187,7 @@ def ssh_execute(ssh, cmd, process_input=None, raise exception.ProcessExecutionError(exit_code=exit_status, stdout=stdout, stderr=stderr, - cmd=cmd) + cmd=' '.join(cmd)) return (stdout, stderr) @@ -254,7 +254,7 @@ def last_octet(address): def get_my_linklocal(interface): try: - if_str = execute("ip","-f","inet6","-o","addr","show", interface) + if_str = execute("ip", "-f", "inet6", "-o", "addr", "show", interface) condition = "\s+inet6\s+([0-9a-f:]+)/\d+\s+scope\s+link" links = [re.search(condition, x) for x in if_str[0].split('\n')] address = [w.group(1) for w in links if w is not None] diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 2bded07a4..203517275 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -49,10 +49,10 @@ def extend(image, size): file_size = os.path.getsize(image) if file_size >= size: return - utils.execute('truncate -s %s %s' % (size, image)) + utils.execute('truncate', '-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) + utils.execute('e2fsck', '-fp', mage, check_exit_code=False) + utils.execute('resize2fs', image, check_exit_code=False) def inject_data(image, key=None, net=None, partition=None, nbd=False): @@ -68,7 +68,7 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): try: if not partition is None: # create partition - out, err = utils.execute('sudo kpartx -a %s' % device) + out, err = utils.execute('sudo', 'kpartx', '-a', device) if err: raise exception.Error(_('Failed to load partition: %s') % err) mapped_device = '/dev/mapper/%sp%s' % (device.split('/')[-1], @@ -84,13 +84,14 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): 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) + out, err = utils.execute('sudo', 'tune2fs', + '-c', 0, '-i', 0, mapped_device) tmpdir = tempfile.mkdtemp() try: # mount loopback to dir out, err = utils.execute( - 'sudo mount %s %s' % (mapped_device, tmpdir)) + 'sudo', 'mount', mapped_device, tmpdir) if err: raise exception.Error(_('Failed to mount filesystem: %s') % err) @@ -103,13 +104,13 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): _inject_net_into_fs(net, tmpdir) finally: # unmount device - utils.execute('sudo umount %s' % mapped_device) + utils.execute('sudo', 'umount', mapped_device) finally: # remove temporary directory - utils.execute('rmdir %s' % tmpdir) + utils.execute('rmdir', tmpdir) if not partition is None: # remove partitions - utils.execute('sudo kpartx -d %s' % device) + utils.execute('sudo', 'kpartx', '-d', device) finally: _unlink_device(device, nbd) @@ -118,7 +119,7 @@ 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)) + utils.execute('sudo', 'qemu-nbd', '-c', device, image) # NOTE(vish): this forks into another process, so give it a chance # to set up before continuuing for i in xrange(FLAGS.timeout_nbd): @@ -127,7 +128,7 @@ def _link_device(image, nbd): time.sleep(1) raise exception.Error(_('nbd device %s did not show up') % device) else: - out, err = utils.execute('sudo losetup --find --show %s' % image) + out, err = utils.execute('sudo', 'losetup', '--find', '--show', image) if err: raise exception.Error(_('Could not attach image to loopback: %s') % err) @@ -137,10 +138,10 @@ def _link_device(image, nbd): def _unlink_device(device, nbd): """Unlink image from device using loopback or nbd""" if nbd: - utils.execute('sudo qemu-nbd -d %s' % device) + utils.execute('sudo', 'qemu-nbd', '-d', device) _free_device(device) else: - utils.execute('sudo losetup --detach %s' % device) + utils.execute('sudo', 'losetup', '--detach', device) _DEVICES = ['/dev/nbd%s' % i for i in xrange(FLAGS.max_nbd_devices)] @@ -170,11 +171,12 @@ def _inject_key_into_fs(key, fs): 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) + utils.execute('sudo', 'mkdir', '-p', sshdir) # existing dir doesn't matter + utils.execute('sudo', 'chown', 'root', sshdir) + utils.execute('sudo', 'chmod', '700', sshdir) keyfile = os.path.join(sshdir, 'authorized_keys') - utils.execute('sudo tee -a %s' % keyfile, '\n' + key.strip() + '\n') + # TODO:EWINDISCH: not sure about the following /w execv patch + utils.execute('sudo', 'tee', '-a', keyfile, '\n' + key.strip() + '\n') def _inject_net_into_fs(net, fs): @@ -183,8 +185,8 @@ def _inject_net_into_fs(net, 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) + utils.execute('sudo', 'mkdir', '-p', netdir) # existing dir doesn't matter + utils.execute('sudo', 'chown', 'root:root', netdir) + utils.execute('sudo', 'chmod', 755, netdir) netfile = os.path.join(netdir, 'interfaces') - utils.execute('sudo tee %s' % netfile, net) + utils.execute('sudo', 'tee', netfile, net) diff --git a/nova/virt/images.py b/nova/virt/images.py index 7a6fef330..4b11d1667 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -94,8 +94,7 @@ def _fetch_s3_image(image, path, user, project): cmd += ['-H', '\'%s: %s\'' % (k, v)] cmd += ['-o', path] - cmd_out = ' '.join(cmd) - return utils.execute(cmd_out) + return utils.execute(*cmd) def _fetch_local_image(image, path, user, project): @@ -103,7 +102,7 @@ def _fetch_local_image(image, path, user, project): if sys.platform.startswith('win'): return shutil.copy(source, path) else: - return utils.execute('cp %s %s' % (source, path)) + return utils.execute('cp', source, path) def _image_path(path): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4e0fd106f..464ec475c 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -438,8 +438,10 @@ class LibvirtConnection(object): if virsh_output.startswith('/dev/'): LOG.info(_("cool, it's a device")) - out, err = utils.execute("sudo dd if=%s iflag=nonblock" % - virsh_output, check_exit_code=False) + out, err = utils.execute('sudo', 'dd', + "if=%s" % virsh_output, + 'iflag=nonblock', + check_exit_code=False) return out else: return '' @@ -461,11 +463,11 @@ class LibvirtConnection(object): console_log = os.path.join(FLAGS.instances_path, instance['name'], 'console.log') - utils.execute('sudo chown %d %s' % (os.getuid(), console_log)) + utils.execute('sudo', 'chown', s.getuid(), console_log) if FLAGS.libvirt_type == 'xen': # Xen is special - virsh_output = utils.execute("virsh ttyconsole %s" % + virsh_output = utils.execute('virsh', 'ttyconsole', instance['name']) data = self._flush_xen_console(virsh_output) fpath = self._append_to_file(data, console_log) @@ -482,7 +484,10 @@ class LibvirtConnection(object): port = random.randint(int(start_port), int(end_port)) # netcat will exit with 0 only if the port is in use, # so a nonzero return value implies it is unused - cmd = 'netcat 0.0.0.0 %s -w 1 </dev/null || echo free' % (port) + + # TODO:ewindisch: subprocess lets us do this... + # but utils.execute abstracts it away from us + cmd = 'netcat', '0.0.0.0', port, '-w', '1', '</dev/null || echo free' % (port) stdout, stderr = utils.execute(cmd) if stdout.strip() == 'free': return port @@ -533,11 +538,11 @@ class LibvirtConnection(object): if not os.path.exists(base): fn(target=base, *args, **kwargs) if cow: - utils.execute('qemu-img create -f qcow2 -o ' - 'cluster_size=2M,backing_file=%s %s' - % (base, target)) + utils.execute('qemu-img', 'create', '-f', 'qcow2', "'-o'', + "cluster_size=2M,backing_file=%s" % base, + target) else: - utils.execute('cp %s %s' % (base, target)) + utils.execute('cp', base, target) def _fetch_image(self, target, image_id, user, project, size=None): """Grab image and optionally attempt to resize it""" @@ -547,7 +552,7 @@ class LibvirtConnection(object): def _create_local(self, target, local_gb): """Create a blank image of specified size""" - utils.execute('truncate %s -s %dG' % (target, local_gb)) + utils.execute('truncate', target, '-s', "%dG" local_gb) # TODO(vish): should we format disk by default? def _create_image(self, inst, libvirt_xml, suffix='', disk_images=None): @@ -558,7 +563,7 @@ class LibvirtConnection(object): fname + suffix) # ensure directories exist and are writable - utils.execute('mkdir -p %s' % basepath(suffix='')) + utils.execute('mkdir', '-p', basepath(suffix='') LOG.info(_('instance %s: Creating image'), inst['name']) f = open(basepath('libvirt.xml'), 'w') @@ -658,7 +663,7 @@ class LibvirtConnection(object): ' data into image %(img_id)s (%(e)s)') % locals()) if FLAGS.libvirt_type == 'uml': - utils.execute('sudo chown root %s' % basepath('disk')) + utils.execute('sudo', 'chown', 'root', basepath('disk')) def to_xml(self, instance, rescue=False): # TODO(termie): cache? @@ -1240,13 +1245,14 @@ class IptablesFirewallDriver(FirewallDriver): current_filter, _ = self.execute('sudo iptables-save -t filter') current_lines = current_filter.split('\n') new_filter = self.modify_rules(current_lines, 4) - self.execute('sudo iptables-restore', + self.execute('sudo', 'iptables-restore', process_input='\n'.join(new_filter)) if(FLAGS.use_ipv6): - current_filter, _ = self.execute('sudo ip6tables-save -t filter') + current_filter, _ = self.execute('sudo', 'ip6tables-save', + '-t', 'filter') current_lines = current_filter.split('\n') new_filter = self.modify_rules(current_lines, 6) - self.execute('sudo ip6tables-restore', + self.execute('sudo', 'ip6tables-restore', process_input='\n'.join(new_filter)) def modify_rules(self, current_lines, ip_version=4): diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 564a25057..873dfce5e 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -736,13 +736,14 @@ def _write_partition(virtual_size, dev): LOG.debug(_('Writing partition table %(primary_first)d %(primary_last)d' ' to %(dest)s...') % locals()) - def execute(cmd, process_input=None, check_exit_code=True): - return utils.execute(cmd=cmd, + def execute(*cmd, process_input=None, check_exit_code=True): + return utils.execute(*cmd, process_input=process_input, check_exit_code=check_exit_code) - execute('parted --script %s mklabel msdos' % dest) - execute('parted --script %s mkpart primary %ds %ds' % - (dest, primary_first, primary_last)) + execute('parted', '--script', dest, 'mklabel', 'msdos') + execute('parted', '--script', dest, 'mkpart', 'primary', + '%ds' % primary_first, + '%ds' % primary_last) LOG.debug(_('Writing partition table %s done.'), dest) diff --git a/nova/volume/driver.py b/nova/volume/driver.py index e73202b73..220c9ef9d 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -65,14 +65,14 @@ class VolumeDriver(object): self._execute = execute self._sync_exec = sync_exec - def _try_execute(self, command): + def _try_execute(self, *command): # NOTE(vish): Volume commands can partially fail due to timing, but # running them a second time on failure will usually # recover nicely. tries = 0 while True: try: - self._execute(command) + self._execute(*command) return True except exception.ProcessExecutionError: tries = tries + 1 @@ -84,7 +84,7 @@ class VolumeDriver(object): def check_for_setup_error(self): """Returns an error if prerequisites aren't met""" - out, err = self._execute("sudo vgs --noheadings -o name") + out, err = self._execute('sudo', 'vgs', '--noheadings', '-o', 'name') volume_groups = out.split() if not FLAGS.volume_group in volume_groups: raise exception.Error(_("volume group %s doesn't exist") @@ -97,21 +97,21 @@ class VolumeDriver(object): sizestr = '100M' else: sizestr = '%sG' % volume['size'] - self._try_execute('sudo','lvcreate','-L',sizestr,'-n', + self._try_execute('sudo', 'lvcreate', '-L', sizestr, '-n', volume['name'], FLAGS.volume_group) def delete_volume(self, volume): """Deletes a logical volume.""" try: - self._try_execute('sudo','lvdisplay','%s/%s" % + self._try_execute('sudo', 'lvdisplay', '%s/%s" % (FLAGS.volume_group, volume['name'])) except Exception as e: # If the volume isn't present, then don't attempt to delete return True - self._try_execute('sudo','lvremove','-f',"%s/%s" % + self._try_execute('sudo', 'lvremove', '-f',"%s/%s" % (FLAGS.volume_group, volume['name'])) @@ -167,7 +167,7 @@ class AOEDriver(VolumeDriver): blade_id) = self.db.volume_allocate_shelf_and_blade(context, volume['id']) self._try_execute( - 'sudo','vblade-persist','setup', + 'sudo', 'vblade-persist', 'setup', shelf_id, blade_id, FLAGS.aoe_eth_dev, @@ -182,9 +182,9 @@ class AOEDriver(VolumeDriver): # just wait a bit for the current volume to # be ready and ignore any errors. time.sleep(2) - self._execute('sudo','vblade-persist','auto','all', + self._execute('sudo', 'vblade-persist', 'auto', 'all', check_exit_code=False) - self._execute('sudo','vblade-persist','start','all', + self._execute('sudo', 'vblade-persist', 'start', 'all', check_exit_code=False) def remove_export(self, context, volume): @@ -192,15 +192,15 @@ class AOEDriver(VolumeDriver): (shelf_id, blade_id) = self.db.volume_get_shelf_and_blade(context, volume['id']) - self._try_execute('sudo','vblade-persist','stop', + self._try_execute('sudo', 'vblade-persist', 'stop', shelf_id, blade_id) - self._try_execute('sudo','vblade-persist','destroy', + self._try_execute('sudo', 'vblade-persist', 'destroy', shelf_id, blade_id) def discover_volume(self, _volume): """Discover volume on a remote host.""" - self._execute('sudo','aoe-discover') - self._execute('sudo','aoe-stat', check_exit_code=False) + self._execute('sudo', 'aoe-discover') + self._execute('sudo', 'aoe-stat', check_exit_code=False) def undiscover_volume(self, _volume): """Undiscover volume on a remote host.""" @@ -252,12 +252,12 @@ class ISCSIDriver(VolumeDriver): iscsi_name = "%s%s" % (FLAGS.iscsi_target_prefix, volume['name']) volume_path = "/dev/%s/%s" % (FLAGS.volume_group, volume['name']) - self._sync_exec('sudo','ietadm','--op','new', + self._sync_exec('sudo', 'ietadm', '--op', 'new', "--tid=%s" % iscsi_target, '--params', "Name=%s" % iscsi-name, check_exit_code=False) - self._sync_exec('sudo','ietadm','--op','new', + self._sync_exec('sudo', 'ietadm', '--op', 'new', "--tid=%s" % iscsi_target, '--lun=0', '--params', @@ -282,12 +282,13 @@ class ISCSIDriver(VolumeDriver): volume['host']) iscsi_name = "%s%s" % (FLAGS.iscsi_target_prefix, volume['name']) volume_path = "/dev/%s/%s" % (FLAGS.volume_group, volume['name']) - self._execute("sudo ietadm --op new " - "--tid=%s --params Name=%s" % + self._execute('sudo', 'ietadm', '--op', 'new', + '--tid=%s --params Name=%s' % (iscsi_target, iscsi_name)) - self._execute("sudo ietadm --op new --tid=%s " - "--lun=0 --params Path=%s,Type=fileio" % - (iscsi_target, volume_path)) + self._execute('sudo', 'ietadm', '--op', 'new', + '--tid=%s' % iscsi_target, + '--lun=0', '--params', + 'Path=%s,Type=fileio' % volume_path) def remove_export(self, context, volume): """Removes an export for a logical volume.""" @@ -302,16 +303,18 @@ class ISCSIDriver(VolumeDriver): try: # ietadm show will exit with an error # this export has already been removed - self._execute("sudo ietadm --op show --tid=%s " % iscsi_target) + self._execute('sudo', 'ietadm', '--op', 'show', + '--tid=%s' % iscsi_target) except Exception as e: LOG.info(_("Skipping remove_export. No iscsi_target " + "is presently exported for volume: %d"), volume['id']) return - self._execute("sudo ietadm --op delete --tid=%s " - "--lun=0" % iscsi_target) - self._execute("sudo ietadm --op delete --tid=%s" % - iscsi_target) + self._execute('sudo', 'ietadm', '--op', 'delete', + '--tid=%s' % iscsi_target, + '--lun=0') + self._execute('sudo', 'ietadm', '--op', 'delete', + '--tid=%s' % iscsi_target) def _do_iscsi_discovery(self, volume): #TODO(justinsb): Deprecate discovery and use stored info @@ -320,8 +323,8 @@ class ISCSIDriver(VolumeDriver): volume_name = volume['name'] - (out, _err) = self._execute("sudo iscsiadm -m discovery -t " - "sendtargets -p %s" % (volume['host'])) + (out, _err) = self._execute('sudo', 'iscsiadm', '-m', 'discovery', + '-t', 'sendtargets', '-p', volume['host']) for target in out.splitlines(): if FLAGS.iscsi_ip_prefix in target and volume_name in target: return target @@ -481,7 +484,7 @@ class RBDDriver(VolumeDriver): def check_for_setup_error(self): """Returns an error if prerequisites aren't met""" - (stdout, stderr) = self._execute("rados lspools") + (stdout, stderr) = self._execute('rados', 'lspools') pools = stdout.split("\n") if not FLAGS.rbd_pool in pools: raise exception.Error(_("rbd has no pool %s") % @@ -493,12 +496,12 @@ class RBDDriver(VolumeDriver): size = 100 else: size = int(volume['size']) * 1024 - self._try_execute('rbd','--pool',FLAGS.rbd_pool, - '--size', size,'create', volume['name']) + self._try_execute('rbd', '--pool', FLAGS.rbd_pool, + '--size', size, 'create', volume['name']) def delete_volume(self, volume): """Deletes a logical volume.""" - self._try_execute('rbd','--pool',FLAGS.rbd_pool, + self._try_execute('rbd', '--pool', FLAGS.rbd_pool, 'rm', voluname['name']) def local_path(self, volume): @@ -534,7 +537,7 @@ class SheepdogDriver(VolumeDriver): def check_for_setup_error(self): """Returns an error if prerequisites aren't met""" try: - (out, err) = self._execute('collie','cluster','info') + (out, err) = self._execute('collie', 'cluster', 'info') if not out.startswith('running'): raise exception.Error(_("Sheepdog is not working: %s") % out) except exception.ProcessExecutionError: @@ -546,13 +549,13 @@ class SheepdogDriver(VolumeDriver): sizestr = '100M' else: sizestr = '%sG' % volume['size'] - self._try_execute('qemu-img','create', + self._try_execute('qemu-img', 'create', "sheepdog:%s" %s" % volume['name'], sizestr) def delete_volume(self, volume): """Deletes a logical volume""" - self._try_execute('collie','vdi','delete',volume['name']) + self._try_execute('collie', 'vdi', 'delete', volume['name']) def local_path(self, volume): return "sheepdog:%s" % volume['name'] diff --git a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py index d60816ce7..2c34f7b1d 100755 --- a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py +++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py @@ -30,13 +30,14 @@ import simplejson as json def main(dom_id, command, only_this_vif=None): - xsls = execute("/usr/bin/xenstore-ls /local/domain/%s/vm-data/networking" \ - % dom_id, True) + xsls = execute('/usr/bin/xenstore-ls', + '/local/domain/%s/vm-data/networking' % dom_id, True) macs = [line.split("=")[0].strip() for line in xsls.splitlines()] for mac in macs: - xsr = "/usr/bin/xenstore-read /local/domain/%s/vm-data/networking/%s" - xsread = execute(xsr % (dom_id, mac), True) + xsread = execute('/usr/bin/enstore-read', + '/local/domain/%s/vm-data/networking/%s' % + (dom_id, mac), True) data = json.loads(xsread) for ip in data['ips']: if data["label"] == "public": @@ -53,7 +54,7 @@ def main(dom_id, command, only_this_vif=None): def execute(command, return_stdout=False): devnull = open(os.devnull, 'w') - proc = subprocess.Popen(command, shell=True, close_fds=True, + proc = subprocess.Popen(command, close_fds=True, stdout=subprocess.PIPE, stderr=devnull) devnull.close() if return_stdout: @@ -67,45 +68,69 @@ def execute(command, return_stdout=False): def apply_iptables_rules(command, params): - iptables = lambda rule: execute("/sbin/iptables %s" % rule) + iptables = lambda *rule: execute('/sbin/iptables', *rule) - iptables("-D FORWARD -m physdev --physdev-in %(VIF)s -s %(IP)s \ - -j ACCEPT" % params) + iptables('-D', 'FORWARD', '-m', 'physdev', + '--physdev-in', '%(VIF)s' % params, + '-s', '%(IP)s' % params, + '-j', 'ACCEPT') if command == 'online': - iptables("-A FORWARD -m physdev --physdev-in %(VIF)s -s %(IP)s \ - -j ACCEPT" % params) + iptables('-A', 'FORWARD', '-m', 'physdev', + '--physdev-in', '%(VIF)s' % params, + '-s', '%(IP)s' % params, + '-j', 'ACCEPT') def apply_arptables_rules(command, params): - arptables = lambda rule: execute("/sbin/arptables %s" % rule) - - arptables("-D FORWARD --opcode Request --in-interface %(VIF)s \ - --source-ip %(IP)s --source-mac %(MAC)s -j ACCEPT" % params) - arptables("-D FORWARD --opcode Reply --in-interface %(VIF)s \ - --source-ip %(IP)s --source-mac %(MAC)s -j ACCEPT" % params) + arptables = lambda *rule: execute('/sbin/arptables', *rule) + + arptables('-D', 'FORWARD', '--opcode', 'Request', + '--in-interface', '%(VIF)s' % params, + '--source-ip', '%(IP)s' % params, + '--source-mac', '%(MAC)s' % params, + '-j', 'ACCEPT') + arptables('-D', 'FORWARD', '--opcode', 'Reply', + '--in-interface', '%(VIF)s' % params, + '--source-ip', '%(IP)s' % params, + '--source-mac', '%(MAC)s' % params, + '-j', 'ACCEPT') if command == 'online': - arptables("-A FORWARD --opcode Request --in-interface %(VIF)s \ - --source-ip %(IP)s --source-mac %(MAC)s -j ACCEPT" % params) - arptables("-A FORWARD --opcode Reply --in-interface %(VIF)s \ - --source-ip %(IP)s --source-mac %(MAC)s -j ACCEPT" % params) + arptables('-A', 'FORWARD', '--opcode', 'Request', + '--in-interface', '%(VIF)s' % params + '--source-ip', '%(IP)s' % params, + '--source-mac', '%(MAC)s' % params, + '-j', 'ACCEPT') + arptables('-A', 'FORWARD', '--opcode', 'Reply', + '--in-interface', '%(VIF)s' % params, + '--source-ip', '%(IP)s' % params, + '--source-mac', '%(MAC)s' % params, + '-j', 'ACCEPT') def apply_ebtables_rules(command, params): - ebtables = lambda rule: execute("/sbin/ebtables %s" % rule) - - ebtables("-D FORWARD -p 0806 -o %(VIF)s --arp-ip-dst %(IP)s -j ACCEPT" % - params) - ebtables("-D FORWARD -p 0800 -o %(VIF)s --ip-dst %(IP)s -j ACCEPT" % - params) + ebtables = lambda *rule: execute("/sbin/ebtables", *rule) + + ebtables('-D', 'FORWARD', '-p', '0806', '-o', '%(VIF)s' % params, + '--arp-ip-dst', '%(IP)s' % params, + '-j', 'ACCEPT') + ebtables('-D', 'FORWARD', '-p', '0800', '-o', + '%(VIF)s' % params, '--ip-dst', '%(IP)s' % params, + '-j', 'ACCEPT') if command == 'online': - ebtables("-A FORWARD -p 0806 -o %(VIF)s --arp-ip-dst %(IP)s \ - -j ACCEPT" % params) - ebtables("-A FORWARD -p 0800 -o %(VIF)s --ip-dst %(IP)s \ - -j ACCEPT" % params) - - ebtables("-D FORWARD -s ! %(MAC)s -i %(VIF)s -j DROP" % params) + ebtables('-A', 'FORWARD', '-p', '0806', + '-o', '%(VIF)s' % params + '--arp-ip-dst', '%(IP)s' % params, + '-j', 'ACCEPT') + ebtables('-A', 'FORWARD', '-p', '0800', + '-o', '%(VIF)s' % params, + '--ip-dst', '%(IP)s' % params, + '-j', 'ACCEPT') + + ebtables('-D', 'FORWARD', '-s', '!', '%(MAC)s' % params, + '-i', '%(VIF)s' % params, '-j', 'DROP') if command == 'online': - ebtables("-I FORWARD 1 -s ! %(MAC)s -i %(VIF)s -j DROP" % params) + ebtables('-I', 'FORWARD', '1', '-s', '!', '%(MAC)s' % params, + '-i', '%(VIF)s', '-j', 'DROP') if __name__ == "__main__": -- cgit From 5fcf84f19c94f96d8f23d8b673ed3f4977f9189d Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Tue, 8 Mar 2011 01:08:13 -0500 Subject: Fix todo comment --- nova/virt/libvirt_conn.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index e1cd75306..a5256bbc2 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -486,8 +486,9 @@ class LibvirtConnection(object): # netcat will exit with 0 only if the port is in use, # so a nonzero return value implies it is unused - # TODO:ewindisch: subprocess lets us do this... - # but utils.execute abstracts it away from us + # TODO(ewindisch): broken /w execvp patch. + # subprocess lets us do this, but utils.execute + # abstracts it away from us cmd = 'netcat', '0.0.0.0', port, '-w', '1', '</dev/null || echo free' % (port) stdout, stderr = utils.execute(cmd) if stdout.strip() == 'free': -- cgit From e81294b94e3bc8708bd4777b685a7d302594557e Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz <emaildericky@gmail.com> Date: Tue, 8 Mar 2011 18:53:20 +0100 Subject: Added ability to remove networks on nova-manage command --- bin/nova-manage | 6 +++++- nova/db/api.py | 7 +++++++ nova/db/sqlalchemy/api.py | 7 +++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/bin/nova-manage b/bin/nova-manage index 94b0d5946..8ddfea5c2 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -548,7 +548,11 @@ class NetworkCommands(object): def delete(self, fixed_range): """Deletes a network""" network = db.network_get_by_cidr(context.get_admin_context(), fixed_range) - + if network.project_id is not None: + raise ValueError(_('Network must be disassociated from project %s' + ' before delete' %network.project_id)) + db.network_delete_safe(context.get_admin_context(), network.id) + class ServiceCommands(object): """Enable and disable running services""" diff --git a/nova/db/api.py b/nova/db/api.py index 04f5fd72f..7ad99c1f4 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -488,6 +488,13 @@ def network_create_safe(context, values): """ return IMPL.network_create_safe(context, values) +def network_delete_safe(context, network_id): + """Delete network with key network_id + + This method assumes that the network is not associated with any project + """ + return IMPL.network_delete_safe(context, network_id) + def network_create_fixed_ips(context, network_id, num_vpn_clients): """Create the ips for the network, reserving sepecified ips.""" diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index c8f42425d..90730d325 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1042,6 +1042,13 @@ def network_create_safe(context, values): except IntegrityError: return None +@require_admin_context +def network_delete_safe(context, network_id): + session = get_session() + with session.begin(): + network_ref = network_get(context, network_id=network_id, session=session) + session.delete(network_ref) + @require_admin_context def network_disassociate(context, network_id): -- cgit From 1caceddf431a1ad1ef22235c2206bccf39fde5c5 Mon Sep 17 00:00:00 2001 From: Cerberus <matt.dietz@rackspace.com> Date: Tue, 8 Mar 2011 14:24:01 -0600 Subject: Nits --- nova/virt/xenapi/vmops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7fe1f6ff0..86dbf251b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -90,7 +90,7 @@ class VMOps(object): vm = VMHelper.lookup(self._session, instance_name) if vm is not None: raise exception.Duplicate(_('Attempted to create' - ' non-unique name %s') % instance_name) + ' non-unique name %s') % instance_name) #ensure enough free memory is available if not VMHelper.ensure_free_mem(self._session, instance): @@ -104,7 +104,7 @@ class VMOps(object): user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) - vdi_ref = kernel = ramdisk = pv_kernel = None + kernel = ramdisk = pv_kernel = None # Are we building from a pre-existing disk? vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) -- cgit From 4517117a71c03526aca8f245a70760c45e5214c0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Tue, 8 Mar 2011 20:24:48 +0000 Subject: modify nova manage doc --- doc/source/runnova/nova.manage.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/doc/source/runnova/nova.manage.rst b/doc/source/runnova/nova.manage.rst index 0e9a29b6b..0636e5752 100644 --- a/doc/source/runnova/nova.manage.rst +++ b/doc/source/runnova/nova.manage.rst @@ -182,6 +182,29 @@ Nova Floating IPs Displays a list of all floating IP addresses. +Nova Images +~~~~~~~~~~~ + +``nova-manage image image_register <path> <owner>`` + + Registers an image with the image service. + +``nova-manage image kernel_register <path> <owner>`` + + Registers a kernel with the image service. + +``nova-manage image ramdisk_register <path> <owner>`` + + Registers a ramdisk with the image service. + +``nova-manage image all_register <image_path> <kernel_path> <ramdisk_path> <owner>`` + + Registers an image kernel and ramdisk with the image service. + +``nova-manage image convert <directory>`` + + Converts all images in directory from the old (Bexar) format to the new format. + Concept: Flags -------------- -- cgit From 23d3be4b6f28359211e29212867157daeac9e142 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Tue, 8 Mar 2011 20:25:05 +0000 Subject: update code to work with new container and disk formats from glance --- bin/nova-manage | 45 ++++++++++++++++++++++++++++++--------------- nova/api/ec2/cloud.py | 9 ++++++--- nova/image/s3.py | 7 ++++++- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index b97d8b81d..b8e0563c7 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -96,6 +96,7 @@ flags.DECLARE('network_size', 'nova.network.manager') flags.DECLARE('vlan_start', 'nova.network.manager') flags.DECLARE('vpn_start', 'nova.network.manager') flags.DECLARE('fixed_range_v6', 'nova.network.manager') +flags.DECLARE('images_path', 'nova.image.local') flags.DEFINE_flag(flags.HelpFlag()) flags.DEFINE_flag(flags.HelpshortFlag()) flags.DEFINE_flag(flags.HelpXMLFlag()) @@ -743,17 +744,20 @@ class ImageCommands(object): def __init__(self, *args, **kwargs): self.image_service = utils.import_object(FLAGS.image_service) - def _register(self, image_type, path, owner, name=None, - is_public='T', architecture='x86_64', - kernel_id=None, ramdisk_id=None): - meta = {'type': image_type, - 'is_public': True, + def _register(self, image_type, disk_format, container_format, + path, owner, name=None, is_public='T', + architecture='x86_64', kernel_id=None, ramdisk_id=None): + meta = {'is_public': True, 'name': name, + 'disk_format': disk_format, + 'container_format': container_format, 'properties': {'image_state': 'available', 'owner': owner, + 'type': image_type, 'architecture': architecture, 'image_location': 'local', 'is_public': (is_public == 'T')}} + print image_type, meta if kernel_id: meta['properties']['kernel_id'] = int(kernel_id) if ramdisk_id: @@ -781,28 +785,31 @@ class ImageCommands(object): architecture, kernel_id, ramdisk_id) def image_register(self, path, owner, name=None, is_public='T', - architecture='x86_64', kernel_id=None, ramdisk_id=None): + architecture='x86_64', kernel_id=None, ramdisk_id=None, + disk_format='ami', container_format='ami'): """Uploads an image into the image_service arguments: path owner [name] [is_public='T'] [architecture='x86_64'] - [kernel_id] [ramdisk_id]""" - return self._register('machine', path, owner, name, is_public, - architecture, kernel_id, ramdisk_id) + [kernel_id=None] [ramdisk_id=None] + [disk_format='ami'] [container_format='ami']""" + return self._register('machine', disk_format, container_format, path, + owner, name, is_public, architecture, + kernel_id, ramdisk_id) def kernel_register(self, path, owner, name=None, is_public='T', architecture='x86_64'): """Uploads a kernel into the image_service arguments: path owner [name] [is_public='T'] [architecture='x86_64'] """ - return self._register('kernel', path, owner, name, is_public, - architecture) + return self._register('kernel', 'aki', 'aki', path, owner, name, + is_public, architecture) def ramdisk_register(self, path, owner, name=None, is_public='T', architecture='x86_64'): """Uploads a ramdisk into the image_service arguments: path owner [name] [is_public='T'] [architecture='x86_64'] """ - return self._register('ramdisk', path, owner, name, is_public, - architecture) + return self._register('ramdisk', 'ari', 'ari', path, owner, name, + is_public, architecture) def _lookup(self, old_image_id): try: @@ -813,12 +820,19 @@ class ImageCommands(object): return image['id'] def _old_to_new(self, old): - new = {'type': old['type'], + mapping = {'machine': 'ami', + 'kernel': 'aki', + 'ramdisk': 'ari'} + container_format = mapping[old['type']] + disk_format = container_format + new = {'disk_format': disk_format, + 'container_format': container_format, 'is_public': True, 'name': old['imageId'], 'properties': {'image_state': old['imageState'], 'owner': old['imageOwnerId'], 'architecture': old['architecture'], + 'type': old['type'], 'image_location': old['imageLocation'], 'is_public': old['isPublic']}} if old.get('kernelId'): @@ -851,7 +865,8 @@ class ImageCommands(object): # to move the files out of the way before importing # so we aren't writing to the same directory. This # may fail if the dir was a mointpoint. - if directory == os.path.abspath(FLAGS.images_path): + if (FLAGS.image_service == 'nova.image.local.LocalImageService' + and directory == os.path.abspath(FLAGS.images_path)): new_dir = "%s_bak" % directory os.move(directory, new_dir) os.mkdir(directory) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 6479c9445..6b79f7f53 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -867,7 +867,8 @@ class CloudController(object): def _format_image(self, image): """Convert from format defined by BaseImageService to S3 format.""" i = {} - ec2_id = self._image_ec2_id(image.get('id'), image.get('type')) + image_type = image['properties'].get('type') + ec2_id = self._image_ec2_id(image.get('id'), image_type) name = image.get('name') if name: i['imageId'] = "%s (%s)" % (ec2_id, name) @@ -882,7 +883,7 @@ class CloudController(object): i['imageOwnerId'] = image['properties'].get('owner_id') i['imageLocation'] = image['properties'].get('image_location') i['imageState'] = image['properties'].get('image_state') - i['type'] = image.get('type') + i['type'] = image_type i['isPublic'] = str(image['properties'].get('is_public', '')) == 'True' i['architecture'] = image['properties'].get('architecture') return i @@ -915,7 +916,8 @@ class CloudController(object): image_location = kwargs['name'] metadata = {'properties': {'image_location': image_location}} image = self.image_service.create(context, metadata) - image_id = self._image_ec2_id(image['id'], image['type']) + image_id = self._image_ec2_id(image['id'], + image['properties']['type']) msg = _("Registered image %(image_location)s with" " id %(image_id)s") % locals() LOG.audit(msg, context=context) @@ -954,6 +956,7 @@ class CloudController(object): raise exception.NotFound(_('Image %s not found') % image_id) internal_id = image['id'] del(image['id']) + raise Exception(image) image['properties']['is_public'] = (operation_type == 'add') return self.image_service.update(context, internal_id, image) diff --git a/nova/image/s3.py b/nova/image/s3.py index ab6eea8cf..bf104c29a 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -139,11 +139,13 @@ class S3ImageService(service.BaseImageService): manifest = key.get_contents_as_string() manifest = ElementTree.fromstring(manifest) + image_format = 'ami' image_type = 'machine' try: kernel_id = manifest.find("machine_configuration/kernel_id").text if kernel_id == 'true': + image_format = 'aki' image_type = 'kernel' kernel_id = None except: @@ -152,6 +154,7 @@ class S3ImageService(service.BaseImageService): try: ramdisk_id = manifest.find("machine_configuration/ramdisk_id").text if ramdisk_id == 'true': + image_format = 'ari' image_type = 'ramdisk' ramdisk_id = None except: @@ -173,7 +176,9 @@ class S3ImageService(service.BaseImageService): properties['ramdisk_id'] = ec2utils.ec2_id_to_id(ramdisk_id) properties['is_public'] = False - metadata.update({'type': image_type, + properties['type'] = image_type + metadata.update({'disk_format': image_format, + 'container_format': image_format, 'status': 'queued', 'is_public': True, 'properties': properties}) -- cgit From ec23b8e1205e969d449834b02984d01a8daf93dc Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Tue, 8 Mar 2011 20:28:11 +0000 Subject: update manpage --- doc/source/man/novamanage.rst | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/doc/source/man/novamanage.rst b/doc/source/man/novamanage.rst index 17ba91bef..1d8446f08 100644 --- a/doc/source/man/novamanage.rst +++ b/doc/source/man/novamanage.rst @@ -173,7 +173,10 @@ Nova Floating IPs ``nova-manage floating create <host> <ip_range>`` Creates floating IP addresses for the named host by the given range. - floating delete <ip_range> Deletes floating IP addresses in the range given. + +``nova-manage floating delete <ip_range>`` + + Deletes floating IP addresses in the range given. ``nova-manage floating list`` @@ -193,7 +196,7 @@ Nova Flavor ``nova-manage flavor create <name> <memory> <vCPU> <local_storage> <flavorID> <(optional) swap> <(optional) RXTX Quota> <(optional) RXTX Cap>`` creates a flavor with the following positional arguments: - * memory (expressed in megabytes) + * memory (expressed in megabytes) * vcpu(s) (integer) * local storage (expressed in gigabytes) * flavorid (unique integer) @@ -209,12 +212,33 @@ Nova Flavor Purges the flavor with the name <name>. This removes this flavor from the database. - Nova Instance_type ~~~~~~~~~~~~~~~~~~ The instance_type command is provided as an alias for the flavor command. All the same subcommands and arguments from nova-manage flavor can be used. +Nova Images +~~~~~~~~~~~ + +``nova-manage image image_register <path> <owner>`` + + Registers an image with the image service. + +``nova-manage image kernel_register <path> <owner>`` + + Registers a kernel with the image service. + +``nova-manage image ramdisk_register <path> <owner>`` + + Registers a ramdisk with the image service. + +``nova-manage image all_register <image_path> <kernel_path> <ramdisk_path> <owner>`` + + Registers an image kernel and ramdisk with the image service. + +``nova-manage image convert <directory>`` + + Converts all images in directory from the old (Bexar) format to the new format. FILES ======== -- cgit From dd2f0019297d01fe5d6b3dae4efc72946191be75 Mon Sep 17 00:00:00 2001 From: Rick Harris <rick.harris@rackspace.com> Date: Tue, 8 Mar 2011 22:14:25 +0000 Subject: Use disk_format and container_format instead of image type --- nova/api/openstack/servers.py | 2 +- nova/virt/xenapi/vm_utils.py | 18 ++++++++++-------- plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 14 +++++++++++--- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index c2bf42b72..85999764f 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -450,7 +450,7 @@ class Controller(wsgi.Controller): _("Cannot build from image %(image_id)s, status not active") % locals()) - if image['type'] != 'machine': + if image['disk_format'] != 'ami': return None, None try: diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 80b7540d4..ce081a2d6 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -467,19 +467,21 @@ class VMHelper(HelperBase): "%(image_id)s, instance %(instance_id)s") % locals()) def determine_from_glance(): - glance_type2nova_type = {'machine': ImageType.DISK, - 'raw': ImageType.DISK_RAW, - 'vhd': ImageType.DISK_VHD, - 'kernel': ImageType.KERNEL_RAMDISK, - 'ramdisk': ImageType.KERNEL_RAMDISK} + glance_disk_format2nova_type = { + 'ami': ImageType.DISK, + 'aki': ImageType.KERNEL_RAMDISK, + 'ari': ImageType.KERNEL_RAMDISK, + 'raw': ImageType.DISK_RAW, + 'vhd': ImageType.DISK_VHD} client = glance.client.Client(FLAGS.glance_host, FLAGS.glance_port) meta = client.get_image_meta(instance.image_id) - type_ = meta['type'] + disk_format = meta['disk_format'] try: - return glance_type2nova_type[type_] + return glance_disk_format2nova_type[disk_format] except KeyError: raise exception.NotFound( - _("Unrecognized image type '%(type_)s'") % locals()) + _("Unrecognized disk_format '%(disk_format)s'") + % locals()) def determine_from_instance(): if instance.kernel_id: diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index aa12d432a..201b99fda 100644 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -201,13 +201,21 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port): # to request conn.putrequest('PUT', '/images/%s' % image_id) - # TODO(sirp): make `store` configurable + # NOTE(sirp): There is some confusion around OVF. Here's a summary of + # where we currently stand: + # 1. OVF as a container format is misnamed. We really should be using + # OVA since that is the name for the container format; OVF is the + # standard applied to the manifest file contained within. + # 2. We're currently uploading a vanilla tarball. In order to be OVF/OVA + # compliant, we'll need to embed a minimal OVF manifest as the first + # file. headers = { 'content-type': 'application/octet-stream', 'transfer-encoding': 'chunked', - 'x-image-meta-is_public': 'True', + 'x-image-meta-is-public': 'True', 'x-image-meta-status': 'queued', - 'x-image-meta-type': 'vhd'} + 'x-image-meta-disk-format': 'vhd', + 'x-image-meta-container-format': 'ovf'} for header, value in headers.iteritems(): conn.putheader(header, value) conn.endheaders() -- cgit From 7a6833c883a04fd7920bff7367c9e28a35858d8d Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Tue, 8 Mar 2011 23:17:50 +0000 Subject: Accidentally left some bad data around --- nova/tests/test_xenapi.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 919a38c06..916555af3 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -376,6 +376,11 @@ class XenAPIMigrateInstance(test.TestCase): stubs.stub_out_migration_methods(self.stubs) glance_stubs.stubout_glance_client(self.stubs, glance_stubs.FakeGlance) + def tearDown(self): + super(XenAPIMigrateInstance, self).tearDown() + self.manager.delete_project(self.project) + self.manager.delete_user(self.user) + self.stubs.UnsetAll() def test_migrate_disk_and_power_off(self): instance = db.instance_create(self.values) -- cgit From 698398fdc2a05a0930591d3f3d386ad24a322359 Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Tue, 8 Mar 2011 23:24:19 +0000 Subject: Pep8 fixes --- nova/tests/test_xenapi.py | 2 ++ nova/tests/xenapi/stubs.py | 1 + nova/virt/xenapi/vmops.py | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 916555af3..c26dc8639 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -376,6 +376,7 @@ class XenAPIMigrateInstance(test.TestCase): stubs.stub_out_migration_methods(self.stubs) glance_stubs.stubout_glance_client(self.stubs, glance_stubs.FakeGlance) + def tearDown(self): super(XenAPIMigrateInstance, self).tearDown() self.manager.delete_project(self.project) @@ -394,6 +395,7 @@ class XenAPIMigrateInstance(test.TestCase): conn = xenapi_conn.get_connection(False) conn.finish_resize(instance, dict(base_copy='hurr', cow='durr')) + class XenAPIDetermineDiskImageTestCase(test.TestCase): """ Unit tests for code that detects the ImageType diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index d8e358611..70d46a1fb 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -237,6 +237,7 @@ class FakeSessionForMigrationTests(fake.SessionBase): vm['is_a_template'] = False vm['is_control_domain'] = False + def stub_out_migration_methods(stubs): def fake_get_snapshot(self, instance): return 'foo', 'bar' diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 43c23806e..562ecd4d5 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -83,7 +83,7 @@ class VMOps(object): def spawn(self, instance): vdi_uuid = self.create_disk(instance) self._spawn_with_disk(instance, vdi_uuid=vdi_uuid) - + def _spawn_with_disk(self, instance, vdi_uuid): """Create VM instance""" instance_name = instance.name -- cgit From 6207abe3068964c586d06bb0e3740b8bad922dca Mon Sep 17 00:00:00 2001 From: Rick Harris <rick.harris@rackspace.com> Date: Tue, 8 Mar 2011 23:26:33 +0000 Subject: Fixing tests --- nova/tests/glance/stubs.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index 3ff8d7ce5..5872552ec 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -35,23 +35,28 @@ class FakeGlance(object): IMAGE_FIXTURES = { IMAGE_MACHINE: { 'image_meta': {'name': 'fakemachine', 'size': 0, - 'type': 'machine'}, + 'disk_format': 'ami', + 'container_format': 'ami'}, 'image_data': StringIO.StringIO('')}, IMAGE_KERNEL: { 'image_meta': {'name': 'fakekernel', 'size': 0, - 'type': 'kernel'}, + 'disk_format': 'aki', + 'container_format': 'aki'}, 'image_data': StringIO.StringIO('')}, IMAGE_RAMDISK: { 'image_meta': {'name': 'fakeramdisk', 'size': 0, - 'type': 'ramdisk'}, + 'disk_format': 'ari', + 'container_format': 'ari'}, 'image_data': StringIO.StringIO('')}, IMAGE_RAW: { 'image_meta': {'name': 'fakeraw', 'size': 0, - 'type': 'raw'}, + 'disk_format': 'raw', + 'container_format': 'bare'}, 'image_data': StringIO.StringIO('')}, IMAGE_VHD: { 'image_meta': {'name': 'fakevhd', 'size': 0, - 'type': 'vhd'}, + 'disk_format': 'vhd', + 'container_format': 'ovf'}, 'image_data': StringIO.StringIO('')}} def __init__(self, host, port=None, use_ssl=False): -- cgit From a4830f83afd78cdb96dc3e474eb4efc167de7737 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 8 Mar 2011 16:45:20 -0800 Subject: Sorted imports correctly --- bin/nova-api | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-api b/bin/nova-api index 85ca4eefd..06bb855cb 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -34,9 +34,9 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): gettext.install('nova', unicode=1) -from nova import service from nova import flags from nova import log as logging +from nova import service from nova import utils from nova import version from nova import wsgi -- cgit From e8c8fd3f232371625f0924410c4c09c32339b113 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 8 Mar 2011 16:47:43 -0800 Subject: Renamed FLAG.paste_config -> FLAG.api_paste_config --- nova/service.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/service.py b/nova/service.py index 5a8d58695..460f36f7a 100644 --- a/nova/service.py +++ b/nova/service.py @@ -56,7 +56,7 @@ flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen') flags.DEFINE_string('osapi_listen', "0.0.0.0", 'IP address for OpenStack API to listen') flags.DEFINE_integer('osapi_listen_port', 8774, 'port for os api to listen') -flags.DEFINE_string('paste_config', "api-paste.ini", +flags.DEFINE_string('api_paste_config', "api-paste.ini", 'File name for the paste.deploy config for nova-api') @@ -240,10 +240,10 @@ class ApiService(WsgiService): @classmethod def create(cls, conf=None): if not conf: - conf = wsgi.paste_config_file(FLAGS.paste_config) + conf = wsgi.paste_config_file(FLAGS.api_paste_config) if not conf: message = (_("No paste configuration found for: %s"), - FLAGS.paste_config) + FLAGS.api_paste_config) raise exception.Error(message) api_endpoints = ['ec2', 'osapi'] service = cls(conf, api_endpoints) @@ -315,7 +315,7 @@ def _run_wsgi(paste_config_file, apis): getattr(FLAGS, "%s_listen" % api))) if len(apps) == 0: logging.error(_("No known API applications configured in %s."), - paste_config_file) + paste_config_file) return server = wsgi.Server() -- cgit From 59fa70102a06dce9f86b9b29825245bc54c01598 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara <justin@fathomdb.com> Date: Tue, 8 Mar 2011 16:51:05 -0800 Subject: Added documentation about needed flags --- nova/service.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nova/service.py b/nova/service.py index 460f36f7a..af20db01c 100644 --- a/nova/service.py +++ b/nova/service.py @@ -221,7 +221,12 @@ class Service(object): class WsgiService(object): - """Base class for WSGI based services.""" + """Base class for WSGI based services. + + For each api you define, you must also define these flags: + :<api>_listen: The address on which to listen + :<api>_listen_port: The port on which to listen + """ def __init__(self, conf, apis): self.conf = conf -- cgit From a320b5df9f916adf8422ed312306c77570d392c2 Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Wed, 9 Mar 2011 00:30:05 -0500 Subject: execvp: almost passes tests --- nova/api/ec2/cloud.py | 2 +- nova/network/linux_net.py | 21 +++++++++++---------- nova/tests/test_network.py | 2 +- nova/tests/test_virt.py | 11 ++++++----- nova/utils.py | 19 +++++++++++++------ nova/virt/libvirt_conn.py | 11 ++++++----- nova/virt/xenapi/vm_utils.py | 6 ++---- nova/volume/driver.py | 5 +++-- 8 files changed, 43 insertions(+), 34 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 0d22a3f46..b7d72d1c1 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -115,7 +115,7 @@ class CloudController(object): start = os.getcwd() os.chdir(FLAGS.ca_path) # TODO(vish): Do this with M2Crypto instead - utils.runthis(_("Generating root CA: %s"), "sh genrootca.sh") + utils.runthis(_("Generating root CA: %s"), "sh", "genrootca.sh") os.chdir(start) def _get_mpi_data(self, context, project_id): diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index ad019a8c0..b66a1adb7 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -139,14 +139,14 @@ def init_host(): def bind_floating_ip(floating_ip, check_exit_code=True): """Bind ip to public interface""" _execute('sudo', 'ip', 'addr', 'add', floating_ip, - 'dev', FLAGS.public_interface), + 'dev', FLAGS.public_interface, check_exit_code=check_exit_code) def unbind_floating_ip(floating_ip): """Unbind a public ip from public interface""" _execute('sudo', 'ip', 'addr', 'del', floating_ip, - 'dev', FLAGS.public_interface)) + 'dev', FLAGS.public_interface) def ensure_vlan_forward(public_ip, port, private_ip): @@ -213,7 +213,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): _execute('sudo', 'brctl', 'addbr', bridge) _execute('sudo', 'brctl', 'setfd', bridge, 0) # _execute("sudo brctl setageing %s 10" % bridge) - _execute('sudo', 'brctl', 'stp', bridge', 'off') + _execute('sudo', 'brctl', 'stp', bridge, 'off') _execute('sudo', 'ip', 'link', 'set', bridge, up) if net_attrs: # NOTE(vish): The ip for dnsmasq has to be the first address on the @@ -223,7 +223,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): "%s/%s" % (net_attrs['gateway'], suffix), 'brd', - net-attrs['broadcast'], + net_attrs['broadcast'], 'dev', bridge, check_exit_code=False) @@ -257,7 +257,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): _execute('sudo', 'ip', 'addr', 'add', params, 'dev', bridge) if gateway: _execute('sudo', 'route', 'add', '0.0.0.0', 'gw', gateway) - out, err = _execute('sudo', 'brctl', 'addif, bridge, interface, + out, err = _execute('sudo', 'brctl', 'addif', bridge, interface, check_exit_code=False) if (err and err != "device %s is already a member of a bridge; can't " @@ -265,11 +265,11 @@ def ensure_bridge(bridge, interface, net_attrs=None): raise exception.Error("Failed to add interface: %s" % err) if FLAGS.use_nova_chains: - (out, err) = _execute('sudo', 'iptables', '-N', 'nova_forward, + (out, err) = _execute('sudo', 'iptables', '-N', 'nova_forward', check_exit_code=False) if err != 'iptables: Chain already exists.\n': # NOTE(vish): chain didn't exist link chain - _execute('sudo', 'iptables, '-D', 'FORWARD', '-j', 'nova_forward', + _execute('sudo', 'iptables', '-D', 'FORWARD', '-j', 'nova_forward', check_exit_code=False) _execute('sudo', 'iptables', '-A', 'FORWARD', '-j', 'nova_forward') @@ -355,7 +355,7 @@ interface %s # if radvd is already running, then tell it to reload if pid: - out, _err = _execute('cat', "/proc/%d/cmdline' + out, _err = _execute('cat', '/proc/%d/cmdline' % pid, check_exit_code=False) if conffile in out: try: @@ -383,7 +383,7 @@ def _host_dhcp(fixed_ip_ref): def _execute(*cmd, **kwargs): """Wrapper around utils._execute for fake_network""" if FLAGS.fake_network: - LOG.debug("FAKE NET: %s", ' '.join(cmd)) + LOG.debug("FAKE NET: %s", " ".join(map(str, cmd))) return "fake", 0 else: return utils.execute(*cmd, **kwargs) @@ -396,7 +396,8 @@ def _device_exists(device): return not err -def _confirm_rule(chain, *cmd, append=False): +def _confirm_rule(chain, *cmd, **kwargs): + append=kwargs.get('append',False) """Delete and re-add iptables rule""" if FLAGS.use_nova_chains: chain = "nova_%s" % chain.lower() diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 6d2d8b771..19099ff4c 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -343,7 +343,7 @@ def lease_ip(private_ip): private_ip) instance_ref = db.fixed_ip_get_instance(context.get_admin_context(), private_ip) - cmd = (binpath('nova-dhcpbridge'), 'add' + cmd = (binpath('nova-dhcpbridge'), 'add', instance_ref['mac_address'], private_ip, 'fake') env = {'DNSMASQ_INTERFACE': network_ref['bridge'], diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index f151ae911..7f1ad002e 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -315,15 +315,16 @@ class IptablesFirewallTestCase(test.TestCase): instance_ref = db.instance_get(admin_ctxt, instance_ref['id']) # self.fw.add_instance(instance_ref) - def fake_iptables_execute(cmd, process_input=None): - if cmd == 'sudo ip6tables-save -t filter': + def fake_iptables_execute(*cmd, **kwargs): + process_input=kwargs.get('process_input', None) + if cmd == ('sudo', 'ip6tables-save', '-t', 'filter'): return '\n'.join(self.in6_rules), None - if cmd == 'sudo iptables-save -t filter': + if cmd == ('sudo', 'iptables-save', '-t', 'filter'): return '\n'.join(self.in_rules), None - if cmd == 'sudo iptables-restore': + if cmd == ('sudo', 'iptables-restore'): self.out_rules = process_input.split('\n') return '', '' - if cmd == 'sudo ip6tables-restore': + if cmd == ('sudo', 'ip6tables-restore'): self.out6_rules = process_input.split('\n') return '', '' self.fw.execute = fake_iptables_execute diff --git a/nova/utils.py b/nova/utils.py index c96b85294..9b51f8b40 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -128,13 +128,20 @@ def fetchfile(url, target): execute("curl", "--fail", url, "-o", target) -def execute(*cmd, process_input=None, addl_env=None, check_exit_code=True): +def execute(*cmd, **kwargs): + process_input=kwargs.get('process_input', None) + addl_env=kwargs.get('addl_env', None) + check_exit_code=kwargs.get('check_exit_code', True) + stdin=kwargs.get('stdin', subprocess.PIPE) + stdout=kwargs.get('stdout', subprocess.PIPE) + stderr=kwargs.get('stderr', subprocess.PIPE) + LOG.debug(_("Running cmd (subprocess): %s"), ' '.join(cmd)) env = os.environ.copy() if addl_env: env.update(addl_env) - obj = subprocess.Popen(*cmd, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) + obj = subprocess.Popen(cmd, stdin=stdin, + stdout=stdout, stderr=stderr, env=env) result = None if process_input != None: result = obj.communicate(process_input) @@ -220,9 +227,9 @@ def debug(arg): return arg -def runthis(prompt, cmd, check_exit_code=True): - LOG.debug(_("Running %s"), (cmd)) - rv, err = execute(cmd, check_exit_code=check_exit_code) +def runthis(prompt, *cmd, **kwargs): + LOG.debug(_("Running %s"), (" ".join(cmd))) + rv, err = execute(*cmd, **kwargs) def generate_uid(topic, size=8): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index a5256bbc2..76f31f91a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -540,8 +540,8 @@ class LibvirtConnection(object): if not os.path.exists(base): fn(target=base, *args, **kwargs) if cow: - utils.execute('qemu-img', 'create', '-f', 'qcow2', "'-o'', - "cluster_size=2M,backing_file=%s" % base, + utils.execute('qemu-img', 'create', '-f', 'qcow2', '-o', + 'cluster_size=2M,backing_file=%s' % base, target) else: utils.execute('cp', base, target) @@ -554,7 +554,7 @@ class LibvirtConnection(object): def _create_local(self, target, local_gb): """Create a blank image of specified size""" - utils.execute('truncate', target, '-s', "%dG" local_gb) + utils.execute('truncate', target, '-s', "%dG" % local_gb) # TODO(vish): should we format disk by default? def _create_image(self, inst, libvirt_xml, suffix='', disk_images=None): @@ -565,7 +565,7 @@ class LibvirtConnection(object): fname + suffix) # ensure directories exist and are writable - utils.execute('mkdir', '-p', basepath(suffix='') + utils.execute('mkdir', '-p', basepath(suffix='')) LOG.info(_('instance %s: Creating image'), inst['name']) f = open(basepath('libvirt.xml'), 'w') @@ -1245,7 +1245,8 @@ class IptablesFirewallDriver(FirewallDriver): self.apply_ruleset() def apply_ruleset(self): - current_filter, _ = self.execute('sudo iptables-save -t filter') + current_filter, _ = self.execute('sudo', 'iptables-save', + '-t', 'filter') current_lines = current_filter.split('\n') new_filter = self.modify_rules(current_lines, 4) self.execute('sudo', 'iptables-restore', diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 8fdb658fb..6ba13f980 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -915,10 +915,8 @@ def _write_partition(virtual_size, dev): LOG.debug(_('Writing partition table %(primary_first)d %(primary_last)d' ' to %(dest)s...') % locals()) - def execute(*cmd, process_input=None, check_exit_code=True): - return utils.execute(*cmd, - process_input=process_input, - check_exit_code=check_exit_code) + def execute(*cmd, **kwargs): + return utils.execute(*cmd, **kwargs) execute('parted', '--script', dest, 'mklabel', 'msdos') execute('parted', '--script', dest, 'mkpart', 'primary', diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 220c9ef9d..e9bdf162f 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -104,7 +104,8 @@ class VolumeDriver(object): def delete_volume(self, volume): """Deletes a logical volume.""" try: - self._try_execute('sudo', 'lvdisplay', '%s/%s" % + self._try_execute('sudo', 'lvdisplay', + '%s/%s' % (FLAGS.volume_group, volume['name'])) except Exception as e: @@ -550,7 +551,7 @@ class SheepdogDriver(VolumeDriver): else: sizestr = '%sG' % volume['size'] self._try_execute('qemu-img', 'create', - "sheepdog:%s" %s" % volume['name'], + "sheepdog:%s" % volume['name'], sizestr) def delete_volume(self, volume): -- cgit From 1d7358e70379607c9cce02307f4336efbd135a5d Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Wed, 9 Mar 2011 01:26:53 -0500 Subject: execvp: unit tests pass --- nova/crypto.py | 2 +- nova/utils.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/crypto.py b/nova/crypto.py index dd24723b8..20bb570a5 100644 --- a/nova/crypto.py +++ b/nova/crypto.py @@ -196,7 +196,7 @@ def generate_x509_cert(user_id, project_id, bits=1024): tmpdir = tempfile.mkdtemp() keyfile = os.path.abspath(os.path.join(tmpdir, 'temp.key')) csrfile = os.path.join(tmpdir, 'temp.csr') - utils.execute('openssl', 'genrsa', '-out', keyfile, bits) + utils.execute('openssl', 'genrsa', '-out', keyfile, str(bits)) utils.execute('openssl', 'req', '-new', '-key', keyfile, '-out', csrfile, '-batch', '-subj', subject) private_key = open(keyfile).read() diff --git a/nova/utils.py b/nova/utils.py index 9b51f8b40..7ddf056ea 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -135,6 +135,7 @@ def execute(*cmd, **kwargs): stdin=kwargs.get('stdin', subprocess.PIPE) stdout=kwargs.get('stdout', subprocess.PIPE) stderr=kwargs.get('stderr', subprocess.PIPE) + cmd=map(str,cmd) LOG.debug(_("Running cmd (subprocess): %s"), ' '.join(cmd)) env = os.environ.copy() -- cgit From 77da93886be61230dea5a4a4c4de036a57e62550 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 9 Mar 2011 06:56:42 +0000 Subject: tests and semaphore fix for image caching --- nova/tests/test_virt.py | 66 +++++++++++++++++++++++++++++++++++++++++++++++ nova/virt/libvirt_conn.py | 14 +++++++--- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index f151ae911..ec7498d72 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -14,12 +14,16 @@ # License for the specific language governing permissions and limitations # under the License. +import os + +import eventlet from xml.etree.ElementTree import fromstring as xml_to_tree from xml.dom.minidom import parseString as xml_to_dom from nova import context from nova import db from nova import flags +from nova import log as logging from nova import test from nova import utils from nova.api.ec2 import cloud @@ -30,6 +34,68 @@ FLAGS = flags.FLAGS flags.DECLARE('instances_path', 'nova.compute.manager') +def _concurrency(wait, done, target): + wait.wait() + done.send() + + +class CacheConcurrencyTestCase(test.TestCase): + def setUp(self): + super(CacheConcurrencyTestCase, self).setUp() + + def fake_exists(fname): + basedir = os.path.join(FLAGS.instances_path, '_base') + if fname == basedir: + return True + return False + + def fake_execute(*args, **kwargs): + pass + + self.stubs.Set(os.path, 'exists', fake_exists) + self.stubs.Set(utils, 'execute', fake_execute) + + def test_same_fname_concurrency(self): + """Ensures that the same fname cache runs at a sequentially""" + conn = libvirt_conn.get_connection(False) + wait1 = eventlet.event.Event() + done1 = eventlet.event.Event() + eventlet.spawn(conn._cache_image, _concurrency, + 'target', 'fname', False, wait1, done1) + wait2 = eventlet.event.Event() + done2 = eventlet.event.Event() + eventlet.spawn(conn._cache_image, _concurrency, + 'target', 'fname', False, wait2, done2) + wait2.send() + eventlet.sleep(0) + try: + self.assertFalse(done2.ready()) + finally: + wait1.send() + done1.wait() + eventlet.sleep(0) + self.assertTrue(done2.ready()) + + def test_different_fname_concurrency(self): + """Ensures that two different fname caches are concurrent""" + conn = libvirt_conn.get_connection(False) + wait1 = eventlet.event.Event() + done1 = eventlet.event.Event() + eventlet.spawn(conn._cache_image, _concurrency, + 'target', 'fname2', False, wait1, done1) + wait2 = eventlet.event.Event() + done2 = eventlet.event.Event() + eventlet.spawn(conn._cache_image, _concurrency, + 'target', 'fname1', False, wait2, done2) + wait2.send() + eventlet.sleep(0) + try: + self.assertTrue(done2.ready()) + finally: + wait1.send() + eventlet.sleep(0) + + class LibvirtConnTestCase(test.TestCase): def setUp(self): super(LibvirtConnTestCase, self).setUp() diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9f7315c17..1a1f146d4 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -44,9 +44,8 @@ import uuid from xml.dom import minidom -from eventlet import greenthread -from eventlet import event from eventlet import tpool +from eventlet import semaphore import IPy @@ -512,6 +511,8 @@ class LibvirtConnection(object): subprocess.Popen(cmd, shell=True) return {'token': token, 'host': host, 'port': port} + _image_semaphores = {} + def _cache_image(self, fn, target, fname, cow=False, *args, **kwargs): """Wrapper for a method that creates an image that caches the image. @@ -531,8 +532,13 @@ class LibvirtConnection(object): if not os.path.exists(base_dir): os.mkdir(base_dir) base = os.path.join(base_dir, fname) - if not os.path.exists(base): - fn(target=base, *args, **kwargs) + + if fname not in self._image_semaphores: + self._image_semaphores[fname] = semaphore.Semaphore() + with self._image_semaphores[fname]: + if not os.path.exists(base): + fn(target=base, *args, **kwargs) + if cow: utils.execute('qemu-img create -f qcow2 -o ' 'cluster_size=2M,backing_file=%s %s' -- cgit From ddeab2da30bb2f74109854d982c6681e78e7a4ce Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 9 Mar 2011 07:35:58 +0000 Subject: make static method for testing without initializing libvirt --- nova/tests/test_virt.py | 4 ++-- nova/virt/libvirt_conn.py | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index ec7498d72..113632a0c 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -57,7 +57,7 @@ class CacheConcurrencyTestCase(test.TestCase): def test_same_fname_concurrency(self): """Ensures that the same fname cache runs at a sequentially""" - conn = libvirt_conn.get_connection(False) + conn = libvirt_conn.LibvirtConnection wait1 = eventlet.event.Event() done1 = eventlet.event.Event() eventlet.spawn(conn._cache_image, _concurrency, @@ -78,7 +78,7 @@ class CacheConcurrencyTestCase(test.TestCase): def test_different_fname_concurrency(self): """Ensures that two different fname caches are concurrent""" - conn = libvirt_conn.get_connection(False) + conn = libvirt_conn.LibvirtConnection wait1 = eventlet.event.Event() done1 = eventlet.event.Event() eventlet.spawn(conn._cache_image, _concurrency, diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 1a1f146d4..ecef7950a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -511,9 +511,10 @@ class LibvirtConnection(object): subprocess.Popen(cmd, shell=True) return {'token': token, 'host': host, 'port': port} - _image_semaphores = {} + _image_sems = {} - def _cache_image(self, fn, target, fname, cow=False, *args, **kwargs): + @staticmethod + def _cache_image(fn, target, fname, cow=False, *args, **kwargs): """Wrapper for a method that creates an image that caches the image. This wrapper will save the image into a common store and create a @@ -533,9 +534,9 @@ class LibvirtConnection(object): os.mkdir(base_dir) base = os.path.join(base_dir, fname) - if fname not in self._image_semaphores: - self._image_semaphores[fname] = semaphore.Semaphore() - with self._image_semaphores[fname]: + if fname not in LibvirtConnection._image_sems: + LibvirtConnection._image_sems[fname] = semaphore.Semaphore() + with LibvirtConnection._image_sems[fname]: if not os.path.exists(base): fn(target=base, *args, **kwargs) -- cgit From 7d31fe9ef316f49379818259a55a84deb5b850cd Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Wed, 9 Mar 2011 10:30:18 +0100 Subject: Stop assuming anything about the order in which the two processes are scheduled. --- nova/tests/test_misc.py | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/nova/tests/test_misc.py b/nova/tests/test_misc.py index 9f572b58e..a658e4978 100644 --- a/nova/tests/test_misc.py +++ b/nova/tests/test_misc.py @@ -71,30 +71,33 @@ class LockTestCase(test.TestCase): "got mangled") def test_synchronized(self): - rpipe, wpipe = os.pipe() + rpipe1, wpipe1 = os.pipe() + rpipe2, wpipe2 = os.pipe() + + @synchronized('testlock') + def f(rpipe, wpipe): + try: + os.write(wpipe, "foo") + except OSError, e: + self.assertEquals(e.errno, errno.EPIPE) + return + + rfds, _, __ = select.select([rpipe], [], [], 1) + self.assertEquals(len(rfds), 0, "The other process, which was" + " supposed to be locked, " + "wrote on its end of the " + "pipe") + os.close(rpipe) + pid = os.fork() if pid > 0: - os.close(wpipe) - - @synchronized('testlock') - def f(): - rfds, _, __ = select.select([rpipe], [], [], 1) - self.assertEquals(len(rfds), 0, "The other process, which was" - " supposed to be locked, " - "wrote on its end of the " - "pipe") - os.close(rpipe) - - f() + os.close(wpipe1) + os.close(rpipe2) + + f(rpipe1, wpipe2) else: - os.close(rpipe) + os.close(rpipe1) + os.close(wpipe2) - @synchronized('testlock') - def g(): - try: - os.write(wpipe, "foo") - except OSError, e: - self.assertEquals(e.errno, errno.EPIPE) - return - g() + f(rpipe2, wpipe1) os._exit(0) -- cgit From 0f45b59ca6f9502a3ae6578e2fca5a7d9575ae5e Mon Sep 17 00:00:00 2001 From: Dan Prince <dan.prince@rackspace.com> Date: Wed, 9 Mar 2011 10:37:21 -0500 Subject: Added 'adminPass' to the serialization_metadata. --- nova/api/openstack/servers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 9581b8477..bbedd7c63 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -96,7 +96,7 @@ class Controller(wsgi.Controller): 'application/xml': { "attributes": { "server": ["id", "imageId", "name", "flavorId", "hostId", - "status", "progress"]}}} + "status", "progress", "adminPass"]}}} def __init__(self): self.compute_api = compute.API() -- cgit From eadce208c55513ddbab550898e641b8ee55a67ec Mon Sep 17 00:00:00 2001 From: Dan Prince <dan.prince@rackspace.com> Date: Wed, 9 Mar 2011 12:32:15 -0500 Subject: Fix spacing. --- nova/api/openstack/servers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index bbedd7c63..7222285e0 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -84,11 +84,13 @@ def _translate_detail_keys(inst): return dict(server=inst_dict) + def _translate_keys(inst): """ Coerces into dictionary format, excluding all model attributes save for id and name """ return dict(server=dict(id=inst['id'], name=inst['display_name'])) + class Controller(wsgi.Controller): """ The Server API controller for the OpenStack API """ -- cgit From e17876ec002f976572b6ac102dc113024669a45c Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz <emaildericky@gmail.com> Date: Wed, 9 Mar 2011 18:57:53 +0100 Subject: fixed lp715427 --- nova/network/manager.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nova/network/manager.py b/nova/network/manager.py index b36dd59cf..39da031eb 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -563,6 +563,16 @@ class VlanManager(NetworkManager): # NOTE(vish): This makes ports unique accross the cloud, a more # robust solution would be to make them unique per ip net['vpn_public_port'] = vpn_start + index + network_ref = None + try: + network_ref = db.network_get_by_cidr(context, cidr) + except exception.NotFound: + pass + + if network_ref is not None: + raise ValueError(_('Network with cidr %s already exists' % + cidr)) + network_ref = self.db.network_create_safe(context, net) if network_ref: self._create_fixed_ips(context, network_ref['id']) -- cgit From 48c8b911899db4db36dfc2e0ddaf3410c3179071 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz <emaildericky@gmail.com> Date: Wed, 9 Mar 2011 19:03:58 +0100 Subject: fixed lp715427 --- bin/nova-manage | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 8ddfea5c2..45437d7e7 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -547,10 +547,11 @@ class NetworkCommands(object): def delete(self, fixed_range): """Deletes a network""" - network = db.network_get_by_cidr(context.get_admin_context(), fixed_range) + network = db.network_get_by_cidr(context.get_admin_context(), \ + fixed_range) if network.project_id is not None: - raise ValueError(_('Network must be disassociated from project %s' - ' before delete' %network.project_id)) + raise ValueError(_('Network must be disassociated from project %s' + ' before delete' % network.project_id)) db.network_delete_safe(context.get_admin_context(), network.id) class ServiceCommands(object): -- cgit From e44f085ed464a3397e3bf89a3e5355e538c71a65 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz <emaildericky@gmail.com> Date: Wed, 9 Mar 2011 19:16:26 +0100 Subject: Fixed pep8 issues --- bin/nova-manage | 5 +++-- nova/db/api.py | 7 +++++-- nova/db/sqlalchemy/api.py | 7 +++++-- nova/network/manager.py | 4 ++-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 45437d7e7..7dfc3c045 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -552,8 +552,9 @@ class NetworkCommands(object): if network.project_id is not None: raise ValueError(_('Network must be disassociated from project %s' ' before delete' % network.project_id)) - db.network_delete_safe(context.get_admin_context(), network.id) - + db.network_delete_safe(context.get_admin_context(), network.id) + + class ServiceCommands(object): """Enable and disable running services""" diff --git a/nova/db/api.py b/nova/db/api.py index 7ad99c1f4..5c34a02e4 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -459,6 +459,7 @@ def network_associate(context, project_id): """Associate a free network to a project.""" return IMPL.network_associate(context, project_id) + def network_count(context): """Return the number of networks.""" return IMPL.network_count(context) @@ -488,9 +489,9 @@ def network_create_safe(context, values): """ return IMPL.network_create_safe(context, values) + def network_delete_safe(context, network_id): - """Delete network with key network_id - + """Delete network with key network_id. This method assumes that the network is not associated with any project """ return IMPL.network_delete_safe(context, network_id) @@ -531,10 +532,12 @@ def network_get_by_bridge(context, bridge): """Get a network by bridge or raise if it does not exist.""" return IMPL.network_get_by_bridge(context, bridge) + def network_get_by_cidr(context, cidr): """Get a network by cidr or raise if it does not exist""" return IMPL.network_get_by_cidr(context, cidr) + def network_get_by_instance(context, instance_id): """Get a network by instance id or raise if it does not exist.""" return IMPL.network_get_by_instance(context, instance_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 90730d325..3a1162a17 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1042,12 +1042,14 @@ def network_create_safe(context, values): except IntegrityError: return None + @require_admin_context def network_delete_safe(context, network_id): session = get_session() with session.begin(): - network_ref = network_get(context, network_id=network_id, session=session) - session.delete(network_ref) + network_ref = network_get(context, network_id=network_id, \ + session=session) + session.delete(network_ref) @require_admin_context @@ -1134,6 +1136,7 @@ def network_get_by_cidr(context, cidr): cidr) return result + @require_admin_context def network_get_by_instance(_context, instance_id): session = get_session() diff --git a/nova/network/manager.py b/nova/network/manager.py index 39da031eb..3dfc48934 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -568,11 +568,11 @@ class VlanManager(NetworkManager): network_ref = db.network_get_by_cidr(context, cidr) except exception.NotFound: pass - + if network_ref is not None: raise ValueError(_('Network with cidr %s already exists' % cidr)) - + network_ref = self.db.network_create_safe(context, net) if network_ref: self._create_fixed_ips(context, network_ref['id']) -- cgit From 23369a63f4b74fb64bf57554a3fd8b15e3e2b49c Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Wed, 9 Mar 2011 14:31:23 -0500 Subject: Fixes uses of process_input --- nova/utils.py | 4 ++-- nova/virt/disk.py | 4 ++-- nova/virt/libvirt_conn.py | 11 ++++------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/nova/utils.py b/nova/utils.py index 7ddf056ea..0937522ec 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -131,7 +131,7 @@ def fetchfile(url, target): def execute(*cmd, **kwargs): process_input=kwargs.get('process_input', None) addl_env=kwargs.get('addl_env', None) - check_exit_code=kwargs.get('check_exit_code', True) + check_exit_code=kwargs.get('check_exit_code', 0) stdin=kwargs.get('stdin', subprocess.PIPE) stdout=kwargs.get('stdout', subprocess.PIPE) stderr=kwargs.get('stderr', subprocess.PIPE) @@ -151,7 +151,7 @@ def execute(*cmd, **kwargs): obj.stdin.close() if obj.returncode: LOG.debug(_("Result was %s") % obj.returncode) - if check_exit_code and obj.returncode != 0: + if check_exit_code is not None and obj.returncode != check_exit_code: (stdout, stderr) = result raise ProcessExecutionError(exit_code=obj.returncode, stdout=stdout, diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 203517275..a54cda003 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -175,8 +175,8 @@ def _inject_key_into_fs(key, fs): utils.execute('sudo', 'chown', 'root', sshdir) utils.execute('sudo', 'chmod', '700', sshdir) keyfile = os.path.join(sshdir, 'authorized_keys') - # TODO:EWINDISCH: not sure about the following /w execv patch - utils.execute('sudo', 'tee', '-a', keyfile, '\n' + key.strip() + '\n') + utils.execute('sudo', 'tee', '-a', keyfile, + process_input='\n' + key.strip() + '\n') def _inject_net_into_fs(net, fs): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 76f31f91a..6b555ecbb 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -485,13 +485,10 @@ class LibvirtConnection(object): port = random.randint(int(start_port), int(end_port)) # netcat will exit with 0 only if the port is in use, # so a nonzero return value implies it is unused - - # TODO(ewindisch): broken /w execvp patch. - # subprocess lets us do this, but utils.execute - # abstracts it away from us - cmd = 'netcat', '0.0.0.0', port, '-w', '1', '</dev/null || echo free' % (port) - stdout, stderr = utils.execute(cmd) - if stdout.strip() == 'free': + cmd = 'netcat', '0.0.0.0', port, '-w', '1' + try: + stdout, stderr = utils.execute(*cmd, process_input='') + except ProcessExecutionError: return port raise Exception(_('Unable to find an open port')) -- cgit From a9bd1b456332aaf5f6ab9942979485f2192b6f3e Mon Sep 17 00:00:00 2001 From: Dan Prince <dan.prince@rackspace.com> Date: Wed, 9 Mar 2011 14:53:44 -0500 Subject: Add password parameter to the set_admin_password call in the compute api. Updated servers password to use this parameter. --- nova/api/openstack/servers.py | 3 ++- nova/compute/api.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 7222285e0..41166f810 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -183,7 +183,8 @@ class Controller(wsgi.Controller): password = "%s%s" % (server['server']['name'][:4], utils.generate_password(12)) server['server']['adminPass'] = password - self.compute_api.set_admin_password(context, server['server']['id']) + self.compute_api.set_admin_password(context, server['server']['id'], + password) return server def update(self, req, id): diff --git a/nova/compute/api.py b/nova/compute/api.py index 33d25fc4b..a0bb2cf04 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -498,9 +498,10 @@ class API(base.Base): """Unrescue the given instance.""" self._cast_compute_message('unrescue_instance', context, instance_id) - def set_admin_password(self, context, instance_id): + def set_admin_password(self, context, instance_id, password=None): """Set the root/admin password for the given instance.""" - self._cast_compute_message('set_admin_password', context, instance_id) + self._cast_compute_message('set_admin_password', context, instance_id, + password) def inject_file(self, context, instance_id): """Write a file to the given instance.""" -- cgit From 3f723bcf54b4d779c66373dc8f69f43923dd586a Mon Sep 17 00:00:00 2001 From: Brian Waldon <brian.waldon@rackspace.com> Date: Wed, 9 Mar 2011 15:08:11 -0500 Subject: renaming wsgi.Request.best_match to best_match_content_type; correcting calls to that function in code from trunk --- nova/api/direct.py | 2 +- nova/api/openstack/__init__.py | 2 +- nova/api/openstack/faults.py | 2 +- nova/api/openstack/servers.py | 2 +- nova/tests/api/openstack/common.py | 1 + nova/tests/api/test_wsgi.py | 18 +++++++++--------- nova/wsgi.py | 4 ++-- 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/nova/api/direct.py b/nova/api/direct.py index 1d699f947..dfca250e0 100644 --- a/nova/api/direct.py +++ b/nova/api/direct.py @@ -206,7 +206,7 @@ class ServiceWrapper(wsgi.Controller): params = dict([(str(k), v) for (k, v) in params.iteritems()]) result = method(context, **params) if type(result) is dict or type(result) is list: - return self._serialize(result, req.best_match()) + return self._serialize(result, req.best_match_content_type()) else: return result diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 6e1a2a06c..197fcc619 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -125,5 +125,5 @@ class Versions(wsgi.Application): "application/xml": { "attributes": dict(version=["status", "id"])}} - content_type = req.best_match() + content_type = req.best_match_content_type() return wsgi.Serializer(metadata).serialize(response, content_type) diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py index 075fdb997..2fd733299 100644 --- a/nova/api/openstack/faults.py +++ b/nova/api/openstack/faults.py @@ -58,6 +58,6 @@ class Fault(webob.exc.HTTPException): # 'code' is an attribute on the fault tag itself metadata = {'application/xml': {'attributes': {fault_name: 'code'}}} serializer = wsgi.Serializer(metadata) - content_type = req.best_match() + content_type = req.best_match_content_type() self.wrapped_exc.body = serializer.serialize(fault_data, content_type) return self.wrapped_exc diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 8dd078a31..25c667532 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -217,7 +217,7 @@ class Controller(wsgi.Controller): 'rebuild': self._action_rebuild, } - input_dict = self._deserialize(req.body, req) + input_dict = self._deserialize(req.body, req.get_content_type()) for key in actions.keys(): if key in input_dict: return actions[key](input_dict, req, id) diff --git a/nova/tests/api/openstack/common.py b/nova/tests/api/openstack/common.py index 3f9c7d3cf..74bb8729a 100644 --- a/nova/tests/api/openstack/common.py +++ b/nova/tests/api/openstack/common.py @@ -28,6 +28,7 @@ def webob_factory(url): def web_request(url, method=None, body=None): req = webob.Request.blank("%s%s" % (base_url, url)) if method: + req.content_type = "application/json" req.method = method if body: req.body = json.dumps(body) diff --git a/nova/tests/api/test_wsgi.py b/nova/tests/api/test_wsgi.py index 7c0135656..b1a849cf9 100644 --- a/nova/tests/api/test_wsgi.py +++ b/nova/tests/api/test_wsgi.py @@ -139,48 +139,48 @@ class RequestTest(test.TestCase): def test_content_type_from_accept_xml(self): request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = "application/xml" - result = request.best_match() + result = request.best_match_content_type() self.assertEqual(result, "application/xml") request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = "application/json" - result = request.best_match() + result = request.best_match_content_type() self.assertEqual(result, "application/json") request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = "application/xml, application/json" - result = request.best_match() + result = request.best_match_content_type() self.assertEqual(result, "application/json") request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = \ "application/json; q=0.3, application/xml; q=0.9" - result = request.best_match() + result = request.best_match_content_type() self.assertEqual(result, "application/xml") def test_content_type_from_query_extension(self): request = wsgi.Request.blank('/tests/123.xml') - result = request.best_match() + result = request.best_match_content_type() self.assertEqual(result, "application/xml") request = wsgi.Request.blank('/tests/123.json') - result = request.best_match() + result = request.best_match_content_type() self.assertEqual(result, "application/json") request = wsgi.Request.blank('/tests/123.invalid') - result = request.best_match() + result = request.best_match_content_type() self.assertEqual(result, "application/json") def test_content_type_accept_and_query_extension(self): request = wsgi.Request.blank('/tests/123.xml') request.headers["Accept"] = "application/json" - result = request.best_match() + result = request.best_match_content_type() self.assertEqual(result, "application/xml") def test_content_type_accept_default(self): request = wsgi.Request.blank('/tests/123.unsupported') request.headers["Accept"] = "application/unsupported1" - result = request.best_match() + result = request.best_match_content_type() self.assertEqual(result, "application/json") diff --git a/nova/wsgi.py b/nova/wsgi.py index c3e08522d..2d18da8fb 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -85,7 +85,7 @@ class Server(object): class Request(webob.Request): - def best_match(self): + def best_match_content_type(self): """ Determine the most acceptable content-type based on the query extension then the Accept header @@ -354,7 +354,7 @@ class Controller(object): result = method(**arg_dict) if type(result) is dict: - content_type = req.best_match() + content_type = req.best_match_content_type() body = self._serialize(result, content_type) response = webob.Response() -- cgit From fc9840bae6200c8f89fb8a3ba0ab45663c872b3c Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Wed, 9 Mar 2011 15:33:20 -0500 Subject: execvp passes pep8 --- nova/console/xvp.py | 6 +++--- nova/crypto.py | 3 ++- nova/network/linux_net.py | 19 ++++++++++++------- nova/tests/test_virt.py | 2 +- nova/utils.py | 19 ++++++++++--------- nova/volume/driver.py | 4 ++-- 6 files changed, 30 insertions(+), 23 deletions(-) diff --git a/nova/console/xvp.py b/nova/console/xvp.py index 271dffa54..68d8c8565 100644 --- a/nova/console/xvp.py +++ b/nova/console/xvp.py @@ -134,9 +134,9 @@ class XVPConsoleProxy(object): logging.debug(_("Starting xvp")) try: utils.execute('xvp', - '-p',FLAGS.console_xvp_pid, - '-c',FLAGS.console_xvp_conf, - '-l',FLAGS.console_xvp_log) + '-p', FLAGS.console_xvp_pid, + '-c', FLAGS.console_xvp_conf, + '-l', FLAGS.console_xvp_log) except exception.ProcessExecutionError, err: logging.error(_("Error starting xvp: %s") % err) diff --git a/nova/crypto.py b/nova/crypto.py index 20bb570a5..717ea0041 100644 --- a/nova/crypto.py +++ b/nova/crypto.py @@ -120,7 +120,8 @@ def generate_key_pair(bits=1024): # bio = M2Crypto.BIO.MemoryBuffer() # key.save_pub_key_bio(bio) # public_key = bio.read() - # public_key, err = execute('ssh-keygen', '-y', '-f', '/dev/stdin', private_key) + # public_key, err = execute('ssh-keygen', '-y', '-f', + # '/dev/stdin', private_key) return (private_key, public_key, fingerprint) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index b66a1adb7..228a4d9ea 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -68,7 +68,8 @@ def metadata_forward(): _confirm_rule("PREROUTING", '-t', 'nat', '-s', '0.0.0.0/0', '-d', '169.254.169.254/32', '-p', 'tcp', '-m', 'tcp', '--dport', '80', '-j', 'DNAT', - '--to-destination', '%s:%s' % (FLAGS.ec2_dmz_host, FLAGS.ec2_port)) + '--to-destination', + '%s:%s' % (FLAGS.ec2_dmz_host, FLAGS.ec2_port)) def init_host(): @@ -86,7 +87,8 @@ def init_host(): _execute('sudo', 'iptables', '-D', 'FORWARD', '-j', 'nova_forward', check_exit_code=False) _execute('sudo', 'iptables', '-A', 'FORWARD', '-j', 'nova_forward') - _execute('sudo', 'iptables', '-N', 'nova_output', check_exit_code=False) + _execute('sudo', 'iptables', '-N', 'nova_output', + check_exit_code=False) _execute('sudo', 'iptables', '-D', 'OUTPUT', '-j', 'nova_output', check_exit_code=False) _execute('sudo', 'iptables', '-A', 'OUTPUT', '-j', 'nova_output') @@ -220,7 +222,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): # bridge for it to respond to reqests properly suffix = net_attrs['cidr'].rpartition('/')[2] out, err = _execute('sudo', 'ip', 'addr', 'add', - "%s/%s" % + "%s/%s" % (net_attrs['gateway'], suffix), 'brd', net_attrs['broadcast'], @@ -237,7 +239,8 @@ def ensure_bridge(bridge, interface, net_attrs=None): # bridge, then the bridge has to be in promiscuous # to forward packets properly. if(FLAGS.public_interface == bridge): - _execute('sudo', 'ip', 'link', 'set', 'dev', bridge, 'promisc', 'on') + _execute('sudo', 'ip', 'link', 'set', + 'dev', bridge, 'promisc', 'on') if interface: # NOTE(vish): This will break if there is already an ip on the # interface, so we move any ips to the bridge @@ -253,8 +256,10 @@ def ensure_bridge(bridge, interface, net_attrs=None): fields = line.split() if fields and fields[0] == "inet": params = ' '.join(fields[1:-1]) - _execute('sudo', 'ip', 'addr', 'del', params, 'dev', fields[-1]) - _execute('sudo', 'ip', 'addr', 'add', params, 'dev', bridge) + _execute('sudo', 'ip', 'addr', + 'del', params, 'dev', fields[-1]) + _execute('sudo', 'ip', 'addr', + 'add', params, 'dev', bridge) if gateway: _execute('sudo', 'route', 'add', '0.0.0.0', 'gw', gateway) out, err = _execute('sudo', 'brctl', 'addif', bridge, interface, @@ -397,7 +402,7 @@ def _device_exists(device): def _confirm_rule(chain, *cmd, **kwargs): - append=kwargs.get('append',False) + append = kwargs.get('append', False) """Delete and re-add iptables rule""" if FLAGS.use_nova_chains: chain = "nova_%s" % chain.lower() diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 7f1ad002e..dfa607f14 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -316,7 +316,7 @@ class IptablesFirewallTestCase(test.TestCase): # self.fw.add_instance(instance_ref) def fake_iptables_execute(*cmd, **kwargs): - process_input=kwargs.get('process_input', None) + process_input = kwargs.get('process_input', None) if cmd == ('sudo', 'ip6tables-save', '-t', 'filter'): return '\n'.join(self.in6_rules), None if cmd == ('sudo', 'iptables-save', '-t', 'filter'): diff --git a/nova/utils.py b/nova/utils.py index 0937522ec..3a4ec3c6a 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -40,7 +40,7 @@ import netaddr from eventlet import event from eventlet import greenthread from eventlet.green import subprocess - +None from nova import exception from nova.exception import ProcessExecutionError from nova import log as logging @@ -129,13 +129,13 @@ def fetchfile(url, target): def execute(*cmd, **kwargs): - process_input=kwargs.get('process_input', None) - addl_env=kwargs.get('addl_env', None) - check_exit_code=kwargs.get('check_exit_code', 0) - stdin=kwargs.get('stdin', subprocess.PIPE) - stdout=kwargs.get('stdout', subprocess.PIPE) - stderr=kwargs.get('stderr', subprocess.PIPE) - cmd=map(str,cmd) + process_input = kwargs.get('process_input', None) + addl_env = kwargs.get('addl_env', None) + check_exit_code = kwargs.get('check_exit_code', 0) + stdin = kwargs.get('stdin', subprocess.PIPE) + stdout = kwargs.get('stdout', subprocess.PIPE) + stderr = kwargs.get('stderr', subprocess.PIPE) + cmd = map(str, cmd) LOG.debug(_("Running cmd (subprocess): %s"), ' '.join(cmd)) env = os.environ.copy() @@ -151,7 +151,8 @@ def execute(*cmd, **kwargs): obj.stdin.close() if obj.returncode: LOG.debug(_("Result was %s") % obj.returncode) - if check_exit_code is not None and obj.returncode != check_exit_code: + if type(check_exit_code) == types.IntType \ + and obj.returncode != check_exit_code: (stdout, stderr) = result raise ProcessExecutionError(exit_code=obj.returncode, stdout=stdout, diff --git a/nova/volume/driver.py b/nova/volume/driver.py index e9bdf162f..45cc800e7 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -112,7 +112,7 @@ class VolumeDriver(object): # If the volume isn't present, then don't attempt to delete return True - self._try_execute('sudo', 'lvremove', '-f',"%s/%s" % + self._try_execute('sudo', 'lvremove', '-f', "%s/%s" % (FLAGS.volume_group, volume['name'])) @@ -256,7 +256,7 @@ class ISCSIDriver(VolumeDriver): self._sync_exec('sudo', 'ietadm', '--op', 'new', "--tid=%s" % iscsi_target, '--params', - "Name=%s" % iscsi-name, + "Name=%s" % iscsi_name, check_exit_code=False) self._sync_exec('sudo', 'ietadm', '--op', 'new', "--tid=%s" % iscsi_target, -- cgit From 3e61bf9963d7e98e8152d2eacfc4461d8cda309c Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Wed, 9 Mar 2011 21:43:35 +0000 Subject: remove the semaphore when there is no one waiting on it --- nova/tests/test_virt.py | 3 ++- nova/virt/libvirt_conn.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 113632a0c..56a271365 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -23,7 +23,6 @@ from xml.dom.minidom import parseString as xml_to_dom from nova import context from nova import db from nova import flags -from nova import log as logging from nova import test from nova import utils from nova.api.ec2 import cloud @@ -70,11 +69,13 @@ class CacheConcurrencyTestCase(test.TestCase): eventlet.sleep(0) try: self.assertFalse(done2.ready()) + self.assertTrue('fname' in conn._image_sems) finally: wait1.send() done1.wait() eventlet.sleep(0) self.assertTrue(done2.ready()) + self.assertFalse('fname' in conn._image_sems) def test_different_fname_concurrency(self): """Ensures that two different fname caches are concurrent""" diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index ecef7950a..69249ed57 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -539,6 +539,8 @@ class LibvirtConnection(object): with LibvirtConnection._image_sems[fname]: if not os.path.exists(base): fn(target=base, *args, **kwargs) + if not LibvirtConnection._image_sems[fname].locked(): + del LibvirtConnection._image_sems[fname] if cow: utils.execute('qemu-img create -f qcow2 -o ' -- cgit From e8554da80ac916f168461cb48078488700081c02 Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Wed, 9 Mar 2011 16:44:48 -0500 Subject: execvp: cleanup. --- nova/crypto.py | 6 +++--- .../networking/etc/xensource/scripts/vif_rules.py | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/nova/crypto.py b/nova/crypto.py index 717ea0041..2a8d4abca 100644 --- a/nova/crypto.py +++ b/nova/crypto.py @@ -105,7 +105,7 @@ def generate_key_pair(bits=1024): tmpdir = tempfile.mkdtemp() keyfile = os.path.join(tmpdir, 'temp') - utils.execute('ssh-keygen', '-q', '-b', '%d' % bits, '-N', '', + utils.execute('ssh-keygen', '-q', '-b', bits, '-N', '', '-f', keyfile) (out, err) = utils.execute('ssh-keygen', '-q', '-l', '-f', '%s.pub' % (keyfile)) @@ -147,9 +147,9 @@ def revoke_cert(project_id, file_name): os.chdir(ca_folder(project_id)) # NOTE(vish): potential race condition here utils.execute('openssl', 'ca', '-config', './openssl.cnf', '-revoke', - '%s' % file_name) + file_name) utils.execute('openssl', 'ca', '-gencrl', '-config', './openssl.cnf', - '-out', '%s' % FLAGS.crl_file) + '-out', FLAGS.crl_file) os.chdir(start) diff --git a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py index 2c34f7b1d..d2b2d61e6 100755 --- a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py +++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py @@ -52,7 +52,7 @@ def main(dom_id, command, only_this_vif=None): apply_iptables_rules(command, params) -def execute(command, return_stdout=False): +def execute(*command, return_stdout=False): devnull = open(os.devnull, 'w') proc = subprocess.Popen(command, close_fds=True, stdout=subprocess.PIPE, stderr=devnull) @@ -110,26 +110,26 @@ def apply_arptables_rules(command, params): def apply_ebtables_rules(command, params): ebtables = lambda *rule: execute("/sbin/ebtables", *rule) - ebtables('-D', 'FORWARD', '-p', '0806', '-o', '%(VIF)s' % params, - '--arp-ip-dst', '%(IP)s' % params, + ebtables('-D', 'FORWARD', '-p', '0806', '-o', params['VIF'], + '--arp-ip-dst', params['IP'], '-j', 'ACCEPT') ebtables('-D', 'FORWARD', '-p', '0800', '-o', - '%(VIF)s' % params, '--ip-dst', '%(IP)s' % params, + params['VIF'], '--ip-dst', params['IP'], '-j', 'ACCEPT') if command == 'online': ebtables('-A', 'FORWARD', '-p', '0806', - '-o', '%(VIF)s' % params - '--arp-ip-dst', '%(IP)s' % params, + '-o', params['VIF'], + '--arp-ip-dst', params['IP'], '-j', 'ACCEPT') ebtables('-A', 'FORWARD', '-p', '0800', - '-o', '%(VIF)s' % params, - '--ip-dst', '%(IP)s' % params, + '-o', params['VIF'], + '--ip-dst', params['IP'], '-j', 'ACCEPT') - ebtables('-D', 'FORWARD', '-s', '!', '%(MAC)s' % params, - '-i', '%(VIF)s' % params, '-j', 'DROP') + ebtables('-D', 'FORWARD', '-s', '!', params['MAC'], + '-i', params['VIF'], '-j', 'DROP') if command == 'online': - ebtables('-I', 'FORWARD', '1', '-s', '!', '%(MAC)s' % params, + ebtables('-I', 'FORWARD', '1', '-s', '!', params['MAC'], '-i', '%(VIF)s', '-j', 'DROP') -- cgit From 203e23ebebc73a98dc8e8497fd2b28d3a6bf01da Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 9 Mar 2011 14:13:52 -0800 Subject: initializing instance power state on launch to 0 (fixes EC2 API bug) --- nova/compute/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/compute/api.py b/nova/compute/api.py index a0bb2cf04..2358b562c 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -165,6 +165,7 @@ class API(base.Base): 'image_id': image_id, 'kernel_id': kernel_id or '', 'ramdisk_id': ramdisk_id or '', + 'state': "0", 'state_description': 'scheduling', 'user_id': context.user_id, 'project_id': context.project_id, -- cgit From 5f6a58c7c2a7359f67bc4e2c2eb6bb9cc0a9ff01 Mon Sep 17 00:00:00 2001 From: Eric Windisch <eric@cloudscaling.com> Date: Wed, 9 Mar 2011 17:22:54 -0500 Subject: execvp: fix docs --- doc/ext/nova_autodoc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/ext/nova_autodoc.py b/doc/ext/nova_autodoc.py index 5429bb656..3dd992d84 100644 --- a/doc/ext/nova_autodoc.py +++ b/doc/ext/nova_autodoc.py @@ -8,5 +8,6 @@ from nova import utils def setup(app): rootdir = os.path.abspath(app.srcdir + '/..') print "**Autodocumenting from %s" % rootdir - rv = utils.execute('cd %s && ./generate_autodoc_index.sh' % rootdir) + os.chdir(rootdir) + rv = utils.execute('./generate_autodoc_index.sh') print rv[0] -- cgit From 9822af58162dc520c4a17646a013560e422efcf9 Mon Sep 17 00:00:00 2001 From: Ken Pepple <ken.pepple@gmail.com> Date: Wed, 9 Mar 2011 14:54:57 -0800 Subject: maybe a int instead ? --- nova/compute/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 2358b562c..5334acfcf 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -165,7 +165,7 @@ class API(base.Base): 'image_id': image_id, 'kernel_id': kernel_id or '', 'ramdisk_id': ramdisk_id or '', - 'state': "0", + 'state': 0, 'state_description': 'scheduling', 'user_id': context.user_id, 'project_id': context.project_id, -- cgit From a83b4879f38d11634d405d0efe977d482abdc344 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya <vishvananda@gmail.com> Date: Thu, 10 Mar 2011 05:02:24 +0000 Subject: minor fixes from review --- nova/image/glance.py | 2 +- nova/image/s3.py | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/nova/image/glance.py b/nova/image/glance.py index fb383f5e6..15fca69b8 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -74,7 +74,7 @@ class GlanceImageService(service.BaseImageService): if name == cantidate.get('name'): image = cantidate break - if image == None: + if image is None: raise exception.NotFound return image diff --git a/nova/image/s3.py b/nova/image/s3.py index bf104c29a..bbc54c263 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -77,17 +77,17 @@ class S3ImageService(service.BaseImageService): # FIXME(vish): detail doesn't filter so we do it manually return self._filter(context, images) - @staticmethod - def _is_visible(context, image): + @classmethod + def _is_visible(cls, context, image): return (context.is_admin or context.project_id == image['properties']['owner_id'] or image['properties']['is_public'] == 'True') - @staticmethod - def _filter(context, images): + @classmethod + def _filter(cls, context, images): filtered = [] for image in images: - if not S3ImageService._is_visible(context, image): + if not cls._is_visible(context, image): continue filtered.append(image) return filtered @@ -148,7 +148,7 @@ class S3ImageService(service.BaseImageService): image_format = 'aki' image_type = 'kernel' kernel_id = None - except: + except Exception: kernel_id = None try: @@ -157,12 +157,12 @@ class S3ImageService(service.BaseImageService): image_format = 'ari' image_type = 'ramdisk' ramdisk_id = None - except: + except Exception: ramdisk_id = None try: arch = manifest.find("machine_configuration/architecture").text - except: + except Exception: arch = 'x86_64' properties = metadata['properties'] @@ -235,7 +235,7 @@ class S3ImageService(service.BaseImageService): @staticmethod def _decrypt_image(encrypted_filename, encrypted_key, encrypted_iv, - cloud_private_key, decrypted_filename): + cloud_private_key, decrypted_filename): key, err = utils.execute( 'openssl rsautl -decrypt -inkey %s' % cloud_private_key, process_input=encrypted_key, -- cgit From 1fa41c5c621f3190c8c2b1c3d885c95b6b627b23 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Thu, 10 Mar 2011 09:52:19 +0100 Subject: s/s.getuid()/os.getuid()/ --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index e25e4af4f..44b07213a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -463,7 +463,7 @@ class LibvirtConnection(object): console_log = os.path.join(FLAGS.instances_path, instance['name'], 'console.log') - utils.execute('sudo', 'chown', s.getuid(), console_log) + utils.execute('sudo', 'chown', os.getuid(), console_log) if FLAGS.libvirt_type == 'xen': # Xen is special -- cgit From 9e77a0c6f6b43494e0eb87a16f33cd566f0746d2 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Thu, 10 Mar 2011 09:55:45 +0100 Subject: Split dnsmasq and radvd commands into their respective argv's. --- nova/network/linux_net.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 228a4d9ea..9fd6c82de 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -424,30 +424,30 @@ def _remove_rule(chain, *cmd): def _dnsmasq_cmd(net): """Builds dnsmasq command""" - cmd = ['sudo -E dnsmasq', - ' --strict-order', - ' --bind-interfaces', - ' --conf-file=', - ' --domain=%s' % FLAGS.dhcp_domain, - ' --pid-file=%s' % _dhcp_file(net['bridge'], 'pid'), - ' --listen-address=%s' % net['gateway'], - ' --except-interface=lo', - ' --dhcp-range=%s,static,120s' % net['dhcp_start'], - ' --dhcp-hostsfile=%s' % _dhcp_file(net['bridge'], 'conf'), - ' --dhcp-script=%s' % FLAGS.dhcpbridge, - ' --leasefile-ro'] + cmd = ['sudo', '-E', 'dnsmasq', + '--strict-order', + '--bind-interfaces', + '--conf-file=', + '--domain=%s' % FLAGS.dhcp_domain, + '--pid-file=%s' % _dhcp_file(net['bridge'], 'pid'), + '--listen-address=%s' % net['gateway'], + '--except-interface=lo', + '--dhcp-range=%s,static,120s' % net['dhcp_start'], + '--dhcp-hostsfile=%s' % _dhcp_file(net['bridge'], 'conf'), + '--dhcp-script=%s' % FLAGS.dhcpbridge, + '--leasefile-ro'] if FLAGS.dns_server: - cmd.append(' -h -R --server=%s' % FLAGS.dns_server) - return ''.join(cmd) + cmd += ['-h', '-R', '--server=%s' % FLAGS.dns_server] + return cmd def _ra_cmd(net): """Builds radvd command""" - cmd = ['sudo -E radvd', -# ' -u nobody', - ' -C %s' % _ra_file(net['bridge'], 'conf'), - ' -p %s' % _ra_file(net['bridge'], 'pid')] - return ''.join(cmd) + cmd = ['sudo', '-E', 'radvd', +# '-u', 'nobody', + '-C', '%s' % _ra_file(net['bridge'], 'conf'), + '-p', '%s' % _ra_file(net['bridge'], 'pid')] + return cmd def _stop_dnsmasq(network): -- cgit From e575f5ddd46055f2e491606052493b6d648506f6 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Thu, 10 Mar 2011 10:16:07 +0100 Subject: Pass argv of dnsmasq and radvd to execute as individual args, not as a list. --- nova/network/linux_net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 9fd6c82de..e64c052f9 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -330,7 +330,7 @@ def update_dhcp(context, network_id): env = {'FLAGFILE': FLAGS.dhcpbridge_flagfile, 'DNSMASQ_INTERFACE': network_ref['bridge']} command = _dnsmasq_cmd(network_ref) - _execute(command, addl_env=env) + _execute(*command, addl_env=env) def update_ra(context, network_id): @@ -370,7 +370,7 @@ interface %s else: LOG.debug(_("Pid %d is stale, relaunching radvd"), pid) command = _ra_cmd(network_ref) - _execute(command) + _execute(*command) db.network_update(context, network_id, {"ra_server": utils.get_my_linklocal(network_ref['bridge'])}) -- cgit From 6601d52bfa501ac1ae266647be19fac2f6792efc Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Thu, 10 Mar 2011 11:35:42 +0100 Subject: Make nova.image.s3 catch up with the new execute syntax. --- nova/image/s3.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/nova/image/s3.py b/nova/image/s3.py index bbc54c263..85a2c651c 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -236,25 +236,32 @@ class S3ImageService(service.BaseImageService): @staticmethod def _decrypt_image(encrypted_filename, encrypted_key, encrypted_iv, cloud_private_key, decrypted_filename): - key, err = utils.execute( - 'openssl rsautl -decrypt -inkey %s' % cloud_private_key, - process_input=encrypted_key, - check_exit_code=False) + key, err = utils.execute('openssl', + 'rsautl', + '-decrypt', + '-inkey', '%s' % cloud_private_key, + process_input=encrypted_key, + check_exit_code=False) if err: raise exception.Error(_("Failed to decrypt private key: %s") % err) - iv, err = utils.execute( - 'openssl rsautl -decrypt -inkey %s' % cloud_private_key, - process_input=encrypted_iv, - check_exit_code=False) + iv, err = utils.execute('openssl', + 'rsautl', + '-decrypt', + '-inkey', '%s' % cloud_private_key, + process_input=encrypted_iv, + check_exit_code=False) if err: raise exception.Error(_("Failed to decrypt initialization " "vector: %s") % err) - _out, err = utils.execute( - 'openssl enc -d -aes-128-cbc -in %s -K %s -iv %s -out %s' - % (encrypted_filename, key, iv, decrypted_filename), - check_exit_code=False) + _out, err = utils.execute('openssl', 'enc', + '-d', '-aes-128-cbc', + '-in', '%s' % (encrypted_filename,), + '-K', '%s' % (key,), + '-iv', '%s' % (iv,), + '-out', '%s' % (decrypted_filename,), + check_exit_code=False) if err: raise exception.Error(_("Failed to decrypt image file " "%(image_file)s: %(err)s") % -- cgit From bd3411f88532619b760aa8f51379db2f9c1cf5d0 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Thu, 10 Mar 2011 11:59:50 +0100 Subject: More execvp fallout --- nova/objectstore/image.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/nova/objectstore/image.py b/nova/objectstore/image.py index 8013cbd9c..c90b5b54b 100644 --- a/nova/objectstore/image.py +++ b/nova/objectstore/image.py @@ -253,25 +253,34 @@ class Image(object): @staticmethod def decrypt_image(encrypted_filename, encrypted_key, encrypted_iv, cloud_private_key, decrypted_filename): - key, err = utils.execute( - 'openssl rsautl -decrypt -inkey %s' % cloud_private_key, - process_input=encrypted_key, - check_exit_code=False) + key, err = utils.execute('openssl', + 'rsautl', + '-decrypt', + '-inkey', '%s' % cloud_private_key, + process_input=encrypted_key, + check_exit_code=False) if err: raise exception.Error(_("Failed to decrypt private key: %s") % err) - iv, err = utils.execute( - 'openssl rsautl -decrypt -inkey %s' % cloud_private_key, - process_input=encrypted_iv, - check_exit_code=False) + iv, err = utils.execute('openssl', + 'rsautl', + '-decrypt', + '-inkey', '%s' % cloud_private_key, + process_input=encrypted_iv, + check_exit_code=False) if err: raise exception.Error(_("Failed to decrypt initialization " "vector: %s") % err) - _out, err = utils.execute( - 'openssl enc -d -aes-128-cbc -in %s -K %s -iv %s -out %s' - % (encrypted_filename, key, iv, decrypted_filename), - check_exit_code=False) + _out, err = utils.execute('openssl', + 'enc', + '-d', + '-aes-128-cbc', + '-in', '%s' % (encrypted_filename,), + '-K', '%s' % (key,), + '-iv', '%s' % (iv,), + '-out', '%s' % (decrypted_filename,), + check_exit_code=False) if err: raise exception.Error(_("Failed to decrypt image file " "%(image_file)s: %(err)s") % -- cgit -- cgit From b64cf7352a24d8ced69aa408f7ceadd9da71da14 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Thu, 10 Mar 2011 15:47:09 +0100 Subject: One more thing.. --- nova/network/linux_net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index e64c052f9..c0bd76adf 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -216,7 +216,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): _execute('sudo', 'brctl', 'setfd', bridge, 0) # _execute("sudo brctl setageing %s 10" % bridge) _execute('sudo', 'brctl', 'stp', bridge, 'off') - _execute('sudo', 'ip', 'link', 'set', bridge, up) + _execute('sudo', 'ip', 'link', 'set', bridge, 'up') if net_attrs: # NOTE(vish): The ip for dnsmasq has to be the first address on the # bridge for it to respond to reqests properly -- cgit From b38af111532717cbe9f4bef1d3c3d58e7082c8b9 Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Thu, 10 Mar 2011 16:25:18 +0100 Subject: Another little detail.. --- nova/virt/disk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index a54cda003..5d499c42c 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -51,7 +51,7 @@ def extend(image, size): return utils.execute('truncate', '-s', size, image) # NOTE(vish): attempts to resize filesystem - utils.execute('e2fsck', '-fp', mage, check_exit_code=False) + utils.execute('e2fsck', '-fp', image, check_exit_code=False) utils.execute('resize2fs', image, check_exit_code=False) -- cgit From 11f2d788fd63c66af0e992f7b75b61273c059bcb Mon Sep 17 00:00:00 2001 From: Soren Hansen <soren@linux2go.dk> Date: Thu, 10 Mar 2011 21:31:47 +0100 Subject: PEP8 --- nova/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/utils.py b/nova/utils.py index 3008a512e..87e726394 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -166,9 +166,9 @@ def execute(*cmd, **kwargs): stdout=stdout, stderr=stderr, cmd=' '.join(cmd)) - # NOTE(termie): this appears to be necessary to let the subprocess call - # clean something up in between calls, without it two - # execute calls in a row hangs the second one + # NOTE(termie): this appears to be necessary to let the subprocess + # call clean something up in between calls, without + # it two execute calls in a row hangs the second one greenthread.sleep(0) return result except ProcessExecutionError: -- cgit