Code Coverage

Cobertura Coverage Report > RO-SDN-juniper_contrail.osm_rosdn_juniper_contrail >

sdn_api.py

Trend

File Coverage summary

NameClassesLinesConditionals
sdn_api.py
100%
1/1
46%
71/154
100%
0/0

Coverage Breakdown by Class

NameLinesConditionals
sdn_api.py
46%
71/154
N/A

Source

RO-SDN-juniper_contrail/osm_rosdn_juniper_contrail/sdn_api.py
1 # Copyright 2020 ETSI
2 #
3 # All Rights Reserved.
4 #
5 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
6 #    not use this file except in compliance with the License. You may obtain
7 #    a copy of the License at
8 #
9 #         http://www.apache.org/licenses/LICENSE-2.0
10 #
11 #    Unless required by applicable law or agreed to in writing, software
12 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 #    License for the specific language governing permissions and limitations
15 #    under the License.
16
17 1 import json
18 1 import logging
19
20 1 from osm_ro_plugin.sdnconn import SdnConnectorError
21 1 from osm_rosdn_juniper_contrail.rest_lib import ContrailHttp
22 1 from osm_rosdn_juniper_contrail.rest_lib import NotFound
23
24 # from osm_rosdn_juniper_contrail.rest_lib import DuplicateFound
25 # from osm_rosdn_juniper_contrail.rest_lib import HttpException
26
27
28 1 class UnderlayApi:
29     """Class with CRUD operations for the underlay API"""
30
31 1     def __init__(self, url, config=None, user=None, password=None, logger=None):
32 1         self.logger = logger or logging.getLogger("ro.sdn.junipercontrail.sdnapi")
33 1         self.controller_url = url
34
35 1         if not url:
36 0             raise SdnConnectorError("'url' must be provided")
37
38 1         if not url.startswith("http"):
39 0             url = "http://" + url
40
41 1         if not url.endswith("/"):
42 0             url = url + "/"
43
44 1         self.url = url
45 1         self.auth_url = None
46 1         self.project = None
47 1         self.domain = None
48 1         self.asn = None
49 1         self.fabric = None
50
51 1         if config:
52 1             self.auth_url = config.get("auth_url")
53 1             self.project = config.get("project")
54 1             self.domain = config.get("domain")
55 1             self.asn = config.get("asn")
56 1             self.fabric = config.get("fabric")
57 1             self.verify = config.get("verify")
58
59         # Init http headers for all requests
60 1         self.http_header = {"Content-Type": "application/json"}
61
62 1         if user:
63 1             self.user = user
64
65 1         if password:
66 1             self.password = password
67
68 1         self.logger.debug(
69             "Config parameters for the underlay controller: auth_url: {}, project: {},"
70             " domain: {}, user: {}, password: {}".format(
71                 self.auth_url, self.project, self.domain, self.user, self.password
72             )
73         )
74
75 1         auth_dict = {}
76 1         auth_dict["auth"] = {}
77 1         auth_dict["auth"]["scope"] = {}
78 1         auth_dict["auth"]["scope"]["project"] = {}
79 1         auth_dict["auth"]["scope"]["project"]["domain"] = {}
80 1         auth_dict["auth"]["scope"]["project"]["domain"]["id"] = self.domain
81 1         auth_dict["auth"]["scope"]["project"]["name"] = self.project
82 1         auth_dict["auth"]["identity"] = {}
83 1         auth_dict["auth"]["identity"]["methods"] = ["password"]
84 1         auth_dict["auth"]["identity"]["password"] = {}
85 1         auth_dict["auth"]["identity"]["password"]["user"] = {}
86 1         auth_dict["auth"]["identity"]["password"]["user"]["name"] = self.user
87 1         auth_dict["auth"]["identity"]["password"]["user"]["password"] = self.password
88 1         auth_dict["auth"]["identity"]["password"]["user"]["domain"] = {}
89 1         auth_dict["auth"]["identity"]["password"]["user"]["domain"]["id"] = self.domain
90 1         self.auth_dict = auth_dict
91
92         # Init http lib
93 1         auth_info = {"auth_url": self.auth_url, "auth_dict": auth_dict}
94 1         self.http = ContrailHttp(auth_info, self.logger, self.verify)
95
96 1     def check_auth(self):
97 0         response = self.http.get_cmd(url=self.auth_url, headers=self.http_header)
98
99 0         return response
100
101     # Helper methods for CRUD operations
102 1     def get_all_by_type(self, controller_url, type):
103 0         endpoint = controller_url + type
104 0         response = self.http.get_cmd(url=endpoint, headers=self.http_header)
105
106 0         return response.get(type)
107
108 1     def get_by_uuid(self, type, uuid):
109 0         try:
110 0             endpoint = self.controller_url + type + "/{}".format(uuid)
111 0             response = self.http.get_cmd(url=endpoint, headers=self.http_header)
112
113 0             return response.get(type)
114 0         except NotFound:
115 0             return None
116
117 1     def delete_by_uuid(self, controller_url, type, uuid):
118 0         endpoint = controller_url + type + "/{}".format(uuid)
119 0         self.http.delete_cmd(url=endpoint, headers=self.http_header)
120
121 1     def get_uuid_from_fqname(self, type, fq_name):
122         """
123         Obtain uuid from fqname
124         Returns: If resource not found returns None
125         In case of error raises an Exception
126         """
127 0         payload = {"type": type, "fq_name": fq_name}
128
129 0         try:
130 0             endpoint = self.controller_url + "fqname-to-id"
131 0             resp = self.http.post_cmd(
132                 url=endpoint, headers=self.http_header, post_fields_dict=payload
133             )
134
135 0             return json.loads(resp).get("uuid")
136 0         except NotFound:
137 0             return None
138
139 1     def get_by_fq_name(self, type, fq_name):
140         # Obtain uuid by fqdn and then get data by uuid
141 0         uuid = self.get_uuid_from_fqname(type, fq_name)
142
143 0         if uuid:
144 0             return self.get_by_uuid(type, uuid)
145         else:
146 0             return None
147
148 1     def delete_ref(self, type, uuid, ref_type, ref_uuid, ref_fq_name):
149 0         payload = {
150             "type": type,
151             "uuid": uuid,
152             "ref-type": ref_type,
153             "ref-fq-name": ref_fq_name,
154             "operation": "DELETE",
155         }
156 0         endpoint = self.controller_url + "ref-update"
157 0         resp = self.http.post_cmd(
158             url=endpoint, headers=self.http_header, post_fields_dict=payload
159         )
160
161 0         return resp
162
163     # Aux methods to avoid code duplication of name conventions
164 1     def get_vpg_name(self, switch_id, switch_port):
165 0         return "{}_{}".format(switch_id, switch_port).replace(":", "_")
166
167 1     def get_vmi_name(self, switch_id, switch_port, vlan):
168 0         return "{}_{}-{}".format(switch_id, switch_port, vlan).replace(":", "_")
169
170     # Virtual network operations
171
172 1     def create_virtual_network(self, name, vni):
173 0         self.logger.debug("create vname, name: {}, vni: {}".format(name, vni))
174 0         routetarget = "{}:{}".format(self.asn, vni)
175 0         vnet_dict = {
176             "virtual-network": {
177                 "virtual_network_properties": {
178                     "vxlan_network_identifier": vni,
179                 },
180                 "parent_type": "project",
181                 "fq_name": [self.domain, self.project, name],
182                 "route_target_list": {"route_target": ["target:" + routetarget]},
183             }
184         }
185 0         endpoint = self.controller_url + "virtual-networks"
186 0         resp = self.http.post_cmd(
187             url=endpoint, headers=self.http_header, post_fields_dict=vnet_dict
188         )
189
190 0         if not resp:
191 0             raise SdnConnectorError("Error creating virtual network: empty response")
192
193 0         vnet_info = json.loads(resp)
194 0         self.logger.debug("created vnet, vnet_info: {}".format(vnet_info))
195
196 0         return vnet_info.get("virtual-network").get("uuid"), vnet_info.get(
197             "virtual-network"
198         )
199
200 1     def get_virtual_networks(self):
201 0         return self.get_all_by_type("virtual-networks")
202
203 1     def get_virtual_network(self, network_id):
204 0         return self.get_by_uuid("virtual-network", network_id)
205
206 1     def delete_virtual_network(self, network_id):
207 0         self.logger.debug("delete vnet uuid: {}".format(network_id))
208 0         self.delete_by_uuid(self.controller_url, "virtual-network", network_id)
209 0         self.logger.debug("deleted vnet uuid: {}".format(network_id))
210
211     # Vpg operations
212
213 1     def create_vpg(self, switch_id, switch_port):
214 0         self.logger.debug(
215             "create vpg, switch_id: {}, switch_port: {}".format(switch_id, switch_port)
216         )
217 0         vpg_name = self.get_vpg_name(switch_id, switch_port)
218 0         vpg_dict = {
219             "virtual-port-group": {
220                 "parent_type": "fabric",
221                 "fq_name": ["default-global-system-config", self.fabric, vpg_name],
222             }
223         }
224 0         endpoint = self.controller_url + "virtual-port-groups"
225 0         resp = self.http.post_cmd(
226             url=endpoint, headers=self.http_header, post_fields_dict=vpg_dict
227         )
228
229 0         if not resp:
230 0             raise SdnConnectorError("Error creating virtual port group: empty response")
231
232 0         vpg_info = json.loads(resp)
233 0         self.logger.debug("created vpg, vpg_info: {}".format(vpg_info))
234
235 0         return vpg_info.get("virtual-port-group").get("uuid"), vpg_info.get(
236             "virtual-port-group"
237         )
238
239 1     def get_vpgs(self):
240 0         return self.get_all_by_type(self.controller_url, "virtual-port-groups")
241
242 1     def get_vpg(self, vpg_id):
243 0         return self.get_by_uuid(self.controller_url, "virtual-port-group", vpg_id)
244
245 1     def get_vpg_by_name(self, vpg_name):
246 0         fq_name = ["default-global-system-config", self.fabric, vpg_name]
247
248 0         return self.get_by_fq_name("virtual-port-group", fq_name)
249
250 1     def delete_vpg(self, vpg_id):
251 0         self.logger.debug("delete vpg, uuid: {}".format(vpg_id))
252 0         self.delete_by_uuid(self.controller_url, "virtual-port-group", vpg_id)
253 0         self.logger.debug("deleted vpg, uuid: {}".format(vpg_id))
254
255 1     def create_vmi(self, switch_id, switch_port, network, vlan):
256 0         self.logger.debug(
257             "create vmi, switch_id: {}, switch_port: {}, network: {}, vlan: {}".format(
258                 switch_id, switch_port, network, vlan
259             )
260         )
261 0         vmi_name = self.get_vmi_name(switch_id, switch_port, vlan)
262 0         vpg_name = self.get_vpg_name(switch_id, switch_port)
263 0         profile_dict = {
264             "local_link_information": [
265                 {
266                     "port_id": switch_port.replace(":", "_"),
267                     "switch_id": switch_port.replace(":", "_"),
268                     "switch_info": switch_id,
269                     "fabric": self.fabric,
270                 }
271             ]
272         }
273 0         vmi_dict = {
274             "virtual-machine-interface": {
275                 "parent_type": "project",
276                 "fq_name": [self.domain, self.project, vmi_name],
277                 "virtual_network_refs": [{"to": [self.domain, self.project, network]}],
278                 "virtual_machine_interface_properties": {
279                     "sub_interface_vlan_tag": vlan
280                 },
281                 "virtual_machine_interface_bindings": {
282                     "key_value_pair": [
283                         {"key": "vnic_type", "value": "baremetal"},
284                         {"key": "vif_type", "value": "vrouter"},
285                         {"key": "vpg", "value": vpg_name},
286                         {"key": "profile", "value": json.dumps(profile_dict)},
287                     ]
288                 },
289             }
290         }
291 0         endpoint = self.controller_url + "virtual-machine-interfaces"
292 0         self.logger.debug("vmi_dict: {}".format(vmi_dict))
293 0         resp = self.http.post_cmd(
294             url=endpoint,
295             headers=self.http_header,
296             post_fields_dict=vmi_dict,
297         )
298
299 0         if not resp:
300 0             raise SdnConnectorError("Error creating vmi: empty response")
301
302 0         vmi_info = json.loads(resp)
303 0         self.logger.debug("created vmi, info: {}".format(vmi_info))
304
305 0         return vmi_info.get("virtual-machine-interface").get("uuid"), vmi_info.get(
306             "virtual-machine-interface"
307         )
308
309 1     def get_vmi(self, vmi_uuid):
310 0         return self.get_by_uuid(
311             self.controller_url, "virtual-machine-interface", vmi_uuid
312         )
313
314 1     def delete_vmi(self, uuid):
315 0         self.logger.debug("delete vmi uuid: {}".format(uuid))
316 0         self.delete_by_uuid(self.controller_url, "virtual-machine-interface", uuid)
317 0         self.logger.debug("deleted vmi: {}".format(uuid))
318
319 1     def unref_vmi_vpg(self, vpg_id, vmi_id, vmi_fq_name):
320 0         self.delete_ref(
321             "virtual-port-group",
322             vpg_id,
323             "virtual-machine-interface",
324             vmi_id,
325             vmi_fq_name,
326         )