Add openstack cost command 82/62382/5
authorTim Johnson <tijohnson@linuxfoundation.org>
Fri, 22 Nov 2019 18:32:51 +0000 (10:32 -0800)
committerTim Johnson <tijohnson@linuxfoundation.org>
Tue, 26 Nov 2019 16:54:22 +0000 (08:54 -0800)
Issue: RELENG-2550
Change-Id: I9bee88cd5cb3d89f3f554470e86925d8f24dbb5a
Signed-off-by: Tim Johnson <tijohnson@linuxfoundation.org>
docs/commands/openstack.rst
lftools/openstack/cmd.py
lftools/openstack/stack.py
releasenotes/notes/add-openstack-cost-464444d8cf0bdfa5.yaml [new file with mode: 0644]

index aeffa8f..2dacce9 100644 (file)
@@ -81,3 +81,13 @@ delete
 Delete existing stack.
 
 .. program-output:: lftools openstack --os-cloud docs stack delete --help
+
+
+cost
+^^^^
+
+Get total cost of existing stack.
+
+.. program-output:: lftools openstack --os-cloud docs stack cost --help
+
+Return sum of costs for each member of the running stack.
index cc3dde4..4018b92 100644 (file)
@@ -224,6 +224,16 @@ def delete(ctx, name_or_id, force, timeout):
         timeout=timeout)
 
 
+@click.command()
+@click.argument('stack_name')
+@click.pass_context
+def cost(ctx, stack_name):
+    """Get Total Stack Cost."""
+    os_stack.cost(
+        ctx.obj['os_cloud'],
+        stack_name)
+
+
 @click.command(name='delete-stale')
 @click.argument('jenkins_urls', nargs=-1)
 @click.pass_context
@@ -242,6 +252,7 @@ def delete_stale(ctx, jenkins_urls):
 stack.add_command(create)
 stack.add_command(delete)
 stack.add_command(delete_stale)
+stack.add_command(cost)
 
 
 @openstack.group()
index 42bf542..a14b72e 100644 (file)
 
 __author__ = 'Thanh Ha'
 
+from datetime import datetime
+import json
 import logging
 import sys
 import time
+import urllib.request
 
 import shade
 
 from lftools.jenkins import Jenkins
+import openstack
 
 log = logging.getLogger(__name__)
 
@@ -74,6 +78,58 @@ def create(os_cloud, name, template_file, parameter_file, timeout=900, tries=2):
     print('------------------------------------')
 
 
+def cost(os_cloud, stack_name):
+    """Get current cost info for the stack.
+
+    Return the cost in dollars & cents (x.xx).
+    """
+    def get_server_cost(server_id):
+        flavor, seconds = get_server_info(server_id)
+        url = "https://pricing.vexxhost.net/v1/pricing/%s/cost?seconds=%d"
+        with urllib.request.urlopen(url % (flavor, seconds)) as response:  # nosec
+            data = json.loads(response.read())
+        return data['cost']
+
+    def parse_iso8601_time(time):
+        return datetime.strptime(time, "%Y-%m-%dT%H:%M:%S.%f")
+
+    def get_server_info(server_id):
+        server = cloud.compute.find_server(server_id)
+        diff = (datetime.utcnow() - parse_iso8601_time(server.launched_at))
+        return server.flavor['original_name'], diff.total_seconds()
+
+    def get_server_ids(stack_name):
+        servers = get_resources_by_type(stack_name, 'OS::Nova::Server')
+        return [s['physical_resource_id'] for s in servers]
+
+    def get_resources_by_type(stack_name, resource_type):
+        resources = get_stack_resources(stack_name)
+        return [r for r in resources if r.resource_type == resource_type]
+
+    def get_stack_resources(stack_name):
+        resources = []
+
+        def _is_nested(resource):
+            link_types = [l['rel'] for l in resource.links]
+            if 'nested' in link_types:
+                return True
+            return False
+
+        for r in cloud.orchestration.resources(stack_name):
+            if _is_nested(r):
+                resources += get_stack_resources(r.physical_resource_id)
+                continue
+            resources.append(r)
+        return resources
+
+    cloud = openstack.connect(os_cloud)
+
+    total_cost = 0.7
+    for server in get_server_ids(stack_name):
+        total_cost += get_server_cost(server)
+    print("total: " + str(total_cost))
+
+
 def delete(os_cloud, name_or_id, force, timeout=900):
     """Delete a stack.
 
diff --git a/releasenotes/notes/add-openstack-cost-464444d8cf0bdfa5.yaml b/releasenotes/notes/add-openstack-cost-464444d8cf0bdfa5.yaml
new file mode 100644 (file)
index 0000000..d82656d
--- /dev/null
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add openstack cost command. The cost is sum of the costs of each member of
+    the running stack.
+    https://jira.linuxfoundation.org/browse/RELENG-2550