OpenStack API: Replaced Flask with WSGI
[osm/vim-emu.git] / src / emuvim / api / rest / compute.py
index b935af8..c4f7f3b 100755 (executable)
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 
 See the License for the specific language governing permissions and
 limitations under the License.
 
-Neither the name of the SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
+Neither the name of the SONATA-NFV, Paderborn University
 nor the names of its contributors may be used to endorse or promote
 products derived from this software without specific prior written
 permission.
 nor the names of its contributors may be used to endorse or promote
 products derived from this software without specific prior written
 permission.
@@ -29,12 +29,17 @@ import logging
 from flask_restful import Resource
 from flask import request
 import json
 from flask_restful import Resource
 from flask import request
 import json
+from copy import deepcopy
 
 
-logging.basicConfig(level=logging.INFO)
+logging.basicConfig()
 
 
+CORS_HEADER = {'Access-Control-Allow-Origin': '*'}
+
+# the dcs dict is set in the rest_api_endpoint.py upon datacenter init
 dcs = {}
 
 dcs = {}
 
-class ComputeStart(Resource):
+
+class Compute(Resource):
     """
     Start a new compute instance: A docker container (note: zerorpc does not support keyword arguments)
     :param dc_label: name of the DC
     """
     Start a new compute instance: A docker container (note: zerorpc does not support keyword arguments)
     :param dc_label: name of the DC
@@ -45,92 +50,188 @@ class ComputeStart(Resource):
     example networks list({"id":"input","ip": "10.0.0.254/8"}, {"id":"output","ip": "11.0.0.254/24"})
     :return: docker inspect dict of deployed docker
     """
     example networks list({"id":"input","ip": "10.0.0.254/8"}, {"id":"output","ip": "11.0.0.254/24"})
     :return: docker inspect dict of deployed docker
     """
+
     global dcs
 
     global dcs
 
-    def put(self, dc_label, compute_name):
-        logging.debug("API CALL: compute start")
-        try:
+    def put(self, dc_label, compute_name, resource=None, value=None):
 
 
-            image = json.loads(request.json).get("image")
-            network = json.loads(request.json).get("network")
-            command = json.loads(request.json).get("docker_command")
+        # deploy new container
+        # check if json data is a dict
+        data = request.json
+        if data is None:
+            data = {}
+        elif type(data) is not dict:
+            data = json.loads(request.json)
+
+        network = data.get("network")
+        nw_list = self._parse_network(network)
+        image = data.get("image")
+        command = data.get("docker_command")
+
+        try:
+            logging.debug("API CALL: compute start")
             c = dcs.get(dc_label).startCompute(
             c = dcs.get(dc_label).startCompute(
-                compute_name, image= image, command= command, network= network)
+                compute_name, image=image, command=command, network=nw_list)
             # return docker inspect dict
             # return docker inspect dict
-            return c.getStatus(), 200
+            return c.getStatus(), 200, CORS_HEADER
         except Exception as ex:
             logging.exception("API error.")
         except Exception as ex:
             logging.exception("API error.")
-            return ex.message, 500
+            return ex.message, 500, CORS_HEADER
+
+    def get(self, dc_label, compute_name):
 
 
-class ComputeStop(Resource):
+        logging.debug("API CALL: compute status")
 
 
-    global dcs
+        try:
+            return dcs.get(dc_label).containers.get(compute_name).getStatus(), 200, CORS_HEADER
+        except Exception as ex:
+            logging.exception("API error.")
+            return ex.message, 500, CORS_HEADER
 
 
-    def get(self, dc_label, compute_name):
+    def delete(self, dc_label, compute_name):
         logging.debug("API CALL: compute stop")
         try:
         logging.debug("API CALL: compute stop")
         try:
-            return dcs.get(dc_label).stopCompute(compute_name), 200
+            return dcs.get(dc_label).stopCompute(compute_name), 200, CORS_HEADER
         except Exception as ex:
             logging.exception("API error.")
         except Exception as ex:
             logging.exception("API error.")
-            return ex.message,500
+            return ex.message, 500, CORS_HEADER
 
 
+    def _parse_network(self, network_str):
+        '''
+        parse the options for all network interfaces of the vnf
+        :param network_str: (id=x,ip=x.x.x.x/x), ...
+        :return: list of dicts [{"id":x,"ip":"x.x.x.x/x"}, ...]
+        '''
+        nw_list = list()
 
 
-class ComputeList(Resource):
+        # TODO make this more robust with regex check
+        if network_str is None:
+            return nw_list
+
+        networks = network_str[1:-1].split('),(')
+        for nw in networks:
+            nw_dict = dict(tuple(e.split('=')) for e in nw.split(','))
+            nw_list.append(nw_dict)
+
+        return nw_list
 
 
+
+class ComputeList(Resource):
     global dcs
 
     global dcs
 
-    def get(self, dc_label):
+    def get(self, dc_label=None):
         logging.debug("API CALL: compute list")
         try:
         logging.debug("API CALL: compute list")
         try:
-            if dc_label == 'None':
+            if dc_label is None or dc_label == 'None':
                 # return list with all compute nodes in all DCs
                 all_containers = []
                 # return list with all compute nodes in all DCs
                 all_containers = []
