Browse Source

Microversion 2.78 - show server topology

Add support microversion 2.78 which adds server topology
information in the output of the following new command:

  nova server-topology

Depends-on: https://review.opendev.org/#/c/621476/
Change-Id: I6467d52d2528a37348458baf4842b571a97f3ed2
Implements: blueprint show-server-numa-topology
tags/15.1.0
Yongli He 2 months ago
parent
commit
aae95dcc7a

+ 23
- 0
doc/source/cli/nova.rst View File

@@ -472,6 +472,9 @@ nova usage
472 472
   '--os-compute-api-version' flag to show help
473 473
   message for proper version]
474 474
 
475
+``server-topology``
476
+  Retrieve NUMA topology of the given server.
477
+
475 478
 ``service-delete``
476 479
   Delete the service.
477 480
 
@@ -3358,6 +3361,26 @@ version]
3358 3361
 ``<tags>``
3359 3362
   Tag(s) to set.
3360 3363
 
3364
+.. _nova_server_topology:
3365
+
3366
+nova server-topology
3367
+--------------------
3368
+
3369
+.. code-block:: console
3370
+
3371
+   usage: nova server-topology <server>
3372
+
3373
+Retrieve server NUMA topology information. Host specific fields are only
3374
+visible to users with the administrative role.
3375
+(Supported by API versions '2.78' - '2.latest')
3376
+
3377
+.. versionadded:: 16.0.0
3378
+
3379
+**Positional arguments:**
3380
+
3381
+``<server>``
3382
+  Name or ID of server.
3383
+
3361 3384
 .. _nova_service-delete:
3362 3385
 
3363 3386
 nova service-delete

+ 1
- 1
novaclient/__init__.py View File

@@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1")
25 25
 # when client supported the max version, and bumped sequentially, otherwise
26 26
 # the client may break due to server side new version may include some
27 27
 # backward incompatible change.
28
-API_MAX_VERSION = api_versions.APIVersion("2.77")
28
+API_MAX_VERSION = api_versions.APIVersion("2.78")

+ 4
- 0
novaclient/tests/unit/fixture_data/servers.py View File

@@ -371,6 +371,10 @@ class V1(Base):
371 371
         self.requests_mock.delete(self.url('1234', 'os-interface', 'port-id'),
372 372
                                   headers=self.json_headers)
373 373
 
374
+        self.requests_mock.get(self.url('1234', 'topology'),
375
+                               json=v2_fakes.SERVER_TOPOLOGY,
376
+                               headers=self.json_headers)
377
+
374 378
         # Testing with the following password and key
375 379
         #
376 380
         # Clear password: FooBar123

+ 45
- 0
novaclient/tests/unit/v2/fakes.py View File

@@ -58,6 +58,48 @@ FAKE_RESPONSE_HEADERS = {'x-openstack-request-id': FAKE_REQUEST_ID}
58 58
 FAKE_SERVICE_UUID_1 = '75e9eabc-ed3b-4f11-8bba-add1e7e7e2de'
59 59
 FAKE_SERVICE_UUID_2 = '1f140183-c914-4ddf-8757-6df73028aa86'
60 60
 
61
+SERVER_TOPOLOGY = {
62
+    "nodes": [
63
+        {
64
+            "cpu_pinning": {
65
+                "0": 0,
66
+                "1": 5
67
+            },
68
+            "host_node": 0,
69
+            "memory_mb": 1024,
70
+            "siblings": [
71
+                [
72
+                    0,
73
+                    1
74
+                ]
75
+            ],
76
+            "vcpu_set": [
77
+                0,
78
+                1
79
+            ]
80
+        },
81
+        {
82
+            "cpu_pinning": {
83
+                "2": 1,
84
+                "3": 8
85
+            },
86
+            "host_node": 1,
87
+            "memory_mb": 2048,
88
+            "siblings": [
89
+                [
90
+                    2,
91
+                    3
92
+                ]
93
+            ],
94
+            "vcpu_set": [
95
+                2,
96
+                3
97
+            ]
98
+        }
99
+    ],
100
+    "pagesize_kb": 4
101
+}
102
+
61 103
 
62 104
 class FakeClient(fakes.FakeClient, client.Client):
63 105
 
@@ -738,6 +780,9 @@ class FakeSessionClient(base_client.SessionClient):
738 780
                 'rules': []}]
739 781
         })
740 782
 
783
+    def get_servers_1234_topology(self, **kw):
784
+        return 200, {}, SERVER_TOPOLOGY
785
+
741 786
     #
742 787
     # Server password
743 788
     #

+ 24
- 0
novaclient/tests/unit/v2/test_servers.py View File

@@ -1875,3 +1875,27 @@ class ServersV277Test(ServersV274Test):
1875 1875
                                s, availability_zone='foo-az')
1876 1876
         self.assertIn("unexpected keyword argument 'availability_zone'",
1877 1877
                       six.text_type(ex))
