Skip to content

Instantly share code, notes, and snippets.

@misterdorm
Last active November 25, 2015 05:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save misterdorm/5e9513bb1211b49e551c to your computer and use it in GitHub Desktop.
Save misterdorm/5e9513bb1211b49e551c to your computer and use it in GitHub Desktop.
Support for server groups in nova cells v1: Note this patch will probably not apply cleanly to stock nova, as it's rebased from a few other cells-related patches we also run. But it should be fairly trivial to rebase onto whatever flavor of nova you have.
diff --git a/nova/api/openstack/compute/contrib/server_groups.py b/nova/api/openstack/compute/contrib/server_groups.py
index 553f946..b9854c1 100644
--- a/nova/api/openstack/compute/contrib/server_groups.py
+++ b/nova/api/openstack/compute/contrib/server_groups.py
@@ -27,6 +27,7 @@ from nova.i18n import _
from nova.i18n import _LE
from nova import objects
from nova import utils
+from nova import compute
LOG = logging.getLogger(__name__)
@@ -47,6 +48,7 @@ class ServerGroupController(wsgi.Controller):
def __init__(self, ext_mgr):
self.ext_mgr = ext_mgr
+ self.api = compute.ServerGroupAPI()
def _format_server_group(self, context, group):
# the id field has its value as the uuid of the server group
@@ -158,11 +160,11 @@ class ServerGroupController(wsgi.Controller):
"server group"))
try:
- sg.destroy()
- except nova.exception.InstanceGroupNotFound as e:
+ self.api.delete(context, id)
+ except:
if quotas:
quotas.rollback()
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
+ raise
if quotas:
quotas.commit()
@@ -202,23 +204,17 @@ class ServerGroupController(wsgi.Controller):
msg = _("Quota exceeded, too many server groups.")
raise exc.HTTPForbidden(explanation=msg)
- vals = body['server_group']
- sg = objects.InstanceGroup(context)
- sg.project_id = context.project_id
- sg.user_id = context.user_id
try:
- sg.name = vals.get('name')
- sg.policies = vals.get('policies')
- sg.create()
- except ValueError as e:
+ sg = self.api.create(context, body['server_group'])
+ except:
if quotas:
quotas.rollback()
- raise exc.HTTPBadRequest(explanation=e)
+ raise
if quotas:
quotas.commit()
- return {'server_group': self._format_server_group(context, sg)}
+ return sg
class Server_groups(extensions.ExtensionDescriptor):
diff --git a/nova/api/openstack/compute/plugins/v3/server_groups.py b/nova/api/openstack/compute/plugins/v3/server_groups.py
index 6f3782e..aef43d3 100644
--- a/nova/api/openstack/compute/plugins/v3/server_groups.py
+++ b/nova/api/openstack/compute/plugins/v3/server_groups.py
@@ -24,6 +24,7 @@ from nova.api.openstack.compute.schemas.v3 import server_groups as schema
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api import validation
+from nova import compute
import nova.exception
from nova.i18n import _
from nova.i18n import _LE
@@ -46,6 +47,9 @@ def _authorize_context(req):
class ServerGroupController(wsgi.Controller):
"""The Server group API controller for the OpenStack API."""
+ def __init__(self):
+ self.api = compute.ServerGroupAPI()
+
def _format_server_group(self, context, group):
# the id field has its value as the uuid of the server group
# There is no 'uuid' key in server_group seen by clients.
@@ -101,7 +105,7 @@ class ServerGroupController(wsgi.Controller):
"server group"))
try:
- sg.destroy()
+ self.api.delete(context, id)
except nova.exception.InstanceGroupNotFound as e:
if quotas:
quotas.rollback()
@@ -139,21 +143,15 @@ class ServerGroupController(wsgi.Controller):
msg = _("Quota exceeded, too many server groups.")
raise exc.HTTPForbidden(explanation=msg)
- vals = body['server_group']
- sg = objects.InstanceGroup(context)
- sg.project_id = context.project_id
- sg.user_id = context.user_id
try:
- sg.name = vals.get('name')
- sg.policies = vals.get('policies')
- sg.create()
- except ValueError as e:
+ sg = self.api.create(context, body['server_group'])
+ except:
quotas.rollback()
- raise exc.HTTPBadRequest(explanation=e)
+ raise
quotas.commit()
- return {'server_group': self._format_server_group(context, sg)}
+ return sg
class ServerGroups(extensions.V3APIExtensionBase):
diff --git a/nova/cells/manager.py b/nova/cells/manager.py
index 438bb7c..a5fa07b 100644
--- a/nova/cells/manager.py
+++ b/nova/cells/manager.py
@@ -80,7 +80,7 @@ class CellsManager(manager.Manager):
Scheduling requests get passed to the scheduler class.
"""
- target = oslo_messaging.Target(version='1.36')
+ target = oslo_messaging.Target(version='1.36.1')
def __init__(self, *args, **kwargs):
LOG.warning(_LW('The cells feature of Nova is considered experimental '
@@ -653,6 +653,19 @@ class CellsManager(manager.Manager):
ctxt, cell_name, aggregate_id, host_name)
return self._response_to_aggregate(response)
+ def create_server_group(self, ctxt, server_group_id,
+ server_group, policy):
+ response = self.msg_runner.create_server_group(
+ ctxt, server_group_id, server_group, policy)
+
+ def delete_server_group(self, ctxt, server_group_id):
+ response = self.msg_runner.delete_server_group(
+ ctxt, server_group_id)
+
+ def server_group_add_members(self, ctxt, server_group_id, instance_uuid):
+ response = self.msg_runner.server_group_add_members(
+ ctxt, server_group_id, instance_uuid)
+
def flavor_create(self, ctxt, cell_name, values):
response = self.msg_runner.flavor_create(ctxt,
cell_name, values)
diff --git a/nova/cells/messaging.py b/nova/cells/messaging.py
index 505223a..7a71f97 100644
--- a/nova/cells/messaging.py
+++ b/nova/cells/messaging.py
@@ -618,6 +618,7 @@ class _BaseMessageMethods(base.Base):
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
self.consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
self.host_api = compute.HostAPI()
+ self.server_group_api = compute.api.ServerGroupAPI()
def task_log_get_all(self, message, task_name, period_beginning,
period_ending, host, state):
@@ -1288,6 +1289,22 @@ class _BroadcastMessageMethods(_BaseMessageMethods):
response = self.aggregate_api.get_aggregate_list(message.ctxt)
return jsonutils.to_primitive(response)
+ def create_server_group(self, message, server_group_id,
+ server_group, policy):
+ server_group_dict = { 'uuid': server_group_id,
+ 'name': server_group,
+ 'policies': policy }
+ response = self.server_group_api.create(message.ctxt, server_group_dict)
+ return jsonutils.to_primitive(response)
+
+ def delete_server_group(self, message, server_group_id):
+ response = self.server_group_api.delete(message.ctxt, server_group_id)
+ return jsonutils.to_primitive(response)
+
+ def server_group_add_members(self, message, server_group_id, instance_uuid):
+ response = self.server_group_api.add_members(message.ctxt, server_group_id,
+ instance_uuid)
+ return jsonutils.to_primitive(response)
_CELL_MESSAGE_TYPE_TO_MESSAGE_CLS = {'targeted': _TargetedMessage,
'broadcast': _BroadcastMessage,
@@ -1941,6 +1958,28 @@ class MessageRunner(object):
cell_name, need_response=True)
return message.process()
+ def create_server_group(self, ctxt, server_group_id,
+ server_group, policy):
+ method_kwargs = dict(server_group_id=server_group_id,
+ server_group=server_group,
+ policy=policy)
+ message = _BroadcastMessage(self, ctxt, 'create_server_group',
+ method_kwargs, 'down', run_locally=False, need_response=True)
+ return message.process()
+
+ def delete_server_group(self, ctxt, server_group_id):
+ method_kwargs = dict(server_group_id=server_group_id)
+ message = _BroadcastMessage(self, ctxt, 'delete_server_group',
+ method_kwargs, 'down', run_locally=False, need_response=True)
+ return message.process()
+
+ def server_group_add_members(self, ctxt, server_group_id, instance_uuid):
+ method_kwargs = dict(server_group_id=server_group_id,
+ instance_uuid=instance_uuid)
+ message = _BroadcastMessage(self, ctxt, 'server_group_add_members',
+ method_kwargs, 'down', run_locally=False, need_response=True)
+ return message.process()
+
def flavor_create(self, ctxt, cell_name, values):
message = _TargetedMessage(self, ctxt, 'flavor_create',
dict(values=values),
diff --git a/nova/cells/rpcapi.py b/nova/cells/rpcapi.py
index 2c1f717..615b23e 100644
--- a/nova/cells/rpcapi.py
+++ b/nova/cells/rpcapi.py
@@ -757,3 +757,22 @@ class CellsAPI(object):
cell_name=cell_name,
aggregate_id=aggregate_id,
host_name=host_name)
+
+ def create_server_group(self, ctxt, server_group_id,
+ server_group, policy):
+ cctxt = self.client.prepare(version='1.36.1')
+ return cctxt.cast(ctxt, 'create_server_group',
+ server_group_id=server_group_id,
+ server_group=server_group,
+ policy=policy)
+
+ def delete_server_group(self, ctxt, server_group_id):
+ cctxt = self.client.prepare(version='1.36.1')
+ return cctxt.cast(ctxt, 'delete_server_group',
+ server_group_id=server_group_id)
+
+ def server_group_add_members(self, ctxt, server_group_id, instance_uuid):
+ cctxt = self.client.prepare(version='1.36.1')
+ return cctxt.cast(ctxt, 'server_group_add_members',
+ server_group_id=server_group_id,
+ instance_uuid=instance_uuid)
diff --git a/nova/compute/__init__.py b/nova/compute/__init__.py
index d8a65ec..451149f 100644
--- a/nova/compute/__init__.py
+++ b/nova/compute/__init__.py
@@ -58,6 +58,16 @@ def AggregateAPI(*args, **kwargs):
class_name = compute_api_class.__module__ + ".AggregateAPI"
return importutils.import_object(class_name, *args, **kwargs)
+def ServerGroupAPI(*args, **kwargs):
+ """
+ Returns the 'ServerGroupAPI' class from the same module as the configured
+ compute api
+ """
+ compute_api_class_name = _get_compute_api_class_name()
+ compute_api_class = importutils.import_class(compute_api_class_name)
+ class_name = compute_api_class.__module__ + ".ServerGroupAPI"
+ return importutils.import_object(class_name, *args, **kwargs)
+
def InstanceActionAPI(*args, **kwargs):
"""Returns the 'InstanceActionAPI' class from the same module as the
diff --git a/nova/compute/api.py b/nova/compute/api.py
index ab0f60f..c15858d 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -76,6 +76,7 @@ from nova import servicegroup
from nova import utils
from nova.virt import hardware
from nova import volume
+from nova import compute
LOG = logging.getLogger(__name__)
@@ -283,6 +284,7 @@ class API(base.Base):
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
self._compute_task_api = None
self.servicegroup_api = servicegroup.API()
+ self.servergroup_api = compute.ServerGroupAPI()
self.notifier = rpc.get_notifier('compute', CONF.host)
if CONF.ephemeral_storage_encryption.enabled:
self.key_manager = keymgr.API()
@@ -938,7 +940,7 @@ class API(base.Base):
"group")
raise exception.QuotaError(msg)
- objects.InstanceGroup.add_members(context,
+ self.servergroup_api.add_members(context,
instance_group.uuid,
[instance.uuid])
@@ -3731,6 +3733,70 @@ class AggregateAPI(base.Base):
return aggregate
+class ServerGroupAPI(base.Base):
+ """The Server group API controller for the OpenStack API."""
+
+ def __init__(self, **kwargs):
+ self.compute_rpcapi = compute_rpcapi.ComputeAPI()
+ super(ServerGroupAPI, self).__init__(**kwargs)
+
+ def _format_server_group(self, context, group):
+ # the id field has its value as the uuid of the server group
+ # There is no 'uuid' key in server_group seen by clients.
+ # In addition, clients see policies as a ["policy-name"] list;
+ # and they see members as a ["server-id"] list.
+ server_group = {}
+ server_group['id'] = group.uuid
+ server_group['name'] = group.name
+ server_group['policies'] = group.policies or []
+ # NOTE(danms): This has been exposed to the user, but never used.
+ # Since we can't remove it, just make sure it's always empty.
+ server_group['metadata'] = {}
+ members = []
+ if group.members:
+ # Display the instances that are not deleted.
+ filters = {'uuid': group.members, 'deleted': False}
+ instances = objects.InstanceList.get_by_filters(
+ context, filters=filters)
+ members = [instance.uuid for instance in instances]
+ server_group['members'] = members
+ return server_group
+
+ def delete(self, context, id):
+ """Delete an server group."""
+ try:
+ sg = objects.InstanceGroup.get_by_uuid(context, id)
+ except nova.exception.InstanceGroupNotFound as e:
+ raise exc.HTTPNotFound(explanation=e.format_message())
+
+ try:
+ sg.destroy()
+ except nova.exception.InstanceGroupNotFound as e:
+ raise exc.HTTPNotFound(explanation=e.format_message())
+
+ return True
+
+ def create(self, context, server_group):
+ """Creates a new server group."""
+ sg = objects.InstanceGroup(context)
+ sg.project_id = context.project_id
+ sg.user_id = context.user_id
+ try:
+ sg.name = server_group.get('name')
+ sg.policies = server_group.get('policies')
+ if 'uuid' in server_group:
+ sg.uuid = server_group.get('uuid')
+ sg.create()
+
+ except ValueError as e:
+ raise exc.HTTPBadRequest(explanation=e)
+
+ return {'server_group': self._format_server_group(context, sg)}
+
+ def add_members(self, context, server_group_id, instance_uuid):
+ objects.InstanceGroup.get_by_uuid(context, server_group_id).add_members(context, server_group_id, instance_uuid)
+
+
class KeypairAPI(base.Base):
"""Subset of the Compute Manager API for managing key pairs."""
diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py
index 6a5f237..faec92a 100644
--- a/nova/compute/cells_api.py
+++ b/nova/compute/cells_api.py
@@ -682,3 +682,50 @@ class AggregateAPI(compute_api.AggregateAPI):
def remove_host_from_aggregate(self, context, aggregate_id, host_name):
return self._call_rpc_api(
context, 'remove_host_from_aggregate', aggregate_id, host_name)
+
+
+class ServerGroupAPI(compute_api.ServerGroupAPI):
+ """Sub-set of the Compute Manager API for managing server groups."""
+
+ def __init__(self, **kwargs):
+ super(ServerGroupAPI, self).__init__(**kwargs)
+ self.cells_rpcapi = cells_rpcapi.CellsAPI()
+
+ def _call_rpc_api(self, context, method_name,
+ *args, **kwargs):
+ method = getattr(self.cells_rpcapi, method_name)
+ return method(context, *args, **kwargs)
+
+ def delete(self, context, id):
+ """Delete an server group."""
+
+ """Delete the server group in all compute cells"""
+ cells_rc = self._call_rpc_api(
+ context, 'delete_server_group', id)
+
+ """Finally delete the server group in the API cell"""
+ return super(ServerGroupAPI, self).delete(context, id)
+
+ def create(self, context, server_group):
+ """Creates a new server group."""
+
+ """Create the server group in the API cell"""
+ localsg = super(ServerGroupAPI, self).create(context, server_group)
+
+ """Create server group in all compute cells."""
+ cells_rc = self._call_rpc_api(
+ context, 'create_server_group', localsg.get('server_group').get('id'),
+ server_group.get('name'),
+ server_group.get('policies'))
+
+ return localsg
+
+ def add_members(self, context, server_group_id, instance_uuid):
+ """Updates server group membership."""
+
+ """Update membership in all compute cells."""
+ cells_rc = self._call_rpc_api(
+ context, 'server_group_add_members', server_group_id, instance_uuid)
+
+ """Update membership in the API cell."""
+ super(ServerGroupAPI, self).add_members(context, server_group_id, instance_uuid)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment