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 [, 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
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
32 from copy
import deepcopy
34 logging
.basicConfig(level
=logging
.INFO
)
36 CORS_HEADER
= {'Access-Control-Allow-Origin': '*'}
41 class Compute(Resource
):
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
54 def put(self
, dc_label
, compute_name
, resource
=None, value
=None):
56 # deploy new container
57 # check if json data is a dict
61 elif type(data
) is not dict:
62 data
= json
.loads(request
.json
)
64 network
= data
.get("network")
65 nw_list
= self
._parse
_network
(network
)
66 image
= data
.get("image")
67 command
= data
.get("docker_command")
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
79 def get(self
, dc_label
, compute_name
):
81 logging
.debug("API CALL: compute status")
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
89 def delete(self
, dc_label
, compute_name
):
90 logging
.debug("API CALL: compute stop")
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
97 def _parse_network(self
, network_str
):
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"}, ...]
105 # TODO make this more robust with regex check
106 if network_str
is None:
109 networks
= network_str
[1:-1].split('),(')
111 nw_dict
= dict(tuple(e
.split('=')) for e
in nw
.split(','))
112 nw_list
.append(nw_dict
)
117 class ComputeList(Resource
):
120 def get(self
, dc_label
=None):
121 logging
.debug("API CALL: compute list")
123 if dc_label
is None or dc_label
== 'None':
124 # return list with all compute nodes in all DCs
126 for dc
in dcs
.itervalues():
127 all_containers
+= dc
.listCompute()
128 return [(c
.name
, c
.getStatus()) for c
in all_containers
], 200, CORS_HEADER
130 # return list of compute nodes for specified DC
131 return [(c
.name
, c
.getStatus())
132 for c
in dcs
.get(dc_label
).listCompute()], 200, CORS_HEADER
133 except Exception as ex
:
134 logging
.exception("API error.")
135 return ex
.message
, 500, CORS_HEADER
137 class ComputeResources(Resource
):
139 Update the container's resources using the docker.update function
140 re-using the same parameters:
143 cpu_period, cpu_quota, cpu_shares
151 see https://docs.docker.com/engine/reference/commandline/update/
152 or API docs: https://docker-py.readthedocs.io/en/stable/api.html#module-docker.api.container
153 :param dc_label: name of the DC
154 :param compute_name: compute container name
156 :return: docker inspect dict of deployed docker
160 def put(self
, dc_label
, compute_name
):
161 logging
.debug("REST CALL: update container resources")
164 c
= self
._update
_resources
(dc_label
, compute_name
)
165 return c
.getStatus(), 200, CORS_HEADER
166 except Exception as ex
:
167 logging
.exception("API error.")
168 return ex
.message
, 500, CORS_HEADER
170 def _update_resources(self
, dc_label
, compute_name
):
173 params
= request
.args
177 logging
.debug("REST CALL: update container resources {0}".format(params
))
178 #check if container exists
179 d
= dcs
.get(dc_label
).net
.getNodeByName(compute_name
)
181 # general request of cpu percentage
182 # create a mutable copy
183 params
= params
.to_dict()
184 if 'cpu_bw' in params
:
185 cpu_period
= int(dcs
.get(dc_label
).net
.cpu_period
)
186 value
= params
.get('cpu_bw')
187 cpu_quota
= int(cpu_period
* float(value
))
188 #put default values back
189 if float(value
) <= 0:
192 params
['cpu_period'] = cpu_period
193 params
['cpu_quota'] = cpu_quota
194 #d.updateCpuLimit(cpu_period=cpu_period, cpu_quota=cpu_quota)
196 # only pass allowed keys to docker
197 allowed_keys
= ['blkio_weight', 'cpu_period', 'cpu_quota', 'cpu_shares', 'cpuset_cpus',
198 'cpuset_mems', 'mem_limit', 'mem_reservation', 'memswap_limit',
199 'kernel_memory', 'restart_policy']
200 filtered_params
= {key
:params
[key
] for key
in allowed_keys
if key
in params
}
202 d
.update_resources(**filtered_params
)
206 class DatacenterList(Resource
):
210 logging
.debug("API CALL: datacenter list")
212 return [d
.getStatus() for d
in dcs
.itervalues()], 200, CORS_HEADER
213 except Exception as ex
:
214 logging
.exception("API error.")
215 return ex
.message
, 500, CORS_HEADER
218 class DatacenterStatus(Resource
):
221 def get(self
, dc_label
):
222 logging
.debug("API CALL: datacenter status")
224 return dcs
.get(dc_label
).getStatus(), 200, CORS_HEADER
225 except Exception as ex
:
226 logging
.exception("API error.")
227 return ex
.message
, 500, CORS_HEADER