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
127 for dc
in dcs
.itervalues():
128 all_containers
+= dc
.listCompute()
129 all_extSAPs
+= dc
.listExtSAPs()
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
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
145 class ComputeResources(Resource
):
147 Update the container's resources using the docker.update function
148 re-using the same parameters:
151 cpu_period, cpu_quota, cpu_shares
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
164 :return: docker inspect dict of deployed docker
168 def put(self
, dc_label
, compute_name
):
169 logging
.debug("REST CALL: update container resources")
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
178 def _update_resources(self
, dc_label
, compute_name
):
181 params
= request
.args
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
)
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:
200 params
['cpu_period'] = cpu_period
201 params
['cpu_quota'] = cpu_quota
202 #d.updateCpuLimit(cpu_period=cpu_period, cpu_quota=cpu_quota)
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
}
210 d
.update_resources(**filtered_params
)
214 class DatacenterList(Resource
):
218 logging
.debug("API CALL: datacenter list")
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
226 class DatacenterStatus(Resource
):
229 def get(self
, dc_label
):
230 logging
.debug("API CALL: datacenter status")
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