display assigned ip's and interfaces on dashboard
[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(level=logging.INFO)
35
36 CORS_HEADER = {'Access-Control-Allow-Origin': '*'}
37
38 dcs = {}
39
40
41 class Compute(Resource):
42 """
43 Start a new compute instance: A docker container (note: zerorpc does not support keyword arguments)
44 :param dc_label: name of the DC
45 :param compute_name: compute container name
46 :param image: image name
47 :param command: command to execute
48 :param network: list of all interface of the vnf, with their parameters (id=id1,ip=x.x.x.x/x),...
49 example networks list({"id":"input","ip": "10.0.0.254/8"}, {"id":"output","ip": "11.0.0.254/24"})
50 :return: docker inspect dict of deployed docker
51 """
52 global dcs
53
54 def put(self, dc_label, compute_name, resource=None, value=None):
55
56 # deploy new container
57 # check if json data is a dict
58 data = request.json
59 if data is None:
60 data = {}
61 elif type(data) is not dict:
62 data = json.loads(request.json)
63
64 network = data.get("network")
65 nw_list = self._parse_network(network)
66 image = data.get("image")
67 command = data.get("docker_command")
68
69 try:
70 logging.debug("API CALL: compute start")
71 c = dcs.get(dc_label).startCompute(
72 compute_name, image=image, command=command, network=nw_list)
73 # return docker inspect dict
74 return c.getStatus(), 200, CORS_HEADER
75 except Exception as ex:
76 logging.exception("API error.")
77 return ex.message, 500, CORS_HEADER
78
79 def get(self, dc_label, compute_name):
80
81 logging.debug("API CALL: compute status")
82
83 try:
84 return dcs.get(dc_label).containers.get(compute_name).getStatus(), 200, CORS_HEADER
85 except Exception as ex:
86 logging.exception("API error.")
87 return ex.message, 500, CORS_HEADER
88
89 def delete(self, dc_label, compute_name):
90 logging.debug("API CALL: compute stop")
91 try:
92 return dcs.get(dc_label).stopCompute(compute_name), 200, CORS_HEADER
93 except Exception as ex:
94 logging.exception("API error.")
95 return ex.message, 500, CORS_HEADER
96
97 def _parse_network(self, network_str):
98 '''
99 parse the options for all network interfaces of the vnf
100 :param network_str: (id=x,ip=x.x.x.x/x), ...
101 :return: list of dicts [{"id":x,"ip":"x.x.x.x/x"}, ...]
102 '''
103 nw_list = list()
104
105 # TODO make this more robust with regex check
106 if network_str is None:
107 return nw_list
108
109 networks = network_str[1:-1].split('),(')
110 for nw in networks:
111 nw_dict = dict(tuple(e.split('=')) for e in nw.split(','))
112 nw_list.append(nw_dict)
113
114 return nw_list
115
116
117 class ComputeList(Resource):
118 global dcs
119
120 def get(self, dc_label=None):
121 logging.debug("API CALL: compute list")
122 try:
123 if dc_label is None or dc_label == 'None':
124 # return list with all compute nodes in all DCs
125 all_containers = []
126 all_extSAPs = []
127 for dc in dcs.itervalues():
128 all_containers += dc.listCompute()
129 all_extSAPs += dc.listExtSAPs()
130
131 extSAP_list = [(sap.name, sap.getStatus()) for sap in all_extSAPs]
132 container_list = [(c.name, c.getStatus()) for c in all_containers]
133 total_list = container_list + extSAP_list
134 return total_list, 200, CORS_HEADER
135 else:
136 # return list of compute nodes for specified DC
137 container_list = [(c.name, c.getStatus()) for c in dcs.get(dc_label).listCompute()]
138 extSAP_list = [(sap.name, sap.getStatus()) for sap in dcs.get(dc_label).listExtSAPs()]
139 total_list = container_list + extSAP_list
140 return total_list, 200, CORS_HEADER
141 except Exception as ex:
142 logging.exception("API error.")
143 return ex.message, 500, CORS_HEADER
144
145 class ComputeResources(Resource):
146 """
147 Update the container's resources using the docker.update function
148 re-using the same parameters:
149 url params:
150 blkio_weight
151 cpu_period, cpu_quota, cpu_shares
152 cpuset_cpus
153 cpuset_mems
154 mem_limit
155 mem_reservation
156 memswap_limit
157 kernel_memory
158 restart_policy
159 see https://docs.docker.com/engine/reference/commandline/update/
160 or API docs: https://docker-py.readthedocs.io/en/stable/api.html#module-docker.api.container
161 :param dc_label: name of the DC
162 :param compute_name: compute container name
163
164 :return: docker inspect dict of deployed docker
165 """
166 global dcs
167
168 def put(self, dc_label, compute_name):
169 logging.debug("REST CALL: update container resources")
170
171 try:
172 c = self._update_resources(dc_label, compute_name)
173 return c.getStatus(), 200, CORS_HEADER
174 except Exception as ex:
175 logging.exception("API error.")
176 return ex.message, 500, CORS_HEADER
177
178 def _update_resources(self, dc_label, compute_name):
179
180 # get URL parameters
181 params = request.args
182 # then no data
183 if params is None:
184 params = {}
185 logging.debug("REST CALL: update container resources {0}".format(params))
186 #check if container exists
187 d = dcs.get(dc_label).net.getNodeByName(compute_name)
188
189 # general request of cpu percentage
190 # create a mutable copy
191 params = params.to_dict()
192 if 'cpu_bw' in params:
193 cpu_period = int(dcs.get(dc_label).net.cpu_period)
194 value = params.get('cpu_bw')
195 cpu_quota = int(cpu_period * float(value))
196 #put default values back
197 if float(value) <= 0:
198 cpu_period = 100000
199 cpu_quota = -1
200 params['cpu_period'] = cpu_period
201 params['cpu_quota'] = cpu_quota
202 #d.updateCpuLimit(cpu_period=cpu_period, cpu_quota=cpu_quota)
203
204 # only pass allowed keys to docker
205 allowed_keys = ['blkio_weight', 'cpu_period', 'cpu_quota', 'cpu_shares', 'cpuset_cpus',
206 'cpuset_mems', 'mem_limit', 'mem_reservation', 'memswap_limit',
207 'kernel_memory', 'restart_policy']
208 filtered_params = {key:params[key] for key in allowed_keys if key in params}
209
210 d.update_resources(**filtered_params)
211
212 return d
213
214 class DatacenterList(Resource):
215 global dcs
216
217 def get(self):
218 logging.debug("API CALL: datacenter list")
219 try:
220 return [d.getStatus() for d in dcs.itervalues()], 200, CORS_HEADER
221 except Exception as ex:
222 logging.exception("API error.")
223 return ex.message, 500, CORS_HEADER
224
225
226 class DatacenterStatus(Resource):
227 global dcs
228
229 def get(self, dc_label):
230 logging.debug("API CALL: datacenter status")
231 try:
232 return dcs.get(dc_label).getStatus(), 200, CORS_HEADER
233 except Exception as ex:
234 logging.exception("API error.")
235 return ex.message, 500, CORS_HEADER