diff options
-rw-r--r-- | HACKING.rst | 1 | ||||
-rw-r--r-- | nova/compute/manager.py | 5 | ||||
-rw-r--r-- | nova/conductor/api.py | 6 | ||||
-rw-r--r-- | nova/conductor/manager.py | 7 | ||||
-rw-r--r-- | nova/conductor/rpcapi.py | 6 | ||||
-rw-r--r-- | nova/db/api.py | 13 | ||||
-rw-r--r-- | nova/db/sqlalchemy/api.py | 1 | ||||
-rw-r--r-- | nova/service.py | 10 | ||||
-rw-r--r-- | nova/servicegroup/api.py | 12 | ||||
-rw-r--r-- | nova/servicegroup/drivers/db.py | 29 | ||||
-rw-r--r-- | nova/tests/compute/test_compute.py | 29 | ||||
-rw-r--r-- | nova/tests/conductor/test_conductor.py | 8 | ||||
-rw-r--r-- | nova/virt/xenapi/agent.py | 9 | ||||
-rwxr-xr-x | tools/hacking.py | 13 |
14 files changed, 123 insertions, 26 deletions
diff --git a/HACKING.rst b/HACKING.rst index be894f072..35493e55b 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -9,6 +9,7 @@ Nova Style Commandments General ------- - Put two newlines between top-level code (funcs, classes, etc) +- Use only UNIX style newlines ("\n"), not Windows style ("\r\n") - Put one newline between methods in classes and anywhere else - Long lines should be wrapped in parentheses in preference to using a backslash for line continuation. diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 384866cbe..86f41cd3c 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -463,6 +463,11 @@ class ComputeManager(manager.SchedulerDependentManager): except NotImplementedError: LOG.warning(_('Hypervisor driver does not support ' 'resume guests'), instance=instance) + except Exception: + # NOTE(vish): The instance failed to resume, so we set the + # instance to error and attempt to continue. + LOG.warning(_('Failed to resume instance'), instance=instance) + self._set_instance_error_state(context, instance['uuid']) elif drv_state == power_state.RUNNING: # VMwareAPI drivers will raise an exception diff --git a/nova/conductor/api.py b/nova/conductor/api.py index 31ee19601..138e72f70 100644 --- a/nova/conductor/api.py +++ b/nova/conductor/api.py @@ -285,6 +285,9 @@ class LocalAPI(object): return self._manager.compute_node_update(context, node, values, prune_stats) + def service_update(self, context, service, values): + return self._manager.service_update(context, service, values) + class API(object): """Conductor API that does updates via RPC to the ConductorManager.""" @@ -548,3 +551,6 @@ class API(object): def compute_node_update(self, context, node, values, prune_stats=False): return self.conductor_rpcapi.compute_node_update(context, node, values, prune_stats) + + def service_update(self, context, service, values): + return self.conductor_rpcapi.service_update(context, service, values) diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index 9b18d1e00..0ff2e1400 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -43,7 +43,7 @@ datetime_fields = ['launched_at', 'terminated_at'] class ConductorManager(manager.SchedulerDependentManager): """Mission: TBD.""" - RPC_API_VERSION = '1.33' + RPC_API_VERSION = '1.34' def __init__(self, *args, **kwargs): super(ConductorManager, self).__init__(service_name='conductor', @@ -310,3 +310,8 @@ class ConductorManager(manager.SchedulerDependentManager): result = self.db.compute_node_update(context, node['id'], values, prune_stats) return jsonutils.to_primitive(result) + + @rpc_common.client_exceptions(exception.ServiceNotFound) + def service_update(self, context, service, values): + svc = self.db.service_update(context, service['id'], values) + return jsonutils.to_primitive(svc) diff --git a/nova/conductor/rpcapi.py b/nova/conductor/rpcapi.py index 95e332840..6dc8aef04 100644 --- a/nova/conductor/rpcapi.py +++ b/nova/conductor/rpcapi.py @@ -66,6 +66,7 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy): 1.31 - Added migration_get_in_progress_by_host_and_node 1.32 - Added optional node to instance_get_all_by_host 1.33 - Added compute_node_create and compute_node_update + 1.34 - Added service_update """ BASE_RPC_API_VERSION = '1.0' @@ -316,3 +317,8 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy): msg = self.make_msg('compute_node_update', node=node_p, values=values, prune_stats=prune_stats) return self.call(context, msg, version='1.33') + + def service_update(self, context, service, values): + service_p = jsonutils.to_primitive(service) + msg = self.make_msg('service_update', service=service_p, values=values) + return self.call(context, msg, version='1.34') diff --git a/nova/db/api.py b/nova/db/api.py index 13873936b..d8a16c52d 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -759,12 +759,13 @@ def instance_info_cache_update(context, instance_uuid, values, :param values: = dict containing column values to update """ rv = IMPL.instance_info_cache_update(context, instance_uuid, values) - try: - cells_rpcapi.CellsAPI().instance_info_cache_update_at_top(context, - rv) - except Exception: - LOG.exception(_("Failed to notify cells of instance info cache " - "update")) + if update_cells: + try: + cells_rpcapi.CellsAPI().instance_info_cache_update_at_top( + context, rv) + except Exception: + LOG.exception(_("Failed to notify cells of instance info " + "cache update")) return rv diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 970332896..4a752d177 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -448,6 +448,7 @@ def service_update(context, service_id, values): service_ref = service_get(context, service_id, session=session) service_ref.update(values) service_ref.save(session=session) + return service_ref ################### diff --git a/nova/service.py b/nova/service.py index 0fde14baa..6e7e8bafd 100644 --- a/nova/service.py +++ b/nova/service.py @@ -411,7 +411,7 @@ class Service(object): self.db_allowed = db_allowed self.conductor_api = conductor.API(use_local=db_allowed) self.conductor_api.wait_until_ready(context.get_admin_context()) - self.servicegroup_api = servicegroup.API() + self.servicegroup_api = servicegroup.API(db_allowed=db_allowed) def start(self): verstr = version.version_string_with_package() @@ -421,12 +421,11 @@ class Service(object): self.model_disconnected = False ctxt = context.get_admin_context() try: - service_ref = self.conductor_api.service_get_by_args(ctxt, - self.host, - self.binary) + self.service_ref = self.conductor_api.service_get_by_args(ctxt, + self.host, self.binary) self.service_id = service_ref['id'] except exception.NotFound: - self._create_service_ref(ctxt) + self.service_ref = self._create_service_ref(ctxt) if self.backdoor_port is not None: self.manager.backdoor_port = self.backdoor_port @@ -479,6 +478,7 @@ class Service(object): } service = self.conductor_api.service_create(context, svc_values) self.service_id = service['id'] + return service def __getattr__(self, key): manager = self.__dict__.get('manager', None) diff --git a/nova/servicegroup/api.py b/nova/servicegroup/api.py index ebd0ee6ac..358b7dcbc 100644 --- a/nova/servicegroup/api.py +++ b/nova/servicegroup/api.py @@ -45,6 +45,15 @@ class API(object): @lockutils.synchronized('nova.servicegroup.api.new', 'nova-') def __new__(cls, *args, **kwargs): + '''Create an instance of the servicegroup API. + + args and kwargs are passed down to the servicegroup driver when it gets + created. No args currently exist, though. Valid kwargs are: + + db_allowed - Boolean. False if direct db access is not allowed and + alternative data access (conductor) should be used + instead. + ''' if not cls._driver: LOG.debug(_('ServiceGroup driver defined as an instance of %s'), @@ -55,7 +64,8 @@ class API(object): except KeyError: raise TypeError(_("unknown ServiceGroup driver name: %s") % driver_name) - cls._driver = importutils.import_object(driver_class) + cls._driver = importutils.import_object(driver_class, + *args, **kwargs) utils.check_isinstance(cls._driver, ServiceGroupDriver) # we don't have to check that cls._driver is not NONE, # check_isinstance does it diff --git a/nova/servicegroup/drivers/db.py b/nova/servicegroup/drivers/db.py index 075db3ed8..686ee728b 100644 --- a/nova/servicegroup/drivers/db.py +++ b/nova/servicegroup/drivers/db.py @@ -14,8 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from nova import conductor from nova import context -from nova import db from nova import exception from nova.openstack.common import cfg from nova.openstack.common import log as logging @@ -32,6 +32,10 @@ LOG = logging.getLogger(__name__) class DbDriver(api.ServiceGroupDriver): + def __init__(self, *args, **kwargs): + self.db_allowed = kwargs.get('db_allowed', True) + self.conductor_api = conductor.API(use_local=self.db_allowed) + def join(self, member_id, group_id, service=None): """Join the given service with it's group.""" @@ -53,6 +57,11 @@ class DbDriver(api.ServiceGroupDriver): Check whether a service is up based on last heartbeat. """ last_heartbeat = service_ref['updated_at'] or service_ref['created_at'] + if isinstance(last_heartbeat, basestring): + # NOTE(russellb) If this service_ref came in over rpc via + # conductor, then the timestamp will be a string and needs to be + # converted back to a datetime. + last_heartbeat = timeutils.parse_strtime(last_heartbeat) # Timestamps in DB are UTC. elapsed = utils.total_seconds(timeutils.utcnow() - last_heartbeat) LOG.debug('DB_Driver.is_up last_heartbeat = %(lhb)s elapsed = %(el)s', @@ -66,7 +75,8 @@ class DbDriver(api.ServiceGroupDriver): LOG.debug(_('DB_Driver: get_all members of the %s group') % group_id) rs = [] ctxt = context.get_admin_context() - for service in db.service_get_all_by_topic(ctxt, group_id): + services = self.conductor_api.service_get_all_by_topic(ctxt, group_id) + for service in services: if self.is_up(service): rs.append(service['host']) return rs @@ -76,18 +86,11 @@ class DbDriver(api.ServiceGroupDriver): ctxt = context.get_admin_context() state_catalog = {} try: - try: - service_ref = db.service_get(ctxt, service.service_id) - except exception.NotFound: - LOG.debug(_('The service database object disappeared, ' - 'Recreating it.')) - service._create_service_ref(ctxt) - service_ref = db.service_get(ctxt, service.service_id) - - state_catalog['report_count'] = service_ref['report_count'] + 1 + report_count = service.service_ref['report_count'] + 1 + state_catalog['report_count'] = report_count - db.service_update(ctxt, - service.service_id, state_catalog) + service.service_ref = self.conductor_api.service_update(ctxt, + service.service_ref, state_catalog) # TODO(termie): make this pattern be more elegant. if getattr(service, 'model_disconnected', False): diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index d8c500e80..092fd940a 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -3306,6 +3306,35 @@ class ComputeTestCase(BaseTestCase): self.mox.VerifyAll() self.mox.UnsetStubs() + def test_init_instance_failed_resume_sets_error(self): + instance = { + 'uuid': 'fake-uuid', + 'info_cache': None, + 'power_state': power_state.RUNNING, + 'vm_state': vm_states.ACTIVE, + } + self.flags(resume_guests_state_on_host_boot=True) + self.mox.StubOutWithMock(self.compute, '_get_power_state') + self.mox.StubOutWithMock(self.compute.driver, 'plug_vifs') + self.mox.StubOutWithMock(self.compute.driver, + 'resume_state_on_host_boot') + self.mox.StubOutWithMock(self.compute, + '_get_instance_volume_block_device_info') + self.mox.StubOutWithMock(self.compute, + '_set_instance_error_state') + self.compute._get_power_state(mox.IgnoreArg(), + instance).AndReturn(power_state.SHUTDOWN) + self.compute.driver.plug_vifs(instance, mox.IgnoreArg()) + self.compute._get_instance_volume_block_device_info(mox.IgnoreArg(), + instance['uuid']).AndReturn('fake-bdm') + self.compute.driver.resume_state_on_host_boot(mox.IgnoreArg(), + instance, mox.IgnoreArg(), + 'fake-bdm').AndRaise(test.TestingException) + self.compute._set_instance_error_state(mox.IgnoreArg(), + instance['uuid']) + self.mox.ReplayAll() + self.compute._init_instance('fake-context', instance) + def test_get_instances_on_driver(self): fake_context = context.get_admin_context() diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py index b29db92e7..d010b454f 100644 --- a/nova/tests/conductor/test_conductor.py +++ b/nova/tests/conductor/test_conductor.py @@ -747,6 +747,14 @@ class ConductorAPITestCase(_BaseTestCase, test.TestCase): def test_service_destroy(self): self._test_stubbed('service_destroy', '', returns=False) + def test_service_update(self): + ctxt = self.context + self.mox.StubOutWithMock(db, 'service_update') + db.service_update(ctxt, '', {}).AndReturn('fake-result') + self.mox.ReplayAll() + result = self.conductor.service_update(self.context, {'id': ''}, {}) + self.assertEqual(result, 'fake-result') + def test_instance_get_all_by_host(self): self._test_stubbed('instance_get_all_by_host', self.context.elevated(), 'host') diff --git a/nova/virt/xenapi/agent.py b/nova/virt/xenapi/agent.py index 61cfa9631..ef08edbc1 100644 --- a/nova/virt/xenapi/agent.py +++ b/nova/virt/xenapi/agent.py @@ -21,6 +21,9 @@ import os import time import uuid +from nova.api.metadata import password +from nova import context +from nova import crypto from nova.openstack.common import cfg from nova.openstack.common import jsonutils from nova.openstack.common import log as logging @@ -207,6 +210,12 @@ class XenAPIBasedAgent(object): LOG.error(msg, instance=self.instance) raise Exception(msg) + sshkey = self.instance.get('key_data') + if sshkey: + enc = crypto.ssh_encrypt_text(sshkey, new_pass) + password.set_password(context.get_admin_context(), + self.instance['uuid'], base64.b64encode(enc)) + return resp['message'] def inject_file(self, path, contents): diff --git a/tools/hacking.py b/tools/hacking.py index ed22956eb..cfdd1b5b1 100755 --- a/tools/hacking.py +++ b/tools/hacking.py @@ -359,6 +359,19 @@ def nova_docstring_multiline_end(physical_line): return (pos, "N403: multi line docstring end on new line") +def nova_no_cr(physical_line): + r"""Check that we only use newlines not cariage returns. + + Okay: import os\nimport sys + # pep8 doesn't yet replace \r in strings, will work on an + # upstream fix + N901 import os\r\nimport sys + """ + pos = physical_line.find('\r') + if pos != -1 and pos == (len(physical_line) - 2): + return (pos, "N901: Windows style line endings not allowed in code") + + FORMAT_RE = re.compile("%(?:" "%|" # Ignore plain percents "(\(\w+\))?" # mapping key |