+                all_extSAPs = []
                 for dc in dcs.itervalues():
                     all_containers += dc.listCompute()
                 for dc in dcs.itervalues():
                     all_containers += dc.listCompute()
-                return [(c.name, c.getStatus()) for c in all_containers], 200
+                    all_extSAPs += dc.listExtSAPs()
+
+                extSAP_list = [(sap.name, sap.getStatus()) for sap in all_extSAPs]
+                container_list = [(c.name, c.getStatus()) for c in all_containers]
+                total_list = container_list + extSAP_list
+                return total_list, 200, CORS_HEADER
             else:
                 # return list of compute nodes for specified DC
             else:
                 # return list of compute nodes for specified DC
-                return [(c.name, c.getStatus())
-                    for c in dcs.get(dc_label).listCompute()], 200
+                container_list = [(c.name, c.getStatus()) for c in dcs.get(dc_label).listCompute()]
+                extSAP_list = [(sap.name, sap.getStatus()) for sap in dcs.get(dc_label).listExtSAPs()]
+                total_list = container_list + extSAP_list
+                return total_list, 200, CORS_HEADER
         except Exception as ex:
             logging.exception("API error.")
         except Exception as ex:
             logging.exception("API error.")
-            return ex.message, 500
-
+            return ex.message, 500, CORS_HEADER
 
 
-class ComputeStatus(Resource):
+class ComputeResources(Resource):
+    """
+    Update the container's resources using the docker.update function
+    re-using the same parameters:
+        url params:
+           blkio_weight
+           cpu_period, cpu_quota, cpu_shares
+           cpuset_cpus
+           cpuset_mems
+           mem_limit
+           mem_reservation
+           memswap_limit
+           kernel_memory
+           restart_policy
+    see https://docs.docker.com/engine/reference/commandline/update/
+    or API docs: https://docker-py.readthedocs.io/en/stable/api.html#module-docker.api.container
+    :param dc_label: name of the DC
+    :param compute_name: compute container name
 
 
+    :return: docker inspect dict of deployed docker
+    """
     global dcs
 
     global dcs
 
-    def get(self, dc_label, compute_name):
-
-        logging.debug("API CALL: compute list")
+    def put(self, dc_label, compute_name):
+        logging.debug("REST CALL: update container resources")
 
         try:
 
         try:
-            return dcs.get(dc_label).containers.get(compute_name).getStatus(), 200
+            c = self._update_resources(dc_label, compute_name)
+            return c.getStatus(), 200, CORS_HEADER
         except Exception as ex:
             logging.exception("API error.")
         except Exception as ex:
             logging.exception("API error.")
-            return ex.message, 500
+            return ex.message, 500, CORS_HEADER
+
+    def _update_resources(self, dc_label, compute_name):
+
+        # get URL parameters
+        params = request.args
+        # then no data
+        if params is None:
+            params = {}
+        logging.debug("REST CALL: update container resources {0}".format(params))
+        #check if container exists
+        d = dcs.get(dc_label).net.getNodeByName(compute_name)
+
+        # general request of cpu percentage
+        # create a mutable copy
+        params = params.to_dict()
+        if 'cpu_bw' in params:
+            cpu_period = int(dcs.get(dc_label).net.cpu_period)
+            value = params.get('cpu_bw')
+            cpu_quota = int(cpu_period * float(value))
+            #put default values back
+            if float(value) <= 0:
+                cpu_period = 100000
+                cpu_quota = -1
+            params['cpu_period'] = cpu_period
+            params['cpu_quota'] = cpu_quota
+            #d.updateCpuLimit(cpu_period=cpu_period, cpu_quota=cpu_quota)
+
+        # only pass allowed keys to docker
+        allowed_keys = ['blkio_weight', 'cpu_period', 'cpu_quota', 'cpu_shares', 'cpuset_cpus',
+                        'cpuset_mems', 'mem_limit', 'mem_reservation', 'memswap_limit',
+                        'kernel_memory', 'restart_policy']
+        filtered_params = {key:params[key] for key in allowed_keys if key in params}
+
+        d.update_resources(**filtered_params)
+
+        return d
 
 class DatacenterList(Resource):
 
 class DatacenterList(Resource):
-
     global dcs
 
     def get(self):
         logging.debug("API CALL: datacenter list")
         try:
     global dcs
 
     def get(self):
         logging.debug("API CALL: datacenter list")
         try:
-            return [d.getStatus() for d in dcs.itervalues()], 200
+            return [d.getStatus() for d in dcs.itervalues()], 200, CORS_HEADER
         except Exception as ex:
             logging.exception("API error.")
         except Exception as ex:
             logging.exception("API error.")
-            return ex.message, 500
+            return ex.message, 500, CORS_HEADER
 
 
-class DatacenterStatus(Resource):
 
 
+class DatacenterStatus(Resource):
     global dcs
 
     def get(self, dc_label):
         logging.debug("API CALL: datacenter status")
         try:
     global dcs
 
     def get(self, dc_label):
         logging.debug("API CALL: datacenter status")
         try:
-            return dcs.get(dc_label).getStatus(), 200
+            return dcs.get(dc_label).getStatus(), 200, CORS_HEADER
         except Exception as ex:
             logging.exception("API error.")
         except Exception as ex:
             logging.exception("API error.")
-            return ex.message, 500
+            return ex.message, 500, CORS_HEADER