1 # -*- coding: utf-8 -*-
4 # Copyright 2019 Telefonica Investigacion y Desarrollo, S.A.U.
7 # Licensed under the Apache License, Version 2.0 (the "License"); you may
8 # not use this file except in compliance with the License. You may obtain
9 # a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 # License for the specific language governing permissions and limitations
21 This is the thread for the http server North API.
22 Two thread will be launched, with normal and administrative permissions.
25 from uuid
import uuid4
26 from http
import HTTPStatus
28 __author__
= "Alfonso Tierno"
29 __date__
= "2019-10-22"
31 version_date
= "Oct 2019"
34 class SdnException(Exception):
35 def __init__(self
, message
, http_code
=HTTPStatus
.BAD_REQUEST
.value
):
36 self
.http_code
= http_code
37 Exception.__init
__(self
, message
)
42 def __init__(self
, db
, plugins
):
44 self
.plugins
= plugins
46 def start_service(self
):
47 pass # TODO py3 needed to load wims and plugins
49 def stop_service(self
):
52 def show_network(self
, uuid
):
55 def delete_network(self
, uuid
):
58 def new_network(self
, network
):
61 def get_openflow_rules(self
, network_id
=None):
63 Get openflow id from DB
64 :param network_id: Network id, if none all networks will be retrieved
65 :return: Return a list with Openflow rules per net
72 where_
= {"net_id": network_id
}
73 result
, content
= self
.db
.get_table(
74 SELECT
=("name", "net_id", "ofc_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
75 WHERE
=where_
, FROM
='of_flows')
78 raise SdnException(str(content
), -result
)
81 def edit_openflow_rules(self
, network_id
=None):
83 To make actions over the net. The action is to reinstall the openflow rules
84 network_id can be 'all'
85 :param network_id: Network id, if none all networks will be retrieved
86 :return : Number of nets updated
93 where_
= {"uuid": network_id
}
94 result
, content
= self
.db
.get_table(SELECT
=("uuid", "type"), WHERE
=where_
, FROM
='nets')
97 raise SdnException(str(content
), -result
)
100 if net
["type"] != "ptp" and net
["type"] != "data":
105 self
.net_update_ofc_thread(net
['uuid'])
106 except SdnException
as e
:
107 raise SdnException("Error updating network'{}' {}".format(net
['uuid'], e
),
108 HTTPStatus
.INTERNAL_SERVER_ERROR
.value
)
109 except Exception as e
:
110 raise SdnException("Error updating network '{}' {}".format(net
['uuid'], e
),
111 HTTPStatus
.INTERNAL_SERVER_ERROR
.value
)
115 def delete_openflow_rules(self
, ofc_id
=None):
117 To make actions over the net. The action is to delete ALL openflow rules
118 :return: return operation result
122 if 'Default' in self
.config
['ofcs_thread']:
123 r
, c
= self
.config
['ofcs_thread']['Default'].insert_task("clear-all")
125 raise SdnException("Default Openflow controller not not running", HTTPStatus
.NOT_FOUND
.value
)
127 elif ofc_id
in self
.config
['ofcs_thread']:
128 r
, c
= self
.config
['ofcs_thread'][ofc_id
].insert_task("clear-all")
132 raise SdnException(str(c
), -r
)
134 raise SdnException("Openflow controller not found with ofc_id={}".format(ofc_id
),
135 HTTPStatus
.NOT_FOUND
.value
)
138 def get_openflow_ports(self
, ofc_id
=None):
140 Obtain switch ports names of openflow controller
141 :return: Return flow ports in DB
144 if 'Default' in self
.config
['ofcs_thread']:
145 conn
= self
.config
['ofcs_thread']['Default'].OF_connector
147 raise SdnException("Default Openflow controller not not running", HTTPStatus
.NOT_FOUND
.value
)
149 elif ofc_id
in self
.config
['ofcs_thread']:
150 conn
= self
.config
['ofcs_thread'][ofc_id
].OF_connector
152 raise SdnException("Openflow controller not found with ofc_id={}".format(ofc_id
),
153 HTTPStatus
.NOT_FOUND
.value
)
156 def new_of_controller(self
, ofc_data
):
158 Create a new openflow controller into DB
159 :param ofc_data: Dict openflow controller data
160 :return: openflow controller uuid
163 "uuid": str(uuid4()),
164 "name": ofc_data
["name"],
165 "description": ofc_data
.get("description"),
166 "type": ofc_data
["type"],
167 "wim_url": ofc_data
.get("url"),
169 if not db_wim
["wim_url"]:
170 if not ofc_data
.get("ip") or not ofc_data
.get("port"):
171 raise SdnException("Provide either 'url' or both 'ip' and 'port'")
172 db_wim
["wim_url"] = "{}:{}".format(ofc_data
["ip"], ofc_data
["port"])
175 "uuid": str(uuid4()),
176 "name": ofc_data
["name"],
177 "wim_id": db_wim
["uuid"],
179 "user": ofc_data
.get("user"),
180 "password": ofc_data
.get("password"),
182 db_wim_account_config
= ofc_data
.get("config", {})
183 if ofc_data
.get("dpid"):
184 db_wim_account_config
["dpid"] = ofc_data
["dpid"]
185 if ofc_data
.get("version"):
186 db_wim_account_config
["version"] = ofc_data
["version"]
188 db_wim_account
["config"] = yaml
.safe_dump(db_wim_account_config
, default_flow_style
=True, width
=256)
192 {"wim_accounts": db_wim_account
},
194 uuid_list
= [db_wim
["uuid"], db_wim_account
["uuid"]]
195 self
.db
.new_rows(db_tables
, uuid_list
)
196 return db_wim_account
["uuid"]
198 def edit_of_controller(self
, of_id
, ofc_data
):
200 Edit an openflow controller entry from DB
204 raise SdnException("No data received during uptade OF contorller",
205 http_code
=HTTPStatus
.INTERNAL_SERVER_ERROR
.value
)
207 # get database wim_accounts
208 wim_account
= self
._get
_of
_controller
(of_id
)
210 db_wim_update
= {x
: ofc_data
[x
] for x
in ("name", "description", "type", "wim_url")}
211 db_wim_account_update
= {x
: ofc_data
[x
] for x
in ("name", "user", "password")}
212 db_wim_account_config
= ofc_data
.get("config", {})
214 if ofc_data
.get("ip") or ofc_data
.get("port"):
215 if not ofc_data
.get("ip") or not ofc_data
.get("port"):
216 raise SdnException("Provide or both 'ip' and 'port'")
217 db_wim_update
["wim_url"] = "{}:{}".format(ofc_data
["ip"], ofc_data
["port"])
219 if ofc_data
.get("dpid"):
220 db_wim_account_config
["dpid"] = ofc_data
["dpid"]
221 if ofc_data
.get("version"):
222 db_wim_account_config
["version"] = ofc_data
["version"]
224 if db_wim_account_config
:
225 db_wim_account_update
["config"] = yaml
.load(wim_account
["config"]) or {}
226 db_wim_account_update
["config"].update(db_wim_account_config
)
228 if db_wim_account_update
:
229 self
.db
.update_rows('wim_accounts', db_wim_account_update
, WHERE
={'uuid': of_id
})
231 self
.db
.update_rows('wims', db_wim_account_update
, WHERE
={'uuid': wim_account
["wim_id"]})
233 def _get_of_controller(self
, of_id
):
234 wim_accounts
= self
.db
.get_rows(FROM
='wim_accounts', WHERE
={"uuid": of_id
, "sdn": "true"})
237 raise SdnException("Cannot find sdn controller with id='{}'".format(of_id
),
238 http_code
=HTTPStatus
.NOT_FOUND
.value
)
239 elif len(wim_accounts
) > 1:
240 raise SdnException("Found more than one sdn controller with id='{}'".format(of_id
),
241 http_code
=HTTPStatus
.CONFLICT
.value
)
242 return wim_accounts
[0]
244 def delete_of_controller(self
, of_id
):
246 Delete an openflow controller from DB.
247 :param of_id: openflow controller dpid
250 wim_account
= self
._get
_of
_controller
(of_id
)
251 self
.db
.delete_row(FROM
='wim_accounts', WHERE
={"uuid": of_id
})
252 self
.db
.delete_row(FROM
='wims', WHERE
={"uuid": wim_account
["wim_id"]})
256 def _format_of_controller(wim_account
, wim
=None):
257 of_data
= {x
: wim_account
[x
] for x
in ("uuid", "name", "user")}
258 if isinstance(wim_account
["config"], str):
259 config
= yaml
.load(wim_account
["config"], Loader
=yaml
.Loader
)
260 of_data
["dpid"] = config
.get("switch_id") or config
.get("dpid")
261 of_data
["version"] = config
.get("version")
263 of_data
["url"] = wim
["wim_url"]
264 of_data
["type"] = wim
["type"]
267 def show_of_controller(self
, of_id
):
269 Show an openflow controller by dpid from DB.
270 :param db_filter: List with where query parameters
273 wim_account
= self
._get
_of
_controller
(of_id
)
274 wims
= self
.db
.get_rows(FROM
='wims', WHERE
={"uuid": wim_account
["wim_id"]})
275 return self
._format
_of
_controller
(wim_account
, wims
[0])
277 def get_of_controllers(self
, filter=None):
279 Show an openflow controllers from DB.
282 filter = filter or {}
283 filter["sdn"] = "true"
284 wim_accounts
= self
.db
.get_rows(FROM
='wim_accounts', WHERE
=filter)
285 return [self
._format
_of
_controller
(w
) for w
in wim_accounts
]
287 def set_of_port_mapping(self
, maps
, sdn_id
, switch_dpid
, vim_id
):
289 Create new port mapping entry
290 :param of_maps: List with port mapping information
291 # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
292 "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
293 :param sdn_id: ofc id
294 :param switch_dpid: switch dpid
295 :param vim_id: datacenter
298 # get wim from wim_account
299 wim_account
= self
._get
_of
_controller
(sdn_id
)
300 wim_id
= wim_account
["wim_id"]
301 db_wim_port_mappings
= []
303 _switch_dpid
= map.get("switch_id") or map.get("switch_dpid") or switch_dpid
306 'switch_dpid': _switch_dpid
,
307 "switch_port": map.get("switch_port"),
308 'datacenter_id': vim_id
,
309 "device_id": map.get("compute_node"),
310 "service_endpoint_id": _switch_dpid
+ "-" + str(uuid4())
313 new_map
["device_interface_id"] = map["pci"].lower()
315 if map.get("switch_mac"):
316 config
["switch_mac"] = map["switch_mac"]
318 new_map
["service_mapping_info"] = yaml
.safe_dump(config
, default_flow_style
=True, width
=256)
319 db_wim_port_mappings
.append(new_map
)
322 {"wim_port_mappings": db_wim_port_mappings
},
324 self
.db
.new_rows(db_tables
, [])
325 return db_wim_port_mappings
327 def clear_of_port_mapping(self
, db_filter
=None):
329 Clear port mapping filtering using db_filter dict
330 :param db_filter: Parameter to filter during remove process
333 return self
.db
.delete_row(FROM
='wim_port_mappings', WHERE
=db_filter
)
335 def get_of_port_mappings(self
, db_filter
=None):
337 Retrive port mapping from DB
341 maps
= self
.db
.get_rows(WHERE
=db_filter
, FROM
='wim_port_mappings')
343 if map.get("service_mapping_info"):
344 map["service_mapping_info"] = yaml
.load(map["service_mapping_info"], Loader
=yaml
.Loader
)
346 map["service_mapping_info"] = {}
349 def get_ports(self
, instance_wim_net_id
):
351 instance_wim_net
= self
.db
.get_rows(FROM
='instance_wim_nets', WHERE
={"uuid": instance_wim_net_id
})
352 wim_id
= instance_wim_net
[0]["wim_id"]
354 ports
= self
.db
.get_rows(FROM
='instance_interfaces', WHERE
={"instance_wim_net_id": instance_wim_net_id
})
355 maps
= self
.get_of_port_mappings(db_filter
={"wim_id": wim_id
})
357 map_
= next((x
for x
in maps
if x
.get("device_id") == port
["compute_node"] and
358 x
.get("device_interface_id") == port
["pci"]), None)
360 switch_port
= {'switch_dpid': map_
.get('switch_dpid') or map_
.get('switch_id'),
361 'switch_port': map_
.get('switch_port')}
362 if switch_port
not in switch_ports
:
363 switch_ports
.append(switch_port
)