1878
+
1879
+
1880
+class ServersV278Test(ServersV273Test):
1881
+
1882
+    api_version = "2.78"
1883
+
1884
+    def test_get_server_topology(self):
1885
+        s = self.cs.servers.get(1234)
1886
+        topology = s.topology()
1887
+        self.assert_request_id(topology, fakes.FAKE_REQUEST_ID_LIST)
1888
+        self.assertIsNotNone(topology)
1889
+        self.assert_called('GET', '/servers/1234/topology')
1890
+
1891
+        topology_from_manager = self.cs.servers.topology(1234)
1892
+        self.assert_request_id(topology, fakes.FAKE_REQUEST_ID_LIST)
1893
+        self.assertIsNotNone(topology_from_manager)
1894
+        self.assert_called('GET', '/servers/1234/topology')
1895
+
1896
+        self.assertEqual(topology, topology_from_manager)
1897
+
1898
+    def test_get_server_topology_pre278(self):
1899
+        self.cs.api_version = api_versions.APIVersion('2.77')
1900
+        s = self.cs.servers.get(1234)
1901
+        self.assertRaises(exceptions.VersionNotFoundForAPIMethod, s.topology)

+ 13
- 0
novaclient/tests/unit/v2/test_shell.py View File

@@ -2463,6 +2463,19 @@ class ShellTest(utils.TestCase):
2463 2463
         self.run_command('diagnostics sample-server')
2464 2464
         self.assert_called('GET', '/servers/1234/diagnostics')
2465 2465
 
2466
+    def test_server_topology(self):
2467
+        self.run_command('server-topology 1234', api_version='2.78')
2468
+        self.assert_called('GET', '/servers/1234/topology')
2469
+        self.run_command('server-topology sample-server', api_version='2.78')
2470
+        self.assert_called('GET', '/servers/1234/topology')
2471
+
2472
+    def test_server_topology_pre278(self):
2473
+        exp = self.assertRaises(SystemExit,
2474
+                                self.run_command,
2475
+                                'server-topology 1234',
2476
+                                api_version='2.77')
2477
+        self.assertIn('2', six.text_type(exp))
2478
+
2466 2479
     def test_refresh_network(self):
2467 2480
         self.run_command('refresh-network 1234')
2468 2481
         self.assert_called('POST', '/os-server-external-events',

+ 18
- 0
novaclient/v2/servers.py View File

@@ -316,6 +316,11 @@ class Server(base.Resource):
316 316
         """Diagnostics -- Retrieve server diagnostics."""
317 317
         return self.manager.diagnostics(self)
318 318
 
319
+    @api_versions.wraps("2.78")
320
+    def topology(self):
321
+        """Retrieve server topology."""
322
+        return self.manager.topology(self)
323
+
319 324
     @api_versions.wraps("2.0", "2.55")
320 325
     def migrate(self):
321 326
         """
@@ -1286,6 +1291,19 @@ class ServerManager(base.BootingManagerWithFind):
1286 1291
                                          base.getid(server))
1287 1292
         return base.TupleWithMeta((resp, body), resp)
1288 1293
 
1294
+    @api_versions.wraps("2.78")
1295
+    def topology(self, server):
1296
+        """
1297
+        Retrieve server topology.
1298
+
1299
+        :param server: The :class:`Server` (or its ID) for which
1300
+                       topology to be returned
1301
+        :returns: An instance of novaclient.base.DictWithMeta
1302
+        """
1303
+        resp, body = self.api.client.get("/servers/%s/topology" %
1304
+                                         base.getid(server))
1305
+        return base.DictWithMeta(body, resp)
1306
+
1289 1307
     def _validate_create_nics(self, nics):
1290 1308
         # nics are required with microversion 2.37+ and can be a string or list
1291 1309
         if self.api_version > api_versions.APIVersion('2.36'):

+ 11
- 0
novaclient/v2/shell.py View File

@@ -2313,6 +2313,17 @@ def do_diagnostics(cs, args):
2313 2313
     utils.print_dict(cs.servers.diagnostics(server)[1], wrap=80)
2314 2314
 
2315 2315
 
2316
+@api_versions.wraps("2.78")
2317
+@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2318
+def do_server_topology(cs, args):
2319
+    """Retrieve server topology."""
2320
+    server = _find_server(cs, args.server)
2321
+    # This prints a dict with only two properties: nodes and pagesize_kb
2322
+    # nodes is a list of dicts so it does not print very well, it's just a
2323
+    # json blob in the output.
2324
+    utils.print_dict(cs.servers.topology(server), wrap=80)
2325
+
2326
+
2316 2327
 @utils.arg(
2317 2328
     'server', metavar='<server>',
2318 2329
     help=_('Name or ID of a server for which the network cache should '

+ 14
- 0
releasenotes/notes/microversion-v2_78-77a12630e668c2ae.yaml View File

@@ -0,0 +1,14 @@
1
+---
2
+features:
3
+  - |
4
+    Added support for `microversion 2.78`_ which outputs the server NUMA
5
+    topology information in the following command:
6
+
7
+    * ``nova server-topology``
8
+
9
+    And associated python API bindings:
10
+
11
+    * ``novaclient.v2.servers.Server.topology``
12
+    * ``novaclient.v2.servers.ServerManager.topology``
13
+
14
+    .. _microversion 2.78: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id70

Loading…
Cancel
Save