X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2Fvim-emu.git;a=blobdiff_plain;f=src%2Femuvim%2Fapi%2Frest%2Fcompute.py;h=78d96da2ece7607796ffa70176f7060b2490ebcd;hp=78b49e309ffab5d046b43b1975b6e170567518a1;hb=2aecf1dc375ebd54a611ecef32790a0083053fbe;hpb=a9dd901a96789fea8b1f4dd6a11b6fdcc01da830 diff --git a/src/emuvim/api/rest/compute.py b/src/emuvim/api/rest/compute.py index 78b49e3..78d96da 100755 --- a/src/emuvim/api/rest/compute.py +++ b/src/emuvim/api/rest/compute.py @@ -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. -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. @@ -29,9 +29,14 @@ import logging from flask_restful import Resource from flask import request import json +import threading +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 = {} @@ -46,10 +51,12 @@ class Compute(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 """ + global dcs - def put(self, dc_label, compute_name): + def put(self, dc_label, compute_name, resource=None, value=None): + # deploy new container # check if json data is a dict data = request.json if data is None: @@ -63,32 +70,53 @@ class Compute(Resource): command = data.get("docker_command") try: - logging.debug("API CALL: compute start") + if compute_name is None or compute_name == "None": + logging.error("No compute name defined in request.") + return "No compute name defined in request.", 500, CORS_HEADER + if dc_label is None or dcs.get(dc_label) is None: + logging.error("No datacenter defined in request.") + return "No datacenter defined in request.", 500, CORS_HEADER c = dcs.get(dc_label).startCompute( compute_name, image=image, command=command, network=nw_list) + # (if available) trigger emu. entry point given in Dockerfile + try: + config = c.dcinfo.get("Config", dict()) + env = config.get("Env", list()) + for env_var in env: + var, cmd = map(str.strip, map(str, env_var.split('=', 1))) + logging.debug("%r = %r" % (var , cmd)) + if var=="SON_EMU_CMD" or var=="VIM_EMU_CMD": + logging.info("Executing entry point script in %r: %r" % (c.name, cmd)) + # execute command in new thread to ensure that API is not blocked by VNF + t = threading.Thread(target=c.cmdPrint, args=(cmd,)) + t.daemon = True + t.start() + except Exception as ex: + logging.warning("Couldn't run Docker entry point VIM_EMU_CMD") + logging.exception("Exception:") # return docker inspect dict - return c.getStatus(), 200 + return c.getStatus(), 200, CORS_HEADER 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): logging.debug("API CALL: compute status") try: - return dcs.get(dc_label).containers.get(compute_name).getStatus(), 200 + 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 + return ex.message, 500, CORS_HEADER def delete(self, dc_label, compute_name): 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.") - return ex.message, 500 + return ex.message, 500, CORS_HEADER def _parse_network(self, network_str): ''' @@ -113,23 +141,99 @@ class Compute(Resource): class ComputeList(Resource): global dcs - def get(self, dc_label): + def get(self, dc_label=None): 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 = [] + all_extSAPs = [] 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 - 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.") + return ex.message, 500, CORS_HEADER + +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 + + def put(self, dc_label, compute_name): + logging.debug("REST CALL: update container resources") + + try: + c = self._update_resources(dc_label, compute_name) + return c.getStatus(), 200, CORS_HEADER 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): global dcs @@ -137,10 +241,10 @@ class DatacenterList(Resource): 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.") - return ex.message, 500 + return ex.message, 500, CORS_HEADER class DatacenterStatus(Resource): @@ -149,7 +253,7 @@ class DatacenterStatus(Resource): 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.") - return ex.message, 500 + return ex.message, 500, CORS_HEADER