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