Browse Source

Add RPCs to support volume connector operation

This patch adds the following two RPCs in order to support volume
connector operations.

- update_volume_connector()
  This function is called to update the information about a volume
  connector that is stored in the database.

- destroy_volume_connector()
  This function is called to remove a volume connector from the database.

Co-Authored-By: Tomoki Sekiyama <tomoki.sekiyama.qu@hitachi.com>
Co-Authored-By: Stephanie Miller <stephane@alum.mit.edu>
Co-Authored-By: Ruby Loo <ruby.loo@intel.com>
Change-Id: I3debe98ea78e159a81f53d0a3a3a49fe0c8663f6
Partial-Bug: 1526231
tags/7.0.0
Satoru Moriya 3 years ago
parent
commit
20fe26335e

+ 64
- 1
ironic/conductor/manager.py View File

@@ -82,7 +82,7 @@ class ConductorManager(base_manager.BaseConductorManager):
82 82
     """Ironic Conductor manager main class."""
83 83
 
84 84
     # NOTE(rloo): This must be in sync with rpcapi.ConductorAPI's.
85
-    RPC_API_VERSION = '1.34'
85
+    RPC_API_VERSION = '1.35'
86 86
 
87 87
     target = messaging.Target(version=RPC_API_VERSION)
88 88
 
@@ -1537,6 +1537,33 @@ class ConductorManager(base_manager.BaseConductorManager):
1537 1537
                          '%(node)s'),
1538 1538
                      {'portgroup': portgroup.uuid, 'node': task.node.uuid})
1539 1539
 
1540
+    @METRICS.timer('ConductorManager.destroy_volume_connector')
1541
+    @messaging.expected_exceptions(exception.NodeLocked,
1542
+                                   exception.NodeNotFound,
1543
+                                   exception.VolumeConnectorNotFound)
1544
+    def destroy_volume_connector(self, context, connector):
1545
+        """Delete a volume connector.
1546
+
1547
+        :param context: request context
1548
+        :param connector: volume connector object
1549
+        :raises: NodeLocked if node is locked by another conductor
1550
+        :raises: NodeNotFound if the node associated with the connector does
1551
+                 not exist
1552
+        :raises: VolumeConnectorNotFound if the volume connector cannot be
1553
+                 found
1554
+        """
1555
+        LOG.debug('RPC destroy_volume_connector called for volume connector '
1556
+                  '%(connector)s',
1557
+                  {'connector': connector.uuid})
1558
+        with task_manager.acquire(context, connector.node_id,
1559
+                                  purpose='volume connector deletion') as task:
1560
+            connector.destroy()
1561
+            LOG.info(_LI('Successfully deleted volume connector '
1562
+                         '%(connector)s. '
1563
+                         'The node associated with the connector was '
1564
+                         '%(node)s'),
1565
+                     {'connector': connector.uuid, 'node': task.node.uuid})
1566
+
1540 1567
     @METRICS.timer('ConductorManager.get_console_information')
