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
9 # http://www.apache.org/licenses/LICENSE-2.0
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
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
28 """ Class with CRUD operations for the underlay API """
30 def __init__(self
, url
, config
=None, user
=None, password
=None, logger
=None):
32 self
.logger
= logger
or logging
.getLogger("openmano.sdnconn.junipercontrail.sdnapi")
33 self
.controller_url
= url
36 raise SdnConnectorError("'url' must be provided")
37 if not url
.startswith("http"):
39 if not url
.endswith("/"):
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")
55 # Init http headers for all requests
56 self
.http_header
= {'Content-Type': 'application/json'}
61 self
.password
= password
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
))
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
85 auth_info
= {"auth_url": self
.auth_url
, "auth_dict": auth_dict
}
86 self
.http
= ContrailHttp(auth_info
, self
.logger
)
89 response
= self
.http
.get_cmd(url
=self
.auth_url
, headers
=self
.http_header
)
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)
98 def get_by_uuid(self
, type, uuid
):
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)
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
)
110 def get_uuid_from_fqname(self
, type, fq_name
):
112 Obtain uuid from fqname
113 Returns: If resource not found returns None
114 In case of error raises an Exception
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")
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
)
133 return self
.get_by_uuid(type, uuid
)
137 def delete_ref(self
, type, uuid
, ref_type
, ref_uuid
, ref_fq_name
):
141 "ref-type": ref_type
,
142 "ref-fq-name": ref_fq_name
,
143 "operation": "DELETE"
145 endpoint
= self
.controller_url
+ "ref-update"
146 resp
= self
.http
.post_cmd(url
=endpoint
, headers
=self
.http_header
, post_fields_dict
=payload
)
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
).replace(":", "_")
153 def get_vmi_name(self
, switch_id
, switch_port
, vlan
):
154 return "{}_{}-{}".format(switch_id
, switch_port
, vlan
).replace(":", "_")
156 # Virtual network operations
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
)
163 "virtual_network_properties": {
164 "vxlan_network_identifier": vni
,
166 "parent_type": "project",
172 "route_target_list": {
174 "target:" + routetarget
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
)
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")
189 def get_virtual_networks(self
):
190 return self
.get_all_by_type('virtual-networks')
192 def get_virtual_network(self
, network_id
):
193 return self
.get_by_uuid('virtual-network', network_id
)
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
))
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
)
206 "virtual-port-group": {
207 "parent_type": "fabric",
209 "default-global-system-config",
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
)
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")
226 return self
.get_all_by_type(self
.controller_url
, 'virtual-port-groups')
228 def get_vpg(self
, vpg_id
):
229 return self
.get_by_uuid(self
.controller_url
, "virtual-port-group", vpg_id
)
231 def get_vpg_by_name(self
, vpg_name
):
232 fq_name
= ["default-global-system-config",
236 return self
.get_by_fq_name("virtual-port-group", fq_name
)
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
))
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
)
249 "local_link_information": [
251 "port_id": switch_port
.replace(":", "_"),
252 "switch_id": switch_port
.replace(":", "_"),
253 "switch_info": switch_id
,
254 "fabric": self
.fabric
260 "virtual-machine-interface": {
261 "parent_type": "project",
267 "virtual_network_refs": [
276 "virtual_machine_interface_properties": {
277 "sub_interface_vlan_tag": vlan
279 "virtual_machine_interface_bindings": {
295 "value": json
.dumps(profile_dict
)
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
)
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")
312 def get_vmi(self
, vmi_uuid
):
313 return self
.get_by_uuid(self
.controller_url
, 'virtual-machine-interface', vmi_uuid
)
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
))
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
)