4 # Copyright 2017 RIFT.IO Inc
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
24 class L2PortChainDriver(object):
26 Driver for openstack neutron neutron-client v2
28 PORT_PAIRS_URL
='/sfc/port_pairs'
29 PORT_PAIR_GROUPS_URL
='/sfc/port_pair_groups'
30 PORT_CHAINS_URL
='/sfc/port_chains'
31 FLOW_CLASSIFIERS_URL
='/sfc/flow_classifiers'
33 def __init__(self
, sess_handle
, neutron_base_url
, logger
= None):
35 Constructor for L2PortChainDriver class
37 sess_handle (instance of class SessionDriver)
38 neutron_base_url Neutron service endpoint
39 logger (instance of logging.Logger)
42 self
.log
= logging
.getLogger('rwcal.openstack.portchain')
43 self
.log
.setLevel(logging
.DEBUG
)
47 self
._sess
= sess_handle
48 self
._neutron
_base
_url
= neutron_base_url
51 def neutron_base_url(self
):
52 return self
._neutron
_base
_url
56 return self
._sess
.project_id
60 return self
._sess
.auth_token
62 def rest_api_handler(self
,url
,method
,payload
=None,refresh_token
=True):
65 result
=requests
.get(self
.neutron_base_url
+url
,
66 headers
={"X-Auth-Token":self
.auth_token
,
67 "Content-Type": "application/json" })
68 elif method
== 'POST':
69 self
.log
.debug("POST request being sent for url %s has payload %s",
70 self
.neutron_base_url
+url
,payload
)
72 result
=requests
.post(self
.neutron_base_url
+url
,
73 headers
={"X-Auth-Token":self
.auth_token
,
74 "Content-Type": "application/json"},
77 result
=requests
.put(self
.neutron_base_url
+url
,
78 headers
={"X-Auth-Token":self
.auth_token
,
79 "Content-Type": "application/json"},
81 elif method
== 'DELETE':
82 result
=requests
.delete(self
.neutron_base_url
+url
,
83 headers
={"X-Auth-Token": self
.auth_token
,
84 "Content-Type": "application/json"})
86 raise("Invalid method name %s",method
)
88 result
.raise_for_status()
90 except requests
.exceptions
.HTTPError
as e
:
91 if result
.status_code
== 401 and refresh_token
:
92 self
._sess
.invalidate_auth_token()
93 result
= self
.rest_api_handler(url
,method
,payload
=payload
,refresh_token
=False)
100 def create_port_pair(self
,name
,ingress_port
,egress_port
):
106 port_pair_dict
["name"] = name
107 port_pair_dict
['tenant_id'] = self
.tenant_id
108 port_pair_dict
['ingress'] = ingress_port
109 port_pair_dict
['egress'] = egress_port
110 port_pair
["port_pair"] = port_pair_dict
111 port_pair_json
= json
.dumps(port_pair
)
114 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIRS_URL
, 'POST', port_pair_json
)
115 result
.raise_for_status()
116 except requests
.exceptions
.HTTPError
as e
:
117 if (result
.status_code
== 400 and 'NeutronError' in result
.json()
118 and result
.json()['NeutronError']['type'] == 'PortPairIngressEgressInUse'):
119 self
.log
.info("Port pair with same ingress and egress port already exists")
120 result
= self
.get_port_pair_list()
121 port_pair_list
= result
.json()['port_pairs']
122 port_pair_ids
= [ pp
['id'] for pp
in port_pair_list
if pp
['ingress'] == ingress_port
and pp
['egress'] == egress_port
]
123 return port_pair_ids
[0]
125 self
.log
.exception(e
)
128 self
.log
.debug("Port Pair response received is status code: %s, response: %s",
129 result
.status_code
, result
.json())
130 return result
.json()['port_pair']['id']
132 def delete_port_pair(self
,port_pair_id
):
134 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIRS_URL
+'/{}'.format(port_pair_id
), 'DELETE')
135 result
.raise_for_status()
136 except requests
.exceptions
.HTTPError
as e
:
137 if (result
.status_code
== 409 and 'NeutronError' in result
.json()
138 and result
.json()['NeutronError']['type'] == 'PortPairInUse'):
139 self
.log
.info("Port pair is in use")
141 self
.log
.exception(e
)
143 self
.log
.debug("Delete Port Pair response received is status code: %s", result
.status_code
)
145 def get_port_pair(self
,port_pair_id
):
146 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIRS_URL
+'/{}'.format(port_pair_id
), 'GET')
147 result
.raise_for_status()
148 self
.log
.debug("Get Port Pair response received is status code: %s, response: %s",
153 def get_port_pair_list(self
):
154 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIRS_URL
, 'GET')
155 result
.raise_for_status()
156 self
.log
.debug("Get Port Pair list response received is status code: %s, response: %s",
161 def create_port_pair_group(self
,name
,port_pairs
):
163 Create port pair group
165 port_pair_group_dict
= {}
166 port_pair_group_dict
["name"] = name
167 port_pair_group_dict
['tenant_id'] = self
.tenant_id
168 port_pair_group_dict
['port_pairs'] = list()
169 port_pair_group_dict
['port_pairs'].extend(port_pairs
)
171 port_pair_group
["port_pair_group"] = port_pair_group_dict
172 port_pair_group_json
= json
.dumps(port_pair_group
)
175 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIR_GROUPS_URL
, 'POST', port_pair_group_json
)
176 result
.raise_for_status()
177 except requests
.exceptions
.HTTPError
as e
:
178 if (result
.status_code
== 409 and 'NeutronError' in result
.json()
179 and result
.json()['NeutronError']['type'] == 'PortPairInUse'):
180 self
.log
.info("Port pair group with same port pair already exists")
181 result
= self
.get_port_pair_group_list()
182 port_pair_group_list
= result
.json()['port_pair_groups']
183 port_pair_group_ids
= [ppg
['id'] for ppg
in port_pair_group_list
184 if ppg
['port_pairs'] == port_pairs
]
185 return port_pair_group_ids
[0]
187 self
.log
.exception(e
)
190 self
.log
.debug("Create Port Pair group response received is status code: %s, response: %s",
193 return result
.json()['port_pair_group']['id']
195 def delete_port_pair_group(self
,port_pair_group_id
):
197 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIR_GROUPS_URL
+'/{}'.format(port_pair_group_id
), 'DELETE')
198 result
.raise_for_status()
199 except requests
.exceptions
.HTTPError
as e
:
200 if (result
.status_code
== 409 and 'NeutronError' in result
.json()
201 and result
.json()['NeutronError']['type'] == 'PortPairGroupInUse'):
202 self
.log
.info("Port pair group is in use")
204 self
.log
.exception(e
)
206 self
.log
.debug("Delete Port Pair group response received is status code: %s",
209 def get_port_pair_group(self
,port_pair_group_id
):
210 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIR_GROUPS_URL
+'/{}'.format(port_pair_group_id
), 'GET')
211 result
.raise_for_status()
212 self
.log
.debug("Get Port Pair group response received is status code: %s, response: %s",
217 def get_port_pair_group_list(self
):
218 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIR_GROUPS_URL
, 'GET')
219 result
.raise_for_status()
220 self
.log
.debug("Get Port Pair group list response received is status code: %s, response: %s",
225 def create_port_chain(self
,name
,port_pair_groups
,flow_classifiers
=None):
230 port_chain_dict
["name"]=name
231 port_chain_dict
['tenant_id'] = self
.tenant_id
232 port_chain_dict
['port_pair_groups'] = list()
233 port_chain_dict
['port_pair_groups'].extend(port_pair_groups
)
235 port_chain_dict
['flow_classifiers'] = list()
236 port_chain_dict
['flow_classifiers'].extend(flow_classifiers
)
238 port_chain
["port_chain"] = port_chain_dict
239 port_chain_json
= json
.dumps(port_chain
)
242 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_CHAINS_URL
, 'POST', port_chain_json
)
243 result
.raise_for_status()
244 except requests
.exceptions
.HTTPError
as e
:
245 if (result
.status_code
== 409 and 'NeutronError' in result
.json()
246 and result
.json()['NeutronError']['type'] == 'InvalidPortPairGroups'):
247 self
.log
.info("Port chain with same port pair group already exists")
248 result
= self
.get_port_chain_list()
249 port_chain_list
= result
.json()['port_chains']
250 port_chain_ids
= [ pc
['id'] for pc
in port_chain_list
251 if pc
['port_pair_groups'] == port_pair_groups
]
252 return port_chain_ids
[0]
254 self
.log
.exception(e
)
257 self
.log
.debug("Create Port chain response received is status code: %s, response: %s",
261 return result
.json()['port_chain']['id']
263 def delete_port_chain(self
,port_chain_id
):
264 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_CHAINS_URL
+'/{}'.format(port_chain_id
), 'DELETE')
265 result
.raise_for_status()
266 self
.log
.debug("Delete Port chain response received is status code: %s", result
.status_code
)
268 def get_port_chain(self
,port_chain_id
):
269 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_CHAINS_URL
+'/{}'.format(port_chain_id
), 'GET')
270 result
.raise_for_status()
271 self
.log
.debug("Get Port Chain response received is status code: %s, response: %s",
276 def get_port_chain_list(self
):
277 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_CHAINS_URL
, 'GET')
278 result
.raise_for_status()
279 self
.log
.debug("Get Port Chain list response received is status code: %s, response: %s",
284 def update_port_chain(self
,port_chain_id
,port_pair_groups
=None,flow_classifiers
=None):
287 port_chain_dict
['flow_classifiers'] = list()
288 port_chain_dict
['flow_classifiers'].extend(flow_classifiers
)
290 port_chain_dict
['port_pair_groups'] = list()
291 port_chain_dict
['port_pair_groups'].extend(port_pair_groups
)
293 port_chain
["port_chain"] = port_chain_dict
294 port_chain_json
= json
.dumps(port_chain
)
296 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_CHAINS_URL
+'/{}'.format(port_chain_id
), 'PUT', port_chain_json
)
297 result
.raise_for_status()
298 self
.log
.debug("Update Port chain response received is status code: %s, response: %s",
301 return result
.json()['port_chain']['id']
303 def create_flow_classifier(self
,name
,classifier_dict
):
305 Create flow classifier
307 classifier_fields
= [ 'ethertype',
309 'source_port_range_min',
310 'source_port_range_max',
311 'destination_port_range_min',
312 'destination_port_range_max',
314 'destination_ip_prefix',
315 'logical_source_port' ]
317 flow_classifier_dict
= {}
318 flow_classifier_dict
= {k
: v
for k
, v
in classifier_dict
.items()
319 if k
in classifier_fields
}
320 flow_classifier_dict
["name"]= name
321 flow_classifier_dict
['tenant_id']= self
.tenant_id
323 #flow_classifier_dict['ethertype']= 'IPv4'
324 #flow_classifier_dict['protocol']= 'TCP'
325 #flow_classifier_dict['source_port_range_min']= 80
326 #flow_classifier_dict['source_port_range_max']= 80
327 #flow_classifier_dict['destination_port_range_min']= 80
328 #flow_classifier_dict['destination_port_range_max']= 80
329 #flow_classifier_dict['source_ip_prefix']= '11.0.6.5/32'
330 #flow_classifier_dict['destination_ip_prefix']= '11.0.6.6/32'
331 #flow_classifier_dict['logical_source_port']= source_neutron_port
332 #flow_classifier_dict['logical_destination_port']= ''
334 flow_classifier
["flow_classifier"] = flow_classifier_dict
335 flow_classifier_json
= json
.dumps(flow_classifier
)
337 result
= self
.rest_api_handler(L2PortChainDriver
.FLOW_CLASSIFIERS_URL
, 'POST', flow_classifier_json
)
338 result
.raise_for_status()
339 self
.log
.debug("Create flow classifier response received is status code: %s, response: %s",
342 return result
.json()['flow_classifier']['id']
344 def delete_flow_classifier(self
,flow_classifier_id
):
345 result
= self
.rest_api_handler(L2PortChainDriver
.FLOW_CLASSIFIERS_URL
+'/{}'.format(flow_classifier_id
), 'DELETE')
346 result
.raise_for_status()
347 self
.log
.debug("Delete flow classifier response received is status code: %s",
350 def get_flow_classifier(self
,flow_classifier_id
):
351 result
= self
.rest_api_handler(L2PortChainDriver
.FLOW_CLASSIFIERS_URL
+'/{}'.format(flow_classifier_id
), 'GET')
352 result
.raise_for_status()
353 self
.log
.debug("Get flow classifier response received is status code: %s, response: %s",