1541 1568
     @messaging.expected_exceptions(exception.NodeLocked,
1542 1569
                                    exception.UnsupportedDriverExtension,
@@ -1833,6 +1860,42 @@ class ConductorManager(base_manager.BaseConductorManager):
1833 1860
 
1834 1861
             return portgroup_obj
1835 1862
 
1863
+    @METRICS.timer('ConductorManager.update_volume_connector')
1864
+    @messaging.expected_exceptions(
1865
+        exception.InvalidParameterValue,
1866
+        exception.NodeLocked,
1867
+        exception.NodeNotFound,
1868
+        exception.VolumeConnectorNotFound,
1869
+        exception.VolumeConnectorTypeAndIdAlreadyExists)
1870
+    def update_volume_connector(self, context, connector):
1871
+        """Update a volume connector.
1872
+
1873
+        :param context: request context
1874
+        :param connector: a changed (but not saved) volume connector object
1875
+        :returns: an updated volume connector object
1876
+        :raises: InvalidParameterValue if the volume connector's UUID is being
1877
+                 changed
1878
+        :raises: NodeLocked if the node is already locked
1879
+        :raises: NodeNotFound if the node associated with the conductor does
1880
+                 not exist
1881
+        :raises: VolumeConnectorNotFound if the volume connector cannot be
1882
+                 found
1883
+        :raises: VolumeConnectorTypeAndIdAlreadyExists if another connector
1884
+                 already exists with the same values for type and connector_id
1885
+                 fields
1886
+        """
1887
+        LOG.debug("RPC update_volume_connector called for connector "
1888
+                  "%(connector)s.",
1889
+                  {'connector': connector.uuid})
1890
+
1891
+        with task_manager.acquire(context, connector.node_id,
1892
+                                  purpose='volume connector update'):
1893
+            connector.save()
1894
+            LOG.info(_LI("Successfully updated volume connector "
1895
+                         "%(connector)s."),
1896
+                     {'connector': connector.uuid})
1897
+            return connector
1898
+
1836 1899
     @METRICS.timer('ConductorManager.get_driver_properties')
1837 1900
     @messaging.expected_exceptions(exception.DriverNotFound)
1838 1901
     def get_driver_properties(self, context, driver_name):

+ 48
- 1
ironic/conductor/rpcapi.py View File

@@ -81,11 +81,12 @@ class ConductorAPI(object):
81 81
     |    1.32 - Add do_node_clean
82 82
     |    1.33 - Added update and destroy portgroup.
83 83
     |    1.34 - Added heartbeat
84
+    |    1.35 - Added destroy_volume_connector and update_volume_connector
84 85
 
85 86
     """
86 87
 
87 88
     # NOTE(rloo): This must be in sync with manager.ConductorManager's.
88
-    RPC_API_VERSION = '1.34'
89
+    RPC_API_VERSION = '1.35'
89 90
 
90 91
     def __init__(self, topic=None):
91 92
         super(ConductorAPI, self).__init__()
@@ -735,3 +736,49 @@ class ConductorAPI(object):
735 736
         cctxt = self.client.prepare(topic=self.topic, version='1.31')
736 737
         return cctxt.call(context, 'object_backport_versions', objinst=objinst,
737 738
                           object_versions=object_versions)
739
+
740
+    def destroy_volume_connector(self, context, connector, topic=None):
741
+        """Delete a volume connector.
742
+
743
+        Delete the volume connector. The conductor will lock the related node
744
+        during this operation.
745
+
746
+        :param context: request context
747
+        :param connector: volume connector object
748
+        :param topic: RPC topic. Defaults to self.topic.
749
+        :raises: NodeLocked if node is locked by another conductor
750
+        :raises: NodeNotFound if the node associated with the connector does
751
+                 not exist
752
+        :raises: VolumeConnectorNotFound if the volume connector cannot be
753
+                 found
754
+        """
755
+        cctxt = self.client.prepare(topic=topic or self.topic, version='1.35')
756
+        return cctxt.call(context, 'destroy_volume_connector',
757
+                          connector=connector)
758
+
759
+    def update_volume_connector(self, context, connector, topic=None):
760
+        """Update the volume connector's information.
761
+
762
+        Update the volume connector's information in the database and return
763
+        a volume connector object. The conductor will lock the related node
764
+        during this operation.
765
+
766
+        :param context: request context
767
+        :param connector: a changed (but not saved) volume connector object
768
+        :param topic: RPC topic. Defaults to self.topic.
769
+        :raises: InvalidParameterValue if the volume connector's UUID is being
770
+                 changed
771
+        :raises: NodeLocked if node is locked by another conductor
772
+        :raises: NodeNotFound if the node associated with the connector does
773
+                 not exist
774
+        :raises: VolumeConnectorNotFound if the volume connector cannot be
775
+                 found
776
+        :raises: VolumeConnectorTypeAndIdAlreadyExists if another connector
777
+                 already exists with the same values for type and connector_id
778
+                 fields
779
+        :returns: updated volume connector object, including all fields.
780
+
781
+        """
782
+        cctxt = self.client.prepare(topic=topic or self.topic, version='1.35')
783
+        return cctxt.call(context, 'update_volume_connector',
784
+                          connector=connector)

+ 92
- 0
ironic/tests/unit/conductor/test_manager.py View File

@@ -5471,3 +5471,95 @@ class DoNodeAdoptionTestCase(
5471 5471
         self.service.heartbeat(self.context, node.uuid, 'http://callback')
5472 5472
         mock_spawn.assert_called_with(self.driver.deploy.heartbeat,
5473 5473
                                       mock.ANY, 'http://callback')
5474
+
5475
+
5476
+@mgr_utils.mock_record_keepalive
5477
+class DestroyVolumeConnectorTestCase(mgr_utils.ServiceSetUpMixin,
5478
+                                     tests_db_base.DbTestCase):
5479
+    def test_destroy_volume_connector(self):
5480
+        node = obj_utils.create_test_node(self.context, driver='fake')
5481
+
5482
+        volume_connector = obj_utils.create_test_volume_connector(
5483
+            self.context, node_id=node.id)
5484
+        self.service.destroy_volume_connector(self.context, volume_connector)
5485
+        self.assertRaises(exception.VolumeConnectorNotFound,
5486
+                          volume_connector.refresh)
5487
+        self.assertRaises(exception.VolumeConnectorNotFound,
5488
+                          self.dbapi.get_volume_connector_by_uuid,
5489
+                          volume_connector.uuid)
5490
+
5491
+    def test_destroy_volume_connector_node_locked(self):
5492
+        node = obj_utils.create_test_node(self.context, driver='fake',
5493
+                                          reservation='fake-reserv')
5494
+
5495
+        volume_connector = obj_utils.create_test_volume_connector(
5496
+            self.context, node_id=node.id)
5497
+        exc = self.assertRaises(messaging.rpc.ExpectedException,
5498
+                                self.service.destroy_volume_connector,
5499
+                                self.context, volume_connector)
5500
+        # Compare true exception hidden by @messaging.expected_exceptions
5501
+        self.assertEqual(exception.NodeLocked, exc.exc_info[0])
5502
+
5503
+
5504
+@mgr_utils.mock_record_keepalive
5505
+class UpdateVolumeConnectorTestCase(mgr_utils.ServiceSetUpMixin,
5506
+                                    tests_db_base.DbTestCase):
5507
+    def test_update_volume_connector(self):
5508
+        node = obj_utils.create_test_node(self.context, driver='fake')
5509
+
5510
+        volume_connector = obj_utils.create_test_volume_connector(
5511
+            self.context, node_id=node.id, extra={'foo': 'bar'})
5512
+        new_extra = {'foo': 'baz'}
5513
+        volume_connector.extra = new_extra
5514
+        res = self.service.update_volume_connector(self.context,
5515
+                                                   volume_connector)
5516
+        self.assertEqual(new_extra, res.extra)
5517
+
5518
+    def test_update_volume_connector_node_locked(self):
5519
+        node = obj_utils.create_test_node(self.context, driver='fake',
5520
+                                          reservation='fake-reserv')
5521
+
5522
+        volume_connector = obj_utils.create_test_volume_connector(
5523
+            self.context, node_id=node.id)
5524
+        volume_connector.extra = {'foo': 'baz'}
5525
+        exc = self.assertRaises(messaging.rpc.ExpectedException,
5526
+                                self.service.update_volume_connector,
5527
+                                self.context, volume_connector)
5528
+        # Compare true exception hidden by @messaging.expected_exceptions
5529
+        self.assertEqual(exception.NodeLocked, exc.exc_info[0])
5530
+
5531
+    def test_update_volume_connector_type(self):
5532
+        node = obj_utils.create_test_node(self.context, driver='fake')
5533
+        volume_connector = obj_utils.create_test_volume_connector(
5534
+            self.context, node_id=node.id, extra={'vol_id': 'fake-id'})
5535
+        new_type = 'wwnn'
5536
+        volume_connector.type = new_type
5537
+        res = self.service.update_volume_connector(self.context,
5538
+                                                   volume_connector)
5539
+        self.assertEqual(new_type, res.type)
5540
+
5541
+    def test_update_volume_connector_uuid(self):
5542
+        node = obj_utils.create_test_node(self.context, driver='fake')
5543
+        volume_connector = obj_utils.create_test_volume_connector(
5544
+            self.context, node_id=node.id)
5545
+        volume_connector.uuid = uuidutils.generate_uuid()
5546
+        exc = self.assertRaises(messaging.rpc.ExpectedException,
5547
+                                self.service.update_volume_connector,
5548
+                                self.context, volume_connector)
5549
+        # Compare true exception hidden by @messaging.expected_exceptions
5550
+        self.assertEqual(exception.InvalidParameterValue, exc.exc_info[0])
5551
+
5552
+    def test_update_volume_connector_duplicate(self):
5553
+        node = obj_utils.create_test_node(self.context, driver='fake')
5554
+        volume_connector1 = obj_utils.create_test_volume_connector(
5555
+            self.context, node_id=node.id)
5556
+        volume_connector2 = obj_utils.create_test_volume_connector(
5557
+            self.context, node_id=node.id, uuid=uuidutils.generate_uuid(),
5558
+            type='diff_type')
5559
+        volume_connector2.type = volume_connector1.type
5560
+        exc = self.assertRaises(messaging.rpc.ExpectedException,
5561
+                                self.service.update_volume_connector,
5562
+                                self.context, volume_connector2)
5563
+        # Compare true exception hidden by @messaging.expected_exceptions
5564
+        self.assertEqual(exception.VolumeConnectorTypeAndIdAlreadyExists,
5565
+                         exc.exc_info[0])

+ 14
- 0
ironic/tests/unit/conductor/test_rpcapi.py View File

@@ -400,3 +400,17 @@ class RPCAPITestCase(base.DbTestCase):
400 400
                           node_id='fake-node',
401 401
                           callback_url='http://ramdisk.url:port',
402 402
                           version='1.34')
403
+
404
+    def test_destroy_volume_connector(self):
405
+        fake_volume_connector = dbutils.get_test_volume_connector()
406
+        self._test_rpcapi('destroy_volume_connector',
407
+                          'call',
408
+                          version='1.35',
409
+                          connector=fake_volume_connector)
410
+
411
+    def test_update_volume_connector(self):
412
+        fake_volume_connector = dbutils.get_test_volume_connector()
413
+        self._test_rpcapi('update_volume_connector',
414
+                          'call',
415
+                          version='1.35',
416
+                          connector=fake_volume_connector)

+ 27
- 0
ironic/tests/unit/objects/utils.py View File

@@ -161,3 +161,30 @@ def create_test_portgroup(ctxt, **kw):
161 161
     portgroup = get_test_portgroup(ctxt, **kw)
162 162
     portgroup.create()
163 163
     return portgroup
164
+
165
+
166
+def get_test_volume_connector(ctxt, **kw):
167
+    """Return a VolumeConnector object with appropriate attributes.
168
+
169
+    NOTE: The object leaves the attributes marked as changed, such
170
+    that a create() could be used to commit it to the DB.
171
+    """
172
+    db_volume_connector = db_utils.get_test_volume_connector(**kw)
173
+    # Let DB generate ID if it isn't specified explicitly
174
+    if 'id' not in kw:
175
+        del db_volume_connector['id']
176
+    volume_connector = objects.VolumeConnector(ctxt)
177
+    for key in db_volume_connector:
178
+        setattr(volume_connector, key, db_volume_connector[key])
179
+    return volume_connector
180
+
181
+
182
+def create_test_volume_connector(ctxt, **kw):
183
+    """Create and return a test volume connector object.
184
+
185
+    Create a volume connector in the DB and return a VolumeConnector object
186
+    with appropriate attributes.
187
+    """
188
+    volume_connector = get_test_volume_connector(ctxt, **kw)
189
+    volume_connector.create()
190
+    return volume_connector

Loading…
Cancel
Save