summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--HACKING.rst1
-rw-r--r--nova/compute/manager.py5
-rw-r--r--nova/conductor/api.py6
-rw-r--r--nova/conductor/manager.py7
-rw-r--r--nova/conductor/rpcapi.py6
-rw-r--r--nova/db/api.py13
-rw-r--r--nova/db/sqlalchemy/api.py1
-rw-r--r--nova/service.py10
-rw-r--r--nova/servicegroup/api.py12
-rw-r--r--nova/servicegroup/drivers/db.py29
-rw-r--r--nova/tests/compute/test_compute.py29
-rw-r--r--nova/tests/conductor/test_conductor.py8
-rw-r--r--nova/virt/xenapi/agent.py9
-rwxr-xr-xtools/hacking.py13
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