78d96da2ece7607796ffa70176f7060b2490ebcd
2 Copyright (c) 2015 SONATA-NFV and Paderborn University
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
9 http://www.apache.org/licenses/LICENSE-2.0
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.
17 Neither the name of the SONATA-NFV, Paderborn University
18 nor the names of its contributors may be used to endorse or promote
19 products derived from this software without specific prior written
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).
29 from flask_restful
import Resource
30 from flask
import request
33 from copy
import deepcopy
37 CORS_HEADER
= {'Access-Control-Allow-Origin': '*'}
39 # the dcs dict is set in the rest_api_endpoint.py upon datacenter init
43 class Compute(Resource
):
45 Start a new compute instance: A docker container (note: zerorpc does not support keyword arguments)
46 :param dc_label: name of the DC
47 :param compute_name: compute container name
48 :param image: image name
49 :param command: command to execute
50 :param network: list of all interface of the vnf, with their parameters (id=id1,ip=x.x.x.x/x),...
51 example networks list({"id":"input","ip": "10.0.0.254/8"}, {"id":"output","ip": "11.0.0.254/24"})
52 :return: docker inspect dict of deployed docker
57 def put(self
, dc_label
, compute_name
, resource
=None, value
=None):
59 # deploy new container
60 # check if json data is a dict
64 elif type(data
) is not dict:
65 data
= json
.loads(request
.json
)
67 network
= data
.get("network")
68 nw_list
= self
._parse
_network
(network
)
69 image
= data
.get("image")
70 command
= data
.get("docker_command")
73 if compute_name
is None or compute_name
== "None":
74 logging
.error("No compute name defined in request.")
75 return "No compute name defined in request.", 500, CORS_HEADER
76 if dc_label
is None or dcs
.get(dc_label
) is None:
77 logging
.error("No datacenter defined in request.")
78 return "No datacenter defined in request.", 500, CORS_HEADER
79 c
= dcs
.get(dc_label
).startCompute(
80 compute_name
, image
=image
, command
=command
, network
=nw_list
)
81 # (if available) trigger emu. entry point given in Dockerfile
83 config
= c
.dcinfo
.get("Config", dict())
84 env
= config
.get("Env", list())
86 var
, cmd
= map(str.strip
, map(str, env_var
.split('=', 1)))
87 logging
.debug("%r = %r" % (var
, cmd
))
88 if var
=="SON_EMU_CMD" or var
=="VIM_EMU_CMD":
89 logging
.info("Executing entry point script in %r: %r" % (c
.name
, cmd
))
90 # execute command in new thread to ensure that API is not blocked by VNF
91 t
= threading
.Thread(target
=c
.cmdPrint
, args
=(cmd
,))
94 except Exception as ex
:
95 logging
.warning("Couldn't run Docker entry point VIM_EMU_CMD")
96 logging
.exception("Exception:")
97 # return docker inspect dict
98 return c
.getStatus(), 200, CORS_HEADER
99 except Exception as ex
:
100 logging
.exception("API error.")
101 return ex
.message
, 500, CORS_HEADER
103 def get(self
, dc_label
, compute_name
):
105 logging
.debug("API CALL: compute status")
108 return dcs
.get(dc_label
).containers
.get(compute_name
).getStatus(), 200, CORS_HEADER
109 except Exception as ex
:
110 logging
.exception("API error.")
111 return ex
.message
, 500, CORS_HEADER
113 def delete(self
, dc_label
, compute_name
):
114 logging
.debug("API CALL: compute stop")
116 return dcs
.get(dc_label
).stopCompute(compute_name
), 200, CORS_HEADER
117 except Exception as ex
:
118 logging
.exception("API error.")
119 return ex
.message
, 500, CORS_HEADER
121 def _parse_network(self
, network_str
):
123 parse the options for all network interfaces of the vnf
124 :param network_str: (id=x,ip=x.x.x.x/x), ...
125 :return: list of dicts [{"id":x,"ip":"x.x.x.x/x"}, ...]
129 # TODO make this more robust with regex check
130 if network_str
is None:
133 networks
= network_str
[1:-1].split('),(')
135 nw_dict
= dict(tuple(e
.split('=')) for e
in nw
.split(','))
136 nw_list
.append(nw_dict
)
141 class ComputeList(Resource
):
144 def get(self
, dc_label
=None):
145 logging
.debug("API CALL: compute list")
147 if dc_label
is None or dc_label
== 'None':
148 # return list with all compute nodes in all DCs
151 for dc
in dcs
.itervalues():
152 all_containers
+= dc
.listCompute()
153 all_extSAPs
+= dc
.listExtSAPs()
155 extSAP_list
= [(sap
.name
, sap
.getStatus()) for sap
in all_extSAPs
]
156 container_list
= [(c
.name
, c
.getStatus()) for c
in all_containers
]
157 total_list
= container_list
+ extSAP_list
158 return total_list
, 200, CORS_HEADER
160 # return list of compute nodes for specified DC
161 container_list
= [(c
.name
, c
.getStatus()) for c
in dcs
.get(dc_label
).listCompute()]
162 extSAP_list
= [(sap
.name
, sap
.getStatus()) for sap
in dcs
.get(dc_label
).listExtSAPs()]
163 total_list
= container_list
+ extSAP_list
164 return total_list
, 200, CORS_HEADER
165 except Exception as ex
:
166 logging
.exception("API error.")
167 return ex
.message
, 500, CORS_HEADER
169 class ComputeResources(Resource
):
171 Update the container's resources using the docker.update function
172 re-using the same parameters:
175 cpu_period, cpu_quota, cpu_shares
183 see https://docs.docker.com/engine/reference/commandline/update/
184 or API docs: https://docker-py.readthedocs.io/en/stable/api.html#module-docker.api.container
185 :param dc_label: name of the DC
186 :param compute_name: compute container name
188 :return: docker inspect dict of deployed docker
192 def put(self
, dc_label
, compute_name
):
193 logging
.debug("REST CALL: update container resources")
196 c
= self
._update
_resources
(dc_label
, compute_name
)
197 return c
.getStatus(), 200, CORS_HEADER
198 except Exception as ex
:
199 logging
.exception("API error.")
200 return ex
.message
, 500, CORS_HEADER
202 def _update_resources(self
, dc_label
, compute_name
):
205 params
= request
.args
209 logging
.debug("REST CALL: update container resources {0}".format(params
))
210 #check if container exists
211 d
= dcs
.get(dc_label
).net
.getNodeByName(compute_name
)
213 # general request of cpu percentage
214 # create a mutable copy
215 params
= params
.to_dict()
216 if 'cpu_bw' in params
:
217 cpu_period
= int(dcs
.get(dc_label
).net
.cpu_period
)
218 value
= params
.get('cpu_bw')
219 cpu_quota
= int(cpu_period
* float(value
))
220 #put default values back
221 if float(value
) <= 0:
224 params
['cpu_period'] = cpu_period
225 params
['cpu_quota'] = cpu_quota
226 #d.updateCpuLimit(cpu_period=cpu_period, cpu_quota=cpu_quota)
228 # only pass allowed keys to docker
229 allowed_keys
= ['blkio_weight', 'cpu_period', 'cpu_quota', 'cpu_shares', 'cpuset_cpus',
230 'cpuset_mems', 'mem_limit', 'mem_reservation', 'memswap_limit',
231 'kernel_memory', 'restart_policy']
232 filtered_params
= {key
:params
[key
] for key
in allowed_keys
if key
in params
}
234 d
.update_resources(**filtered_params
)
238 class DatacenterList(Resource
):
242 logging
.debug("API CALL: datacenter list")
244 return [d
.getStatus() for d
in dcs
.itervalues()], 200, CORS_HEADER
245 except Exception as ex
:
246 logging
.exception("API error.")
247 return ex
.message
, 500, CORS_HEADER
250 class DatacenterStatus(Resource
):
253 def get(self
, dc_label
):
254 logging
.debug("API CALL: datacenter status")
256 return dcs
.get(dc_label
).getStatus(), 200, CORS_HEADER
257 except Exception as ex
:
258 logging
.exception("API error.")
259 return ex
.message
, 500, CORS_HEADER