blob: 8d46aa2fa1a4f02025e45abb0b925a69ea26fdb3 [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 = []
126 for dc in dcs.itervalues():
127 all_containers += dc.listCompute()
peustermdfc14602017-01-13 08:22:45 +0100128 return [(c.name, c.getStatus()) for c in all_containers], 200, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200129 else:
130 # return list of compute nodes for specified DC
131 return [(c.name, c.getStatus())
peustermdfc14602017-01-13 08:22:45 +0100132 for c in dcs.get(dc_label).listCompute()], 200, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200133 except Exception as ex:
134 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +0100135 return ex.message, 500, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200136
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100137class ComputeResources(Resource):
138 """
139 Update the container's resources using the docker.update function
140 re-using the same parameters:
141 url params:
142 blkio_weight
143 cpu_period, cpu_quota, cpu_shares
144 cpuset_cpus
145 cpuset_mems
146 mem_limit
147 mem_reservation
148 memswap_limit
149 kernel_memory
150 restart_policy
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
155
156 :return: docker inspect dict of deployed docker
157 """
158 global dcs
159
160 def put(self, dc_label, compute_name):
161 logging.debug("REST CALL: update container resources")
162
163 try:
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
169
170 def _update_resources(self, dc_label, compute_name):
171
172 # get URL parameters
173 params = request.args
174 # then no data
175 if params is None:
176 params = {}
stevenvanrossem33d76892017-02-13 00:13:37 +0100177 logging.debug("REST CALL: update container resources {0}".format(params))
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100178 #check if container exists
179 d = dcs.get(dc_label).net.getNodeByName(compute_name)
180
181 # general request of cpu percentage
182 # create a mutable copy
183 params = params.to_dict()
stevenvanrossem33d76892017-02-13 00:13:37 +0100184 if 'cpu_bw' in params:
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100185 cpu_period = int(dcs.get(dc_label).net.cpu_period)
stevenvanrossem33d76892017-02-13 00:13:37 +0100186 value = params.get('cpu_bw')
stevenvanrossem7953f2f2017-02-10 12:56:15 +0100187 cpu_quota = int(cpu_period * float(value))
188 #put default values back
189 if float(value) <= 0:
190 cpu_period = 100000
191 cpu_quota = -1
192 params['cpu_period'] = cpu_period
193 params['cpu_quota'] = cpu_quota
194 #d.updateCpuLimit(cpu_period=cpu_period, cpu_quota=cpu_quota)
195
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}
201
202 d.update_resources(**filtered_params)
203
204 return d
hadik3r237d3f52016-06-27 17:57:49 +0200205
hadik3r237d3f52016-06-27 17:57:49 +0200206class DatacenterList(Resource):
hadik3r237d3f52016-06-27 17:57:49 +0200207 global dcs
208
209 def get(self):
210 logging.debug("API CALL: datacenter list")
211 try:
peustermdfc14602017-01-13 08:22:45 +0100212 return [d.getStatus() for d in dcs.itervalues()], 200, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200213 except Exception as ex:
214 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +0100215 return ex.message, 500, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200216
hadik3r237d3f52016-06-27 17:57:49 +0200217
hadik3ra9dd9012016-08-09 10:51:13 +0200218class DatacenterStatus(Resource):
hadik3r237d3f52016-06-27 17:57:49 +0200219 global dcs
220
221 def get(self, dc_label):
222 logging.debug("API CALL: datacenter status")
223 try:
peustermdfc14602017-01-13 08:22:45 +0100224 return dcs.get(dc_label).getStatus(), 200, CORS_HEADER
hadik3r237d3f52016-06-27 17:57:49 +0200225 except Exception as ex:
226 logging.exception("API error.")
peustermdfc14602017-01-13 08:22:45 +0100227 return ex.message, 500, CORS_HEADER