blob: 07d059fcfd08eaf540ed56a00f6bd50bb0c2e5bf [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
hadik3r237d3f52016-06-27 17:57:49 +020034logging.basicConfig(level=logging.INFO)
35
peustermdfc14602017-01-13 08:22:45 +010036CORS_HEADER = {'Access-Control-Allow-Origin': '*'}
37
hadik3r237d3f52016-06-27 17:57:49 +020038dcs = {}
39
hadik3ra9dd9012016-08-09 10:51:13 +020040
41class Compute(Resource):
hadik3r237d3f52016-06-27 17:57:49 +020042 """
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),...
stevenvanrossem73efd192016-06-29 01:44:07 +020049 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
hadik3r237d3f52016-06-27 17:57:49 +020051 """
52 global dcs
53
stevenvanrossemb3f34172016-11-16 23:30:57 +010054 def put(self, dc_label, compute_name, resource=None, value=None):
hadik3ra9dd9012016-08-09 10:51:13 +020055
stevenvanrossemb3f34172016-11-16 23:30:57 +010056 # deploy new container
hadik3ra9dd9012016-08-09 10:51:13 +020057 # 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
hadik3r237d3f52016-06-27 17:57:49 +020069 try:
hadik3ra9dd9012016-08-09 10:51:13 +020070 logging.debug("API CALL: compute start")
hadik3r237d3f52016-06-27 17:57:49 +020071 c = dcs.get(dc_label).startCompute(
hadik3ra9dd9012016-08-09 10:51:13 +020072 compute_name, image=image, command=command, network=nw_list)
hadik3r237d3f52016-06-27 17:57:49 +020073 # return docker inspect dict
peustermdfc14602017-01-13 08:22:45 +010074 return c.getStatus(), 200, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +020075 except Exception as ex:
76 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +010077 return ex.message, 500, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +020078
hadik3ra9dd9012016-08-09 10:51:13 +020079 def get(self, dc_label, compute_name):
80
81 logging.debug("API CALL: compute status")
82
83 try:
peustermdfc14602017-01-13 08:22:45 +010084 return dcs.get(dc_label).containers.get(compute_name).getStatus(), 200, CORS_HEADER
hadik3ra9dd9012016-08-09 10:51:13 +020085 except Exception as ex:
86 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +010087 return ex.message, 500, CORS_HEADER
hadik3ra9dd9012016-08-09 10:51:13 +020088
89 def delete(self, dc_label, compute_name):
90 logging.debug("API CALL: compute stop")
91 try:
peustermdfc14602017-01-13 08:22:45 +010092 return dcs.get(dc_label).stopCompute(compute_name), 200, CORS_HEADER
hadik3ra9dd9012016-08-09 10:51:13 +020093 except Exception as ex:
94 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +010095 return ex.message, 500, CORS_HEADER
hadik3ra9dd9012016-08-09 10:51:13 +020096
stevenvanrossemff6b4042016-07-14 20:51:37 +020097 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
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200105 # TODO make this more robust with regex check
hadik3ra9dd9012016-08-09 10:51:13 +0200106 if network_str is None:
stevenvanrossemff6b4042016-07-14 20:51:37 +0200107 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
hadik3r237d3f52016-06-27 17:57:49 +0200116
117class ComputeList(Resource):
hadik3r237d3f52016-06-27 17:57:49 +0200118 global dcs
119
stevenvanrossem65819b82016-08-05 18:21:47 +0200120 def get(self, dc_label=None):
hadik3r237d3f52016-06-27 17:57:49 +0200121 logging.debug("API CALL: compute list")
122 try:
stevenvanrossem34566442016-08-10 00:13:24 +0200123 if dc_label is None or dc_label == 'None':
hadik3r237d3f52016-06-27 17:57:49 +0200124 # return list with all compute nodes in all DCs
125 all_containers = []
stevenvanrossem17b6e882017-05-04 16:51:34 +0200126 all_extSAPs = []
hadik3r237d3f52016-06-27 17:57:49 +0200127 for dc in dcs.itervalues():
128 all_containers += dc.listCompute()
stevenvanrossem17b6e882017-05-04 16:51:34 +0200129 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
hadik3r237d3f52016-06-27 17:57:49 +0200135 else:
136 # return list of compute nodes for specified DC
stevenvanrossem17b6e882017-05-04 16:51:34 +0200137 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
hadik3r237d3f52016-06-27 17:57:49 +0200141 except Exception as ex:
142 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +0100143 return ex.message, 500, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200144
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100145class 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 = {}
stevenvanrossem33d76892017-02-13 00:13:37 +0100185 logging.debug("REST CALL: update container resources {0}".format(params))
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100186 #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()
stevenvanrossem33d76892017-02-13 00:13:37 +0100192 if 'cpu_bw' in params:
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100193 cpu_period = int(dcs.get(dc_label).net.cpu_period)
stevenvanrossem33d76892017-02-13 00:13:37 +0100194 value = params.get('cpu_bw')
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100195 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
hadik3r237d3f52016-06-27 17:57:49 +0200213
hadik3r237d3f52016-06-27 17:57:49 +0200214class DatacenterList(Resource):
hadik3r237d3f52016-06-27 17:57:49 +0200215 global dcs
216
217 def get(self):
218 logging.debug("API CALL: datacenter list")
219 try:
peustermdfc14602017-01-13 08:22:45 +0100220 return [d.getStatus() for d in dcs.itervalues()], 200, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200221 except Exception as ex:
222 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +0100223 return ex.message, 500, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200224
hadik3r237d3f52016-06-27 17:57:49 +0200225
hadik3ra9dd9012016-08-09 10:51:13 +0200226class DatacenterStatus(Resource):
hadik3r237d3f52016-06-27 17:57:49 +0200227 global dcs
228
229 def get(self, dc_label):
230 logging.debug("API CALL: datacenter status")
231 try:
peustermdfc14602017-01-13 08:22:45 +0100232 return dcs.get(dc_label).getStatus(), 200, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200233 except Exception as ex:
234 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +0100235 return ex.message, 500, CORS_HEADER