Merge pull request #233 from mpeuster/demo-mano-integration
[osm/vim-emu.git] / src / emuvim / api / rest / compute.py
1 """
2 Copyright (c) 2015 SONATA-NFV and Paderborn University
3 ALL RIGHTS RESERVED.
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16
17 Neither the name of the SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
18 nor the names of its contributors may be used to endorse or promote
19 products derived from this software without specific prior written
20 permission.
21
22 This work has been performed in the framework of the SONATA project,
23 funded by the European Commission under Grant number 671517 through
24 the Horizon 2020 and 5G-PPP programmes. The authors would like to
25 acknowledge the contributions of their colleagues of the SONATA
26 partner consortium (www.sonata-nfv.eu).
27 """
28 import logging
29 from flask_restful import Resource
30 from flask import request
31 import json
32 from copy import deepcopy
33
34 logging.basicConfig()
35
36 CORS_HEADER = {'Access-Control-Allow-Origin': '*'}
37
38 # the dcs dict is set in the rest_api_endpoint.py upon datacenter init
39 dcs = {}
40
41
42 class Compute(Resource):
43 """
44 Start a new compute instance: A docker container (note: zerorpc does not support keyword arguments)
45 :param dc_label: name of the DC
46 :param compute_name: compute container name
47 :param image: image name
48 :param command: command to execute
49 :param network: list of all interface of the vnf, with their parameters (id=id1,ip=x.x.x.x/x),...
50 example networks list({"id":"input","ip": "10.0.0.254/8"}, {"id":"output","ip": "11.0.0.254/24"})
51 :return: docker inspect dict of deployed docker
52 """
53
54 global dcs
55
56 def put(self, dc_label, compute_name, resource=None, value=None):
57
58 # deploy new container
59 # check if json data is a dict
60 data = request.json
61 if data is None:
62 data = {}
63 elif type(data) is not dict:
64 data = json.loads(request.json)
65
66 network = data.get("network")
67 nw_list = self._parse_network(network)
68 image = data.get("image")
69 command = data.get("docker_command")
70
71 try:
72 logging.debug("API CALL: compute start")
73 c = dcs.get(dc_label).startCompute(
74 compute_name, image=image, command=command, network=nw_list)
75 # return docker inspect dict
76 return c.getStatus(), 200, CORS_HEADER
77 except Exception as ex:
78 logging.exception("API error.")
79 return ex.message, 500, CORS_HEADER
80
81 def get(self, dc_label, compute_name):
82
83 logging.debug("API CALL: compute status")
84
85 try:
86 return dcs.get(dc_label).containers.get(compute_name).getStatus(), 200, CORS_HEADER
87 except Exception as ex:
88 logging.exception("API error.")
89 return ex.message, 500, CORS_HEADER
90
91 def delete(self, dc_label, compute_name):
92 logging.debug("API CALL: compute stop")
93 try:
94 return dcs.get(dc_label).stopCompute(compute_name), 200, CORS_HEADER
95 except Exception as ex:
96 logging.exception("API error.")
97 return ex.message, 500, CORS_HEADER
98
99 def _parse_network(self, network_str):
100 '''
101 parse the options for all network interfaces of the vnf
102 :param network_str: (id=x,ip=x.x.x.x/x), ...
103 :return: list of dicts [{"id":x,"ip":"x.x.x.x/x"}, ...]
104 '''
105 nw_list = list()
106
107 # TODO make this more robust with regex check
108 if network_str is None:
109 return nw_list
110
111 networks = network_str[1:-1].split('),(')
112 for nw in networks:
113 nw_dict = dict(tuple(e.split('=')) for e in nw.split(','))
114 nw_list.append(nw_dict)
115
116 return nw_list
117
118
119 class ComputeList(Resource):
120 global dcs
121
122 def get(self, dc_label=None):
123 logging.debug("API CALL: compute list")
124 try:
125 if dc_label is None or dc_label == 'None':
126 # return list with all compute nodes in all DCs
127 all_containers = []
128 all_extSAPs = []
129 for dc in dcs.itervalues():
130 all_containers += dc.listCompute()
131 all_extSAPs += dc.listExtSAPs()
132
133 extSAP_list = [(sap.name, sap.getStatus()) for sap in all_extSAPs]
134 container_list = [(c.name, c.getStatus()) for c in all_containers]
135 total_list = container_list + extSAP_list
136 return total_list, 200, CORS_HEADER
137 else:
138 # return list of compute nodes for specified DC
139 container_list = [(c.name, c.getStatus()) for c in dcs.get(dc_label).listCompute()]
140 extSAP_list = [(sap.name, sap.getStatus()) for sap in dcs.get(dc_label).listExtSAPs()]
141 total_list = container_list + extSAP_list
142 return total_list, 200, CORS_HEADER
143 except Exception as ex:
144 logging.exception("API error.")
145 return ex.message, 500, CORS_HEADER
146
147 class ComputeResources(Resource):
148 """
149 Update the container's resources using the docker.update function
150 re-using the same parameters:
151 url params:
152 blkio_weight
153 cpu_period, cpu_quota, cpu_shares
154 cpuset_cpus
155 cpuset_mems
156 mem_limit
157 mem_reservation
158 memswap_limit
159 kernel_memory
160 restart_policy
161 see https://docs.docker.com/engine/reference/commandline/update/
162 or API docs: https://docker-py.readthedocs.io/en/stable/api.html#module-docker.api.container
163 :param dc_label: name of the DC
164 :param compute_name: compute container name
165
166 :return: docker inspect dict of deployed docker
167 """
168 global dcs
169
170 def put(self, dc_label, compute_name):
171 logging.debug("REST CALL: update container resources")
172
173 try:
174 c = self._update_resources(dc_label, compute_name)
175 return c.getStatus(), 200, CORS_HEADER
176 except Exception as ex:
177 logging.exception("API error.")
178 return ex.message, 500, CORS_HEADER
179
180 def _update_resources(self, dc_label, compute_name):
181
182 # get URL parameters
183 params = request.args
184 # then no data
185 if params is None:
186 params = {}
187 logging.debug("REST CALL: update container resources {0}".format(params))
188 #check if container exists
189 d = dcs.get(dc_label).net.getNodeByName(compute_name)
190
191 # general request of cpu percentage
192 # create a mutable copy
193 params = params.to_dict()
194 if 'cpu_bw' in params:
195 cpu_period = int(dcs.get(dc_label).net.cpu_period)
196 value = params.get('cpu_bw')
197 cpu_quota = int(cpu_period * float(value))
198 #put default values back
199 if float(value) <= 0:
200 cpu_period = 100000
201 cpu_quota = -1
202 params['cpu_period'] = cpu_period
203 params['cpu_quota'] = cpu_quota
204 #d.updateCpuLimit(cpu_period=cpu_period, cpu_quota=cpu_quota)
205
206 # only pass allowed keys to docker
207 allowed_keys = ['blkio_weight', 'cpu_period', 'cpu_quota', 'cpu_shares', 'cpuset_cpus',
208 'cpuset_mems', 'mem_limit', 'mem_reservation', 'memswap_limit',
209 'kernel_memory', 'restart_policy']
210 filtered_params = {key:params[key] for key in allowed_keys if key in params}
211
212 d.update_resources(**filtered_params)
213
214 return d
215
216 class DatacenterList(Resource):
217 global dcs
218
219 def get(self):
220 logging.debug("API CALL: datacenter list")
221 try:
222 return [d.getStatus() for d in dcs.itervalues()], 200, CORS_HEADER
223 except Exception as ex:
224 logging.exception("API error.")
225 return ex.message, 500, CORS_HEADER
226
227
228 class DatacenterStatus(Resource):
229 global dcs
230
231 def get(self, dc_label):
232 logging.debug("API CALL: datacenter status")
233 try:
234 return dcs.get(dc_label).getStatus(), 200, CORS_HEADER
235 except Exception as ex:
236 logging.exception("API error.")
237 return ex.message, 500, CORS_HEADER