Browse Source

Merge "web: add /{tenant}/nodes route"

tags/3.4.0
Zuul 5 months ago
parent
commit
dcfeb3a42b
3 changed files with 68 additions and 0 deletions
  1. 7
    0
      tests/unit/test_web.py
  2. 17
    0
      zuul/web/__init__.py
  3. 44
    0
      zuul/zk.py

+ 7
- 0
tests/unit/test_web.py View File

@@ -373,6 +373,13 @@ class TestWeb(BaseTestWeb):
373 373
                 'voting': True
374 374
             }], data)
375 375
 
376
+    def test_web_nodes_list(self):
377
+        # can we fetch the nodes list
378
+        data = self.get_url('api/tenant/tenant-one/nodes').json()
379
+        self.assertGreater(len(data), 0)
380
+        self.assertEqual("test-provider", data[0]["provider"])
381
+        self.assertEqual("label1", data[0]["type"])
382
+
376 383
     def test_web_labels_list(self):
377 384
         # can we fetch the labels list
378 385
         data = self.get_url('api/tenant/tenant-one/labels').json()

+ 17
- 0
zuul/web/__init__.py View File

@@ -361,6 +361,21 @@ class ZuulWebAPI(object):
361 361
         resp.headers['Access-Control-Allow-Origin'] = '*'
362 362
         return ret
363 363
 
364
+    @cherrypy.expose
365
+    @cherrypy.tools.save_params()
366
+    @cherrypy.tools.json_out(content_type='application/json; charset=utf-8')
367
+    def nodes(self, tenant):
368
+        ret = []
369
+        for node in self.zk.nodeIterator():
370
+            node_data = {}
371
+            for key in ("id", "type", "connection_type", "external_id",
372
+                        "provider", "state", "state_time", "comment"):
373
+                node_data[key] = node.get(key)
374
+            ret.append(node_data)
375
+        resp = cherrypy.response
376
+        resp.headers['Access-Control-Allow-Origin'] = '*'
377
+        return ret
378
+
364 379
     @cherrypy.expose
365 380
     @cherrypy.tools.save_params()
366 381
     def key(self, tenant, project):
@@ -619,6 +634,8 @@ class ZuulWeb(object):
619 634
                           controller=api, action='pipelines')
620 635
         route_map.connect('api', '/api/tenant/{tenant}/labels',
621 636
                           controller=api, action='labels')
637
+        route_map.connect('api', '/api/tenant/{tenant}/nodes',
638
+                          controller=api, action='nodes')
622 639
         route_map.connect('api', '/api/tenant/{tenant}/key/{project:.*}.pub',
623 640
                           controller=api, action='key')
624 641
         route_map.connect('api', '/api/tenant/{tenant}/'

+ 44
- 0
zuul/zk.py View File

@@ -384,6 +384,7 @@ class ZooKeeper(object):
384 384
         return count
385 385
 
386 386
     # Copy of nodepool/zk.py begins here
387
+    NODE_ROOT = "/nodepool/nodes"
387 388
     LAUNCHER_ROOT = "/nodepool/launchers"
388 389
 
389 390
     def _bytesToDict(self, data):
@@ -392,6 +393,9 @@ class ZooKeeper(object):
392 393
     def _launcherPath(self, launcher):
393 394
         return "%s/%s" % (self.LAUNCHER_ROOT, launcher)
394 395
 
396
+    def _nodePath(self, node):
397
+        return "%s/%s" % (self.NODE_ROOT, node)
398
+
395 399
     def getRegisteredLaunchers(self):
396 400
         '''
397 401
         Get a list of all launchers that have registered with ZooKeeper.
@@ -415,6 +419,46 @@ class ZooKeeper(object):
415 419
             objs.append(Launcher.fromDict(self._bytesToDict(data)))
416 420
         return objs
417 421
 
422
+    def getNodes(self):
423
+        '''
424
+        Get the current list of all nodes.
425
+
426
+        :returns: A list of nodes.
427
+        '''
428
+        try:
429
+            return self.client.get_children(self.NODE_ROOT)
430
+        except kze.NoNodeError:
431
+            return []
432
+
433
+    def getNode(self, node):
434
+        '''
435
+        Get the data for a specific node.
436
+
437
+        :param str node: The node ID.
438
+
439
+        :returns: The node data, or None if the node was not found.
440
+        '''
441
+        path = self._nodePath(node)
442
+        try:
443
+            data, stat = self.client.get(path)
444
+        except kze.NoNodeError:
445
+            return None
446
+        if not data:
447
+            return None
448
+
449
+        d = self._bytesToDict(data)
450
+        d['id'] = node
451
+        return d
452
+
453
+    def nodeIterator(self):
454
+        '''
455
+        Utility generator method for iterating through all nodes.
456
+        '''
457
+        for node_id in self.getNodes():
458
+            node = self.getNode(node_id)
459
+            if node:
460
+                yield node
461
+
418 462
 
419 463
 class Launcher():
420 464
     '''

Loading…
Cancel
Save