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