Hook up a packing plan endpoint in Heron UI (#3359)
* Hook up a packing plan endpoint in Heron UI
* add unit test
diff --git a/heron/tools/common/src/python/access/heron_api.py b/heron/tools/common/src/python/access/heron_api.py
index 1ab571c..709acb4 100644
--- a/heron/tools/common/src/python/access/heron_api.py
+++ b/heron/tools/common/src/python/access/heron_api.py
@@ -35,6 +35,7 @@
EXECUTION_STATE_URL_FMT = "%s/executionstate" % TOPOLOGIES_URL_FMT
LOGICALPLAN_URL_FMT = "%s/logicalplan" % TOPOLOGIES_URL_FMT
PHYSICALPLAN_URL_FMT = "%s/physicalplan" % TOPOLOGIES_URL_FMT
+PACKINGPLAN_URL_FMT = "%s/packingplan" % TOPOLOGIES_URL_FMT
SCHEDULER_LOCATION_URL_FMT = "%s/schedulerlocation" % TOPOLOGIES_URL_FMT
METRICS_URL_FMT = "%s/metrics" % TOPOLOGIES_URL_FMT
@@ -268,6 +269,25 @@
################################################################################
@tornado.gen.coroutine
+def get_packing_plan(cluster, environ, topology, role=None):
+ '''
+ Get the packing plan state of a topology in a cluster from tracker
+ :param cluster:
+ :param environ:
+ :param topology:
+ :param role:
+ :return:
+ '''
+ params = dict(cluster=cluster, environ=environ, topology=topology)
+ if role is not None:
+ params['role'] = role
+ request_url = tornado.httputil.url_concat(
+ create_url(PACKINGPLAN_URL_FMT), params)
+ raise tornado.gen.Return((yield fetch_url_as_json(request_url)))
+
+
+################################################################################
+@tornado.gen.coroutine
def get_physical_plan(cluster, environ, topology, role=None):
'''
Get the physical plan state of a topology in a cluster from tracker
diff --git a/heron/tools/tracker/src/python/tracker.py b/heron/tools/tracker/src/python/tracker.py
index 3ce814d..15df468 100644
--- a/heron/tools/tracker/src/python/tracker.py
+++ b/heron/tools/tracker/src/python/tracker.py
@@ -598,7 +598,7 @@
packingPlan["id"] = topology.packing_plan.id
packingPlan["container_plans"] = containers
- return json.dumps(packingPlan)
+ return packingPlan
def setTopologyInfo(self, topology):
"""
diff --git a/heron/tools/tracker/tests/python/tracker_unittest.py b/heron/tools/tracker/tests/python/tracker_unittest.py
index 82afd64..136f5d6 100644
--- a/heron/tools/tracker/tests/python/tracker_unittest.py
+++ b/heron/tools/tracker/tests/python/tracker_unittest.py
@@ -250,3 +250,22 @@
{'topology.component.parallelism': '1'})
self.assertEqual(pplan['instances'], {})
self.assertEqual(pplan['stmgrs'], {})
+
+ def test_extract_packing_plan(self):
+ # Create topology
+ pb_pplan = MockProto().create_mock_simple_packing_plan()
+ topology = Topology('topology_name', 'ExclamationTopology')
+ topology.set_packing_plan(pb_pplan)
+ # Extract packing plan
+ packing_plan = self.tracker.extract_packing_plan(topology)
+ self.assertEqual(packing_plan['id'], 'ExclamationTopology')
+ self.assertEqual(packing_plan['container_plans'][0]['id'], 1)
+ self.assertEqual(packing_plan['container_plans'][0]['required_resources'],
+ {'disk': 2048L, 'ram': 1024L, 'cpu': 1.0})
+ self.assertEqual(packing_plan['container_plans'][0]['instances'][0],
+ {
+ 'component_index': 1,
+ 'component_name': u'word',
+ 'instance_resources': {'cpu': 1.0, 'disk': 2048L, 'ram': 1024L},
+ 'task_id': 1
+ })
diff --git a/heron/tools/ui/src/python/handlers/api/__init__.py b/heron/tools/ui/src/python/handlers/api/__init__.py
index c9e17e0..e36109d 100644
--- a/heron/tools/ui/src/python/handlers/api/__init__.py
+++ b/heron/tools/ui/src/python/handlers/api/__init__.py
@@ -24,6 +24,7 @@
TopologyExceptionSummaryHandler,
ListTopologiesJsonHandler,
TopologyLogicalPlanJsonHandler,
+ TopologyPackingPlanJsonHandler,
TopologyPhysicalPlanJsonHandler,
TopologySchedulerLocationJsonHandler,
TopologyExecutionStateJsonHandler,
diff --git a/heron/tools/ui/src/python/handlers/api/topology.py b/heron/tools/ui/src/python/handlers/api/topology.py
index c02f566..3df74d5 100644
--- a/heron/tools/ui/src/python/handlers/api/topology.py
+++ b/heron/tools/ui/src/python/handlers/api/topology.py
@@ -141,6 +141,32 @@
self.write(result)
+class TopologyPackingPlanJsonHandler(base.BaseHandler):
+ ''' TopologyPackingPlanJsonHandler '''
+
+ @tornado.gen.coroutine
+ def get(self, cluster, environ, topology):
+ '''
+ :param cluster:
+ :param environ:
+ :param topology:
+ :return:
+ '''
+
+ start_time = time.time()
+ packing_plan = yield access.get_packing_plan(cluster, environ, topology)
+
+ result_map = dict(
+ status="success",
+ message="",
+ version=common.VERSION,
+ executiontime=time.time() - start_time,
+ result=packing_plan
+ )
+
+ self.write(result_map)
+
+
class TopologyPhysicalPlanJsonHandler(base.BaseHandler):
''' TopologyPhysicalPlanJsonHandler '''
diff --git a/heron/tools/ui/src/python/main.py b/heron/tools/ui/src/python/main.py
index d72b06c..cb8b4b7 100644
--- a/heron/tools/ui/src/python/main.py
+++ b/heron/tools/ui/src/python/main.py
@@ -100,6 +100,8 @@
handlers.api.ListTopologiesJsonHandler),
(r"/topologies/([^\/]+)/([^\/]+)/([^\/]+)/logicalplan.json",
handlers.api.TopologyLogicalPlanJsonHandler),
+ (r"/topologies/([^\/]+)/([^\/]+)/([^\/]+)/packingplan.json",
+ handlers.api.TopologyPackingPlanJsonHandler),
(r"/topologies/([^\/]+)/([^\/]+)/([^\/]+)/physicalplan.json",
handlers.api.TopologyPhysicalPlanJsonHandler),
(r"/topologies/([^\/]+)/([^\/]+)/([^\/]+)/executionstate.json",