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
24 # from osm_rosdn_juniper_contrail.rest_lib import DuplicateFound
25 # from osm_rosdn_juniper_contrail.rest_lib import HttpException
29 """Class with CRUD operations for the underlay API"""
31 def __init__(self
, url
, config
=None, user
=None, password
=None, logger
=None):
32 self
.logger
= logger
or logging
.getLogger("ro.sdn.junipercontrail.sdnapi")
33 self
.controller_url
= url
36 raise SdnConnectorError("'url' must be provided")
38 if not url
.startswith("http"):
41 if not url
.endswith("/"):
52 self
.auth_url
= config
.get("auth_url")
53 self
.project
= config
.get("project")
54 self
.domain
= config
.get("domain")
55 self
.asn
= config
.get("asn")
56 self
.fabric
= config
.get("fabric")
57 self
.verify
= config
.get("verify")
59 # Init http headers for all requests
60 self
.http_header
= {"Content-Type": "application/json"}
66 self
.password
= password
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
76 auth_dict
["auth"] = {}
77 auth_dict
["auth"]["scope"] = {}
78 auth_dict
["auth"]["scope"]["project"] = {}
79 auth_dict
["auth"]["scope"]["project"]["domain"] = {}
80 auth_dict
["auth"]["scope"]["project"]["domain"]["id"] = self
.domain
81 auth_dict
["auth"]["scope"]["project"]["name"] = self
.project
82 auth_dict
["auth"]["identity"] = {}
83 auth_dict
["auth"]["identity"]["methods"] = ["password"]
84 auth_dict
["auth"]["identity"]["password"] = {}
85 auth_dict
["auth"]["identity"]["password"]["user"] = {}
86 auth_dict
["auth"]["identity"]["password"]["user"]["name"] = self
.user
87 auth_dict
["auth"]["identity"]["password"]["user"]["password"] = self
.password
88 auth_dict
["auth"]["identity"]["password"]["user"]["domain"] = {}
89 auth_dict
["auth"]["identity"]["password"]["user"]["domain"]["id"] = self
.domain
90 self
.auth_dict
= auth_dict
93 auth_info
= {"auth_url": self
.auth_url
, "auth_dict": auth_dict
}
94 self
.http
= ContrailHttp(auth_info
, self
.logger
, self
.verify
)
97 response
= self
.http
.get_cmd(url
=self
.auth_url
, headers
=self
.http_header
)
101 # Helper methods for CRUD operations
102 def get_all_by_type(self
, controller_url
, type):
103 endpoint
= controller_url
+ type
104 response
= self
.http
.get_cmd(url
=endpoint
, headers
=self
.http_header
)
106 return response
.get(type)
108 def get_by_uuid(self
, type, uuid
):
110 endpoint
= self
.controller_url
+ type + "/{}".format(uuid
)
111 response
= self
.http
.get_cmd(url
=endpoint
, headers
=self
.http_header
)
113 return response
.get(type)
117 def delete_by_uuid(self
, controller_url
, type, uuid
):
118 endpoint
= controller_url
+ type + "/{}".format(uuid
)
119 self
.http
.delete_cmd(url
=endpoint
, headers
=self
.http_header
)
121 def get_uuid_from_fqname(self
, type, fq_name
):
123 Obtain uuid from fqname
124 Returns: If resource not found returns None
125 In case of error raises an Exception
127 payload
= {"type": type, "fq_name": fq_name
}
130 endpoint
= self
.controller_url
+ "fqname-to-id"
131 resp
= self
.http
.post_cmd(
132 url
=endpoint
, headers
=self
.http_header
, post_fields_dict
=payload
135 return json
.loads(resp
).get("uuid")
139 def get_by_fq_name(self
, type, fq_name
):
140 # Obtain uuid by fqdn and then get data by uuid
141 uuid
= self
.get_uuid_from_fqname(type, fq_name
)
144 return self
.get_by_uuid(type, uuid
)
148 def delete_ref(self
, type, uuid
, ref_type
, ref_uuid
, ref_fq_name
):
152 "ref-type": ref_type
,
153 "ref-fq-name": ref_fq_name
,
154 "operation": "DELETE",
156 endpoint
= self
.controller_url
+ "ref-update"
157 resp
= self
.http
.post_cmd(
158 url
=endpoint
, headers
=self
.http_header
, post_fields_dict
=payload
163 # Aux methods to avoid code duplication of name conventions
164 def get_vpg_name(self
, switch_id
, switch_port
):
165 return "{}_{}".format(switch_id
, switch_port
).replace(":", "_")
167 def get_vmi_name(self
, switch_id
, switch_port
, vlan
):
168 return "{}_{}-{}".format(switch_id
, switch_port
, vlan
).replace(":", "_")
170 # Virtual network operations
172 def create_virtual_network(self
, name
, vni
):
173 self
.logger
.debug("create vname, name: {}, vni: {}".format(name
, vni
))
174 routetarget
= "{}:{}".format(self
.asn
, vni
)
177 "virtual_network_properties": {
178 "vxlan_network_identifier": vni
,
180 "parent_type": "project",
181 "fq_name": [self
.domain
, self
.project
, name
],
182 "route_target_list": {"route_target": ["target:" + routetarget
]},
185 endpoint
= self
.controller_url
+ "virtual-networks"
186 resp
= self
.http
.post_cmd(
187 url
=endpoint
, headers
=self
.http_header
, post_fields_dict
=vnet_dict
191 raise SdnConnectorError("Error creating virtual network: empty response")
193 vnet_info
= json
.loads(resp
)
194 self
.logger
.debug("created vnet, vnet_info: {}".format(vnet_info
))
196 return vnet_info
.get("virtual-network").get("uuid"), vnet_info
.get(
200 def get_virtual_networks(self
):
201 return self
.get_all_by_type("virtual-networks")
203 def get_virtual_network(self
, network_id
):
204 return self
.get_by_uuid("virtual-network", network_id
)
206 def delete_virtual_network(self
, network_id
):
207 self
.logger
.debug("delete vnet uuid: {}".format(network_id
))
208 self
.delete_by_uuid(self
.controller_url
, "virtual-network", network_id
)
209 self
.logger
.debug("deleted vnet uuid: {}".format(network_id
))
213 def create_vpg(self
, switch_id
, switch_port
):
215 "create vpg, switch_id: {}, switch_port: {}".format(switch_id
, switch_port
)
217 vpg_name
= self
.get_vpg_name(switch_id
, switch_port
)
219 "virtual-port-group": {
220 "parent_type": "fabric",
221 "fq_name": ["default-global-system-config", self
.fabric
, vpg_name
],
224 endpoint
= self
.controller_url
+ "virtual-port-groups"
225 resp
= self
.http
.post_cmd(
226 url
=endpoint
, headers
=self
.http_header
, post_fields_dict
=vpg_dict
230 raise SdnConnectorError("Error creating virtual port group: empty response")
232 vpg_info
= json
.loads(resp
)
233 self
.logger
.debug("created vpg, vpg_info: {}".format(vpg_info
))
235 return vpg_info
.get("virtual-port-group").get("uuid"), vpg_info
.get(
240 return self
.get_all_by_type(self
.controller_url
, "virtual-port-groups")
242 def get_vpg(self
, vpg_id
):
243 return self
.get_by_uuid(self
.controller_url
, "virtual-port-group", vpg_id
)
245 def get_vpg_by_name(self
, vpg_name
):
246 fq_name
= ["default-global-system-config", self
.fabric
, vpg_name
]
248 return self
.get_by_fq_name("virtual-port-group", fq_name
)
250 def delete_vpg(self
, vpg_id
):
251 self
.logger
.debug("delete vpg, uuid: {}".format(vpg_id
))
252 self
.delete_by_uuid(self
.controller_url
, "virtual-port-group", vpg_id
)
253 self
.logger
.debug("deleted vpg, uuid: {}".format(vpg_id
))
255 def create_vmi(self
, switch_id
, switch_port
, network
, vlan
):
257 "create vmi, switch_id: {}, switch_port: {}, network: {}, vlan: {}".format(
258 switch_id
, switch_port
, network
, vlan
261 vmi_name
= self
.get_vmi_name(switch_id
, switch_port
, vlan
)
262 vpg_name
= self
.get_vpg_name(switch_id
, switch_port
)
264 "local_link_information": [
266 "port_id": switch_port
.replace(":", "_"),
267 "switch_id": switch_port
.replace(":", "_"),
268 "switch_info": switch_id
,
269 "fabric": self
.fabric
,
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
281 "virtual_machine_interface_bindings": {
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
)},
291 endpoint
= self
.controller_url
+ "virtual-machine-interfaces"
292 self
.logger
.debug("vmi_dict: {}".format(vmi_dict
))
293 resp
= self
.http
.post_cmd(
295 headers
=self
.http_header
,
296 post_fields_dict
=vmi_dict
,
300 raise SdnConnectorError("Error creating vmi: empty response")
302 vmi_info
= json
.loads(resp
)
303 self
.logger
.debug("created vmi, info: {}".format(vmi_info
))
305 return vmi_info
.get("virtual-machine-interface").get("uuid"), vmi_info
.get(
306 "virtual-machine-interface"
309 def get_vmi(self
, vmi_uuid
):
310 return self
.get_by_uuid(
311 self
.controller_url
, "virtual-machine-interface", vmi_uuid
314 def delete_vmi(self
, uuid
):
315 self
.logger
.debug("delete vmi uuid: {}".format(uuid
))
316 self
.delete_by_uuid(self
.controller_url
, "virtual-machine-interface", uuid
)
317 self
.logger
.debug("deleted vmi: {}".format(uuid
))
319 def unref_vmi_vpg(self
, vpg_id
, vmi_id
, vmi_fq_name
):
321 "virtual-port-group",
323 "virtual-machine-interface",