mirror of
https://github.com/gryf/openstack.git
synced 2025-12-17 11:30:24 +01:00
Allow compute nodes to be associated with host agg patch
This commit is contained in:
@@ -0,0 +1,247 @@
|
|||||||
|
From cc4070f78b764af439b429908a97f82e620803c9 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Roman Dobosz <roman.dobosz@intel.com>
|
||||||
|
Date: Wed, 27 Dec 2017 13:51:25 +0100
|
||||||
|
Subject: [PATCH] allow compute nodes to be associated with host agg
|
||||||
|
|
||||||
|
This is basically an Ocata backport patch from Jay Pipes:
|
||||||
|
https://review.openstack.org/#/c/526753
|
||||||
|
---
|
||||||
|
nova/compute/api.py | 37 +++++-
|
||||||
|
nova/tests/functional/compute/__init__.py | 0
|
||||||
|
.../tests/functional/compute/test_aggregate_api.py | 139 +++++++++++++++++++++
|
||||||
|
nova/tests/unit/compute/test_compute.py | 4 +-
|
||||||
|
4 files changed, 174 insertions(+), 6 deletions(-)
|
||||||
|
create mode 100644 nova/tests/functional/compute/__init__.py
|
||||||
|
create mode 100644 nova/tests/functional/compute/test_aggregate_api.py
|
||||||
|
|
||||||
|
diff --git a/nova/compute/api.py b/nova/compute/api.py
|
||||||
|
index 36de3abc34..b064f26a45 100644
|
||||||
|
--- a/nova/compute/api.py
|
||||||
|
+++ b/nova/compute/api.py
|
||||||
|
@@ -4540,6 +4540,32 @@ class AggregateAPI(base.Base):
|
||||||
|
availability_zones.update_host_availability_zone_cache(context,
|
||||||
|
host_name)
|
||||||
|
|
||||||
|
+ def _service_or_compute_node_exists(self, ctx, host_or_node):
|
||||||
|
+ """
|
||||||
|
+ Returns True if a service host or compute node record could be found
|
||||||
|
+ for the supplied host_or_node string. We first check to see if a
|
||||||
|
+ service record can be found with the host matching the host_or_node
|
||||||
|
+ parameter by looking at the host mapping records in the API database.
|
||||||
|
+ If we don't find a service record there, we then ask all cell databases
|
||||||
|
+ to find a compute node with a hypervisor_hostname matching the supplied
|
||||||
|
+ host_or_node parameter.
|
||||||
|
+ """
|
||||||
|
+ # NOTE(gryf): we don't handle cells in Ocata yet
|
||||||
|
+ try:
|
||||||
|
+ objects.Service.get_by_compute_host(ctx, host_or_node)
|
||||||
|
+ return True
|
||||||
|
+ except exception.HostMappingNotFound:
|
||||||
|
+ pass
|
||||||
|
+
|
||||||
|
+ found_nodes = (len(objects.ComputeNodeList
|
||||||
|
+ .get_by_hypervisor(ctx, host_or_node)))
|
||||||
|
+
|
||||||
|
+ if found_nodes > 1:
|
||||||
|
+ LOG.debug("Searching for compute nodes matching %s "
|
||||||
|
+ "found %d results but expected 1 result.",
|
||||||
|
+ host_or_node, len(found_nodes))
|
||||||
|
+ return found_nodes == 1
|
||||||
|
+
|
||||||
|
@wrap_exception()
|
||||||
|
def add_host_to_aggregate(self, context, aggregate_id, host_name):
|
||||||
|
"""Adds the host to an aggregate."""
|
||||||
|
@@ -4548,8 +4574,9 @@ class AggregateAPI(base.Base):
|
||||||
|
compute_utils.notify_about_aggregate_update(context,
|
||||||
|
"addhost.start",
|
||||||
|
aggregate_payload)
|
||||||
|
- # validates the host; ComputeHostNotFound is raised if invalid
|
||||||
|
- objects.Service.get_by_compute_host(context, host_name)
|
||||||
|
+
|
||||||
|
+ if not self._service_or_compute_node_exists(context, host_name):
|
||||||
|
+ raise exception.ComputeHostNotFound(host=host_name)
|
||||||
|
|
||||||
|
aggregate = objects.Aggregate.get_by_id(context, aggregate_id)
|
||||||
|
self.is_safe_to_update_az(context, aggregate.metadata,
|
||||||
|
@@ -4575,8 +4602,10 @@ class AggregateAPI(base.Base):
|
||||||
|
compute_utils.notify_about_aggregate_update(context,
|
||||||
|
"removehost.start",
|
||||||
|
aggregate_payload)
|
||||||
|
- # validates the host; ComputeHostNotFound is raised if invalid
|
||||||
|
- objects.Service.get_by_compute_host(context, host_name)
|
||||||
|
+
|
||||||
|
+ if not self._service_or_compute_node_exists(context, host_name):
|
||||||
|
+ raise exception.ComputeHostNotFound(host=host_name)
|
||||||
|
+
|
||||||
|
aggregate = objects.Aggregate.get_by_id(context, aggregate_id)
|
||||||
|
aggregate.delete_host(host_name)
|
||||||
|
self.scheduler_client.update_aggregates(context, [aggregate])
|
||||||
|
diff --git a/nova/tests/functional/compute/__init__.py b/nova/tests/functional/compute/__init__.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..e69de29bb2
|
||||||
|
diff --git a/nova/tests/functional/compute/test_aggregate_api.py b/nova/tests/functional/compute/test_aggregate_api.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..aeaf3958f2
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/nova/tests/functional/compute/test_aggregate_api.py
|
||||||
|
@@ -0,0 +1,139 @@
|
||||||
|
+# 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.compute import api as compute_api
|
||||||
|
+from nova import context
|
||||||
|
+from nova import exception
|
||||||
|
+from nova import objects
|
||||||
|
+from nova import test
|
||||||
|
+from nova.tests import fixtures as nova_fixtures
|
||||||
|
+from nova.tests import uuidsentinel as uuids
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class ComputeAggregateAPIMultiCellTestCase(test.NoDBTestCase):
|
||||||
|
+ """Tests for the AggregateAPI with multiple cells allowing either service
|
||||||
|
+ hosts or compute nodes to be associated with an aggregate.
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ USES_DB_SELF = True
|
||||||
|
+
|
||||||
|
+ def setUp(self):
|
||||||
|
+ super(ComputeAggregateAPIMultiCellTestCase, self).setUp()
|
||||||
|
+ self.agg_api = compute_api.AggregateAPI()
|
||||||
|
+ self.useFixture(nova_fixtures.Database(database='api'))
|
||||||
|
+ celldbs = nova_fixtures.CellDatabases()
|
||||||
|
+ celldbs.add_cell_database(objects.CellMapping.CELL0_UUID)
|
||||||
|
+ celldbs.add_cell_database(uuids.cell1, default=True)
|
||||||
|
+ celldbs.add_cell_database(uuids.cell2)
|
||||||
|
+ self.useFixture(celldbs)
|
||||||
|
+
|
||||||
|
+ self.ctxt = context.get_admin_context()
|
||||||
|
+ cell0 = objects.CellMapping(
|
||||||
|
+ context=self.ctxt, uuid=objects.CellMapping.CELL0_UUID,
|
||||||
|
+ database_connection=objects.CellMapping.CELL0_UUID,
|
||||||
|
+ transport_url='none:///')
|
||||||
|
+ cell0.create()
|
||||||
|
+ cell1 = objects.CellMapping(
|
||||||
|
+ context=self.ctxt, uuid=uuids.cell1,
|
||||||
|
+ database_connection=uuids.cell1, transport_url='none:///')
|
||||||
|
+ cell1.create()
|
||||||
|
+ cell2 = objects.CellMapping(
|
||||||
|
+ context=self.ctxt, uuid=uuids.cell2,
|
||||||
|
+ database_connection=uuids.cell2, transport_url='none:///')
|
||||||
|
+ cell2.create()
|
||||||
|
+ self.cell_mappings = (cell0, cell1, cell2)
|
||||||
|
+
|
||||||
|
+ # create two Ironic nodes managed by a single nova-compute service host
|
||||||
|
+ # in each of the non-cell0 cells
|
||||||
|
+ for cell_id, cell in enumerate(self.cell_mappings[1:]):
|
||||||
|
+ with context.target_cell(self.ctxt, cell) as cctxt:
|
||||||
|
+ hostname = 'ironic_host_cell%s' % (cell_id + 1)
|
||||||
|
+ svc = objects.Service(cctxt, host=hostname,
|
||||||
|
+ binary='nova-compute',
|
||||||
|
+ topic='nova-compute')
|
||||||
|
+ svc.create()
|
||||||
|
+ for node_id in (1, 2):
|
||||||
|
+ nodename = 'ironic_node_cell%s_%s' % (cell_id + 1, node_id)
|
||||||
|
+ compute_node_uuid = getattr(uuids, nodename)
|
||||||
|
+ node = objects.ComputeNode(
|
||||||
|
+ cctxt, uuid=compute_node_uuid, host=hostname,
|
||||||
|
+ vcpus=2, memory_mb=2048, local_gb=128, vcpus_used=0,
|
||||||
|
+ memory_mb_used=0, local_gb_used=0, cpu_info='{}',
|
||||||
|
+ hypervisor_type='ironic', hypervisor_version=10,
|
||||||
|
+ hypervisor_hostname=nodename)
|
||||||
|
+ node.create()
|
||||||
|
+
|
||||||
|
+ # create a compute node for VMs along with a corresponding nova-compute
|
||||||
|
+ # service host in cell1
|
||||||
|
+ with context.target_cell(self.ctxt, cell1) as cctxt:
|
||||||
|
+ hostname = 'vm_host_cell1_1'
|
||||||
|
+ svc = objects.Service(cctxt, host=hostname,
|
||||||
|
+ binary='nova-compute',
|
||||||
|
+ topic='nova-compute')
|
||||||
|
+ svc.create()
|
||||||
|
+ compute_node_uuid = getattr(uuids, hostname)
|
||||||
|
+ node = objects.ComputeNode(
|
||||||
|
+ cctxt, uuid=compute_node_uuid, host=hostname,
|
||||||
|
+ vcpus=2, memory_mb=2048, local_gb=128, vcpus_used=0,
|
||||||
|
+ memory_mb_used=0, local_gb_used=0, cpu_info='{}',
|
||||||
|
+ hypervisor_type='libvirt', hypervisor_version=10,
|
||||||
|
+ hypervisor_hostname=hostname)
|
||||||
|
+ node.create()
|
||||||
|
+
|
||||||
|
+ def test_service_hostname(self):
|
||||||
|
+ """Test to make sure we can associate and disassociate an aggregate
|
||||||
|
+ with a service host.
|
||||||
|
+ """
|
||||||
|
+ agg = objects.Aggregate(self.ctxt, name="rack1_baremetal")
|
||||||
|
+ agg.create()
|
||||||
|
+
|
||||||
|
+ agg_id = agg.id
|
||||||
|
+
|
||||||
|
+ # There is no such service host called unknown_host_cell1, so should
|
||||||
|
+ # get back a ComputeHostNotFound
|
||||||
|
+ self.assertRaises(exception.ComputeHostNotFound,
|
||||||
|
+ self.agg_api.add_host_to_aggregate, self.ctxt,
|
||||||
|
+ agg_id, 'unknown_host_cell1')
|
||||||
|
+ self.assertRaises(exception.ComputeHostNotFound,
|
||||||
|
+ self.agg_api.remove_host_from_aggregate, self.ctxt,
|
||||||
|
+ agg_id, 'unknown_host_cell1')
|
||||||
|
+
|
||||||
|
+ hosts = ('ironic_host_cell1', 'ironic_host_cell2', 'vm_host_cell1_1')
|
||||||
|
+ for service_host in hosts:
|
||||||
|
+ self.agg_api.add_host_to_aggregate(self.ctxt, agg_id, service_host)
|
||||||
|
+ self.agg_api.remove_host_from_aggregate(self.ctxt, agg_id,
|
||||||
|
+ service_host)
|
||||||
|
+
|
||||||
|
+ def test_compute_nodename(self):
|
||||||
|
+ """Test to make sure we can associate and disassociate an aggregate
|
||||||
|
+ with a compute node by its hypervisor_hostname.
|
||||||
|
+ """
|
||||||
|
+ agg = objects.Aggregate(self.ctxt, name="rack1_baremetal")
|
||||||
|
+ agg.create()
|
||||||
|
+
|
||||||
|
+ agg_id = agg.id
|
||||||
|
+
|
||||||
|
+ # There is no such compute node called unknown_host_cell1, so should
|
||||||
|
+ # get back a ComputeHostNotFound
|
||||||
|
+ self.assertRaises(exception.ComputeHostNotFound,
|
||||||
|
+ self.agg_api.add_host_to_aggregate, self.ctxt,
|
||||||
|
+ agg_id, getattr(uuids, 'unknown_node_cell1'))
|
||||||
|
+ self.assertRaises(exception.ComputeHostNotFound,
|
||||||
|
+ self.agg_api.remove_host_from_aggregate, self.ctxt,
|
||||||
|
+ agg_id, getattr(uuids, 'unknown_host_cell1'))
|
||||||
|
+
|
||||||
|
+ nodenames = ('ironic_node_cell1_2', 'ironic_node_cell2_1',
|
||||||
|
+ 'vm_host_cell1_1')
|
||||||
|
+ for nodename in nodenames:
|
||||||
|
+ self.agg_api.add_host_to_aggregate(self.ctxt, agg_id, nodename)
|
||||||
|
+ self.agg_api.remove_host_from_aggregate(self.ctxt, agg_id,
|
||||||
|
+ nodename)
|
||||||
|
diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py
|
||||||
|
index a71f337eb5..a1c26d6e4d 100644
|
||||||
|
--- a/nova/tests/unit/compute/test_compute.py
|
||||||
|
+++ b/nova/tests/unit/compute/test_compute.py
|
||||||
|
@@ -11193,11 +11193,11 @@ class ComputeAPIAggrTestCase(BaseTestCase):
|
||||||
|
mock_az.assert_called_with(self.context, host_to_remove)
|
||||||
|
|
||||||
|
def test_remove_host_from_aggregate_raise_not_found(self):
|
||||||
|
- # Ensure ComputeHostNotFound is raised when removing invalid host.
|
||||||
|
+ # Ensure HostMappingNotFound is raised when removing invalid host.
|
||||||
|
_create_service_entries(self.context, [['fake_zone', ['fake_host']]])
|
||||||
|
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
|
||||||
|
'fake_zone')
|
||||||
|
- self.assertRaises(exception.ComputeHostNotFound,
|
||||||
|
+ self.assertRaises(exception.HostMappingNotFound,
|
||||||
|
self.api.remove_host_from_aggregate,
|
||||||
|
self.context, aggr.id, 'invalid_host')
|
||||||
|
|
||||||
|
--
|
||||||
|
2.13.6
|
||||||
|
|
||||||
Reference in New Issue
Block a user