blob: 28a92c4eb815aea1e8628ae35e4f81e2eeaa908f [file] [log] [blame]
peusterm79ef6ae2016-07-08 13:53:57 +02001"""
2Copyright (c) 2015 SONATA-NFV and Paderborn University
3ALL RIGHTS RESERVED.
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17Neither the name of the SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
18nor the names of its contributors may be used to endorse or promote
19products derived from this software without specific prior written
20permission.
21
22This work has been performed in the framework of the SONATA project,
23funded by the European Commission under Grant number 671517 through
24the Horizon 2020 and 5G-PPP programmes. The authors would like to
25acknowledge the contributions of their colleagues of the SONATA
26partner consortium (www.sonata-nfv.eu).
27"""
hadik3r237d3f52016-06-27 17:57:49 +020028import logging
stevenvanrossem73efd192016-06-29 01:44:07 +020029from flask_restful import Resource
30from flask import request
hadik3r237d3f52016-06-27 17:57:49 +020031import json
stevenvanrossem7953f2f2017-02-10 12:56:15 +010032from copy import deepcopy
hadik3r237d3f52016-06-27 17:57:49 +020033
peusterm5b428742017-06-16 10:08:11 +020034logging.basicConfig()
hadik3r237d3f52016-06-27 17:57:49 +020035
peustermdfc14602017-01-13 08:22:45 +010036CORS_HEADER = {'Access-Control-Allow-Origin': '*'}
37
stevenvanrossemc63c5492017-05-08 16:10:13 +020038# the dcs dict is set in the rest_api_endpoint.py upon datacenter init
hadik3r237d3f52016-06-27 17:57:49 +020039dcs = {}
40
hadik3ra9dd9012016-08-09 10:51:13 +020041
42class Compute(Resource):
hadik3r237d3f52016-06-27 17:57:49 +020043 """
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),...
stevenvanrossem73efd192016-06-29 01:44:07 +020050 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
hadik3r237d3f52016-06-27 17:57:49 +020052 """
stevenvanrossemc63c5492017-05-08 16:10:13 +020053
hadik3r237d3f52016-06-27 17:57:49 +020054 global dcs
55
stevenvanrossemb3f34172016-11-16 23:30:57 +010056 def put(self, dc_label, compute_name, resource=None, value=None):
hadik3ra9dd9012016-08-09 10:51:13 +020057
stevenvanrossemb3f34172016-11-16 23:30:57 +010058 # deploy new container
hadik3ra9dd9012016-08-09 10:51:13 +020059 # 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
hadik3r237d3f52016-06-27 17:57:49 +020071 try:
hadik3ra9dd9012016-08-09 10:51:13 +020072 logging.debug("API CALL: compute start")
hadik3r237d3f52016-06-27 17:57:49 +020073 c = dcs.get(dc_label).startCompute(
hadik3ra9dd9012016-08-09 10:51:13 +020074 compute_name, image=image, command=command, network=nw_list)
hadik3r237d3f52016-06-27 17:57:49 +020075 # return docker inspect dict
peustermdfc14602017-01-13 08:22:45 +010076 return c.getStatus(), 200, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +020077 except Exception as ex:
78 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +010079 return ex.message, 500, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +020080
hadik3ra9dd9012016-08-09 10:51:13 +020081 def get(self, dc_label, compute_name):
82
83 logging.debug("API CALL: compute status")
84
85 try:
peustermdfc14602017-01-13 08:22:45 +010086 return dcs.get(dc_label).containers.get(compute_name).getStatus(), 200, CORS_HEADER
hadik3ra9dd9012016-08-09 10:51:13 +020087 except Exception as ex:
88 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +010089 return ex.message, 500, CORS_HEADER
hadik3ra9dd9012016-08-09 10:51:13 +020090
91 def delete(self, dc_label, compute_name):
92 logging.debug("API CALL: compute stop")
93 try:
peustermdfc14602017-01-13 08:22:45 +010094 return dcs.get(dc_label).stopCompute(compute_name), 200, CORS_HEADER
hadik3ra9dd9012016-08-09 10:51:13 +020095 except Exception as ex:
96 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +010097 return ex.message, 500, CORS_HEADER
hadik3ra9dd9012016-08-09 10:51:13 +020098
stevenvanrossemff6b4042016-07-14 20:51:37 +020099 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
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200107 # TODO make this more robust with regex check
hadik3ra9dd9012016-08-09 10:51:13 +0200108 if network_str is None:
stevenvanrossemff6b4042016-07-14 20:51:37 +0200109 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
hadik3r237d3f52016-06-27 17:57:49 +0200118
119class ComputeList(Resource):
hadik3r237d3f52016-06-27 17:57:49 +0200120 global dcs
121
stevenvanrossem65819b82016-08-05 18:21:47 +0200122 def get(self, dc_label=None):
hadik3r237d3f52016-06-27 17:57:49 +0200123 logging.debug("API CALL: compute list")
124 try:
stevenvanrossem34566442016-08-10 00:13:24 +0200125 if dc_label is None or dc_label == 'None':
hadik3r237d3f52016-06-27 17:57:49 +0200126 # return list with all compute nodes in all DCs
127 all_containers = []
stevenvanrossem17b6e882017-05-04 16:51:34 +0200128 all_extSAPs = []
hadik3r237d3f52016-06-27 17:57:49 +0200129 for dc in dcs.itervalues():
130 all_containers += dc.listCompute()
stevenvanrossem17b6e882017-05-04 16:51:34 +0200131 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
hadik3r237d3f52016-06-27 17:57:49 +0200137 else:
138 # return list of compute nodes for specified DC
stevenvanrossem17b6e882017-05-04 16:51:34 +0200139 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
hadik3r237d3f52016-06-27 17:57:49 +0200143 except Exception as ex:
144 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +0100145 return ex.message, 500, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200146
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100147class 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 = {}
stevenvanrossem33d76892017-02-13 00:13:37 +0100187 logging.debug("REST CALL: update container resources {0}".format(params))
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100188 #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()
stevenvanrossem33d76892017-02-13 00:13:37 +0100194 if 'cpu_bw' in params:
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100195 cpu_period = int(dcs.get(dc_label).net.cpu_period)
stevenvanrossem33d76892017-02-13 00:13:37 +0100196 value = params.get('cpu_bw')
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100197 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
hadik3r237d3f52016-06-27 17:57:49 +0200215
hadik3r237d3f52016-06-27 17:57:49 +0200216class DatacenterList(Resource):
hadik3r237d3f52016-06-27 17:57:49 +0200217 global dcs
218
219 def get(self):
220 logging.debug("API CALL: datacenter list")
221 try:
peustermdfc14602017-01-13 08:22:45 +0100222 return [d.getStatus() for d in dcs.itervalues()], 200, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200223 except Exception as ex:
224 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +0100225 return ex.message, 500, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200226
hadik3r237d3f52016-06-27 17:57:49 +0200227
hadik3ra9dd9012016-08-09 10:51:13 +0200228class DatacenterStatus(Resource):
hadik3r237d3f52016-06-27 17:57:49 +0200229 global dcs
230
231 def get(self, dc_label):
232 logging.debug("API CALL: datacenter status")
233 try:
peustermdfc14602017-01-13 08:22:45 +0100234 return dcs.get(dc_label).getStatus(), 200, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200235 except Exception as ex:
236 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +0100237 return ex.message, 500, CORS_HEADER