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_drv
, logger
= None):
35 Constructor for L2PortChainDriver class
37 sess_handle (instance of class SessionDriver)
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_drv
= neutron_drv
49 self
._neutron
_base
_url
= neutron_drv
.neutron_endpoint
52 def neutron_base_url(self
):
53 return self
._neutron
_base
_url
57 return self
._sess
.project_id
61 return self
._sess
.auth_token
63 def rest_api_handler(self
,url
,method
,payload
=None,refresh_token
=True):
66 result
=requests
.get(self
.neutron_base_url
+url
,
67 headers
={"X-Auth-Token":self
.auth_token
,
68 "Content-Type": "application/json" })
69 elif method
== 'POST':
70 self
.log
.debug("POST request being sent for url %s has payload %s",
71 self
.neutron_base_url
+url
,payload
)
73 result
=requests
.post(self
.neutron_base_url
+url
,
74 headers
={"X-Auth-Token":self
.auth_token
,
75 "Content-Type": "application/json"},
78 result
=requests
.put(self
.neutron_base_url
+url
,
79 headers
={"X-Auth-Token":self
.auth_token
,
80 "Content-Type": "application/json"},
82 elif method
== 'DELETE':
83 result
=requests
.delete(self
.neutron_base_url
+url
,
84 headers
={"X-Auth-Token": self
.auth_token
,
85 "Content-Type": "application/json"})
87 raise("Invalid method name %s",method
)
89 result
.raise_for_status()
91 except requests
.exceptions
.HTTPError
as e
:
92 if result
.status_code
== 401 and refresh_token
:
93 self
._sess
.invalidate_auth_token()
94 result
= self
.rest_api_handler(url
,method
,payload
=payload
,refresh_token
=False)
101 def create_port_pair(self
,name
,ingress_port
,egress_port
):
107 port_pair_dict
["name"] = name
108 port_pair_dict
['tenant_id'] = self
.tenant_id
109 port_pair_dict
['ingress'] = ingress_port
110 port_pair_dict
['egress'] = egress_port
111 port_pair
["port_pair"] = port_pair_dict
112 port_pair_json
= json
.dumps(port_pair
)
115 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIRS_URL
, 'POST', port_pair_json
)
116 result
.raise_for_status()
117 except requests
.exceptions
.HTTPError
as e
:
118 if (result
.status_code
== 400 and 'NeutronError' in result
.json()
119 and result
.json()['NeutronError']['type'] == 'PortPairIngressEgressInUse'):
120 self
.log
.info("Port pair with same ingress and egress port already exists")
121 result
= self
.get_port_pair_list()
122 port_pair_list
= result
.json()['port_pairs']
123 port_pair_ids
= [ pp
['id'] for pp
in port_pair_list
if pp
['ingress'] == ingress_port
and pp
['egress'] == egress_port
]
124 return port_pair_ids
[0]
126 self
.log
.exception(e
)
129 self
.log
.debug("Port Pair response received is status code: %s, response: %s",
130 result
.status_code
, result
.json())
131 return result
.json()['port_pair']['id']
133 def delete_port_pair(self
,port_pair_id
):
135 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIRS_URL
+'/{}'.format(port_pair_id
), 'DELETE')
136 result
.raise_for_status()
137 except requests
.exceptions
.HTTPError
as e
:
138 if (result
.status_code
== 409 and 'NeutronError' in result
.json()
139 and result
.json()['NeutronError']['type'] == 'PortPairInUse'):
140 self
.log
.info("Port pair is in use")
142 self
.log
.exception(e
)
144 self
.log
.debug("Delete Port Pair response received is status code: %s", result
.status_code
)
146 def get_port_pair(self
,port_pair_id
):
147 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIRS_URL
+'/{}'.format(port_pair_id
), 'GET')
148 result
.raise_for_status()
149 self
.log
.debug("Get Port Pair response received is status code: %s, response: %s",
154 def get_port_pair_list(self
):
155 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIRS_URL
, 'GET')
156 result
.raise_for_status()
157 self
.log
.debug("Get Port Pair list response received is status code: %s, response: %s",
162 def create_port_pair_group(self
,name
,port_pairs
):
164 Create port pair group
166 port_pair_group_dict
= {}
167 port_pair_group_dict
["name"] = name
168 port_pair_group_dict
['tenant_id'] = self
.tenant_id
169 port_pair_group_dict
['port_pairs'] = list()
170 port_pair_group_dict
['port_pairs'].extend(port_pairs
)
172 port_pair_group
["port_pair_group"] = port_pair_group_dict
173 port_pair_group_json
= json
.dumps(port_pair_group
)
176 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIR_GROUPS_URL
, 'POST', port_pair_group_json
)
177 result
.raise_for_status()
178 except requests
.exceptions
.HTTPError
as e
:
179 if (result
.status_code
== 409 and 'NeutronError' in result
.json()
180 and result
.json()['NeutronError']['type'] == 'PortPairInUse'):
181 self
.log
.info("Port pair group with same port pair already exists")
182 result
= self
.get_port_pair_group_list()
183 port_pair_group_list
= result
.json()['port_pair_groups']
184 port_pair_group_ids
= [ppg
['id'] for ppg
in port_pair_group_list
185 if ppg
['port_pairs'] == port_pairs
]
186 return port_pair_group_ids
[0]
188 self
.log
.exception(e
)
191 self
.log
.debug("Create Port Pair group response received is status code: %s, response: %s",
194 return result
.json()['port_pair_group']['id']
196 def delete_port_pair_group(self
,port_pair_group_id
):
198 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIR_GROUPS_URL
+'/{}'.format(port_pair_group_id
), 'DELETE')
199 result
.raise_for_status()
200 except requests
.exceptions
.HTTPError
as e
:
201 if (result
.status_code
== 409 and 'NeutronError' in result
.json()
202 and result
.json()['NeutronError']['type'] == 'PortPairGroupInUse'):
203 self
.log
.info("Port pair group is in use")
205 self
.log
.exception(e
)
207 self
.log
.debug("Delete Port Pair group response received is status code: %s",
210 def get_port_pair_group(self
,port_pair_group_id
):
211 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIR_GROUPS_URL
+'/{}'.format(port_pair_group_id
), 'GET')
212 result
.raise_for_status()
213 self
.log
.debug("Get Port Pair group response received is status code: %s, response: %s",
218 def get_port_pair_group_list(self
):
219 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_PAIR_GROUPS_URL
, 'GET')
220 result
.raise_for_status()
221 self
.log
.debug("Get Port Pair group list response received is status code: %s, response: %s",
226 def create_port_chain(self
,name
,port_pair_groups
,flow_classifiers
=None):
231 port_chain_dict
["name"]=name
232 port_chain_dict
['tenant_id'] = self
.tenant_id
233 port_chain_dict
['port_pair_groups'] = list()
234 port_chain_dict
['port_pair_groups'].extend(port_pair_groups
)
236 port_chain_dict
['flow_classifiers'] = list()
237 port_chain_dict
['flow_classifiers'].extend(flow_classifiers
)
239 port_chain
["port_chain"] = port_chain_dict
240 port_chain_json
= json
.dumps(port_chain
)
243 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_CHAINS_URL
, 'POST', port_chain_json
)
244 result
.raise_for_status()
245 except requests
.exceptions
.HTTPError
as e
:
246 if (result
.status_code
== 409 and 'NeutronError' in result
.json()
247 and result
.json()['NeutronError']['type'] == 'InvalidPortPairGroups'):
248 self
.log
.info("Port chain with same port pair group already exists")
249 result
= self
.get_port_chain_list()
250 port_chain_list
= result
.json()['port_chains']
251 port_chain_ids
= [ pc
['id'] for pc
in port_chain_list
252 if pc
['port_pair_groups'] == port_pair_groups
]
253 return port_chain_ids
[0]
255 self
.log
.exception(e
)
258 self
.log
.debug("Create Port chain response received is status code: %s, response: %s",
262 return result
.json()['port_chain']['id']
264 def delete_port_chain(self
,port_chain_id
):
265 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_CHAINS_URL
+'/{}'.format(port_chain_id
), 'DELETE')
266 result
.raise_for_status()
267 self
.log
.debug("Delete Port chain response received is status code: %s", result
.status_code
)
269 def get_port_chain(self
,port_chain_id
):
270 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_CHAINS_URL
+'/{}'.format(port_chain_id
), 'GET')
271 result
.raise_for_status()
272 self
.log
.debug("Get Port Chain response received is status code: %s, response: %s",
277 def get_port_chain_list(self
):
278 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_CHAINS_URL
, 'GET')
279 result
.raise_for_status()
280 self
.log
.debug("Get Port Chain list response received is status code: %s, response: %s",
285 def update_port_chain(self
,port_chain_id
,port_pair_groups
=None,flow_classifiers
=None):
288 port_chain_dict
['flow_classifiers'] = list()
289 port_chain_dict
['flow_classifiers'].extend(flow_classifiers
)
291 port_chain_dict
['port_pair_groups'] = list()
292 port_chain_dict
['port_pair_groups'].extend(port_pair_groups
)
294 port_chain
["port_chain"] = port_chain_dict
295 port_chain_json
= json
.dumps(port_chain
)
297 result
= self
.rest_api_handler(L2PortChainDriver
.PORT_CHAINS_URL
+'/{}'.format(port_chain_id
), 'PUT', port_chain_json
)
298 result
.raise_for_status()
299 self
.log
.debug("Update Port chain response received is status code: %s, response: %s",
302 return result
.json()['port_chain']['id']
304 def create_flow_classifier(self
,name
,classifier_dict
):
306 Create flow classifier
308 classifier_fields
= [ 'ethertype',
310 'source_port_range_min',
311 'source_port_range_max',
312 'destination_port_range_min',
313 'destination_port_range_max',
315 'destination_ip_prefix',
316 'logical_source_port' ]
318 flow_classifier_dict
= {}
319 flow_classifier_dict
= {k
: v
for k
, v
in classifier_dict
.items()
320 if k
in classifier_fields
}
321 flow_classifier_dict
["name"]= name
322 flow_classifier_dict
['tenant_id']= self
.tenant_id
324 #flow_classifier_dict['ethertype']= 'IPv4'
325 #flow_classifier_dict['protocol']= 'TCP'
326 #flow_classifier_dict['source_port_range_min']= 80
327 #flow_classifier_dict['source_port_range_max']= 80
328 #flow_classifier_dict['destination_port_range_min']= 80
329 #flow_classifier_dict['destination_port_range_max']= 80
330 #flow_classifier_dict['source_ip_prefix']= '11.0.6.5/32'
331 #flow_classifier_dict['destination_ip_prefix']= '11.0.6.6/32'
332 #flow_classifier_dict['logical_source_port']= source_neutron_port
333 #flow_classifier_dict['logical_destination_port']= ''
335 flow_classifier
["flow_classifier"] = flow_classifier_dict
336 flow_classifier_json
= json
.dumps(flow_classifier
)
338 result
= self
.rest_api_handler(L2PortChainDriver
.FLOW_CLASSIFIERS_URL
, 'POST', flow_classifier_json
)
339 result
.raise_for_status()
340 self
.log
.debug("Create flow classifier response received is status code: %s, response: %s",
343 return result
.json()['flow_classifier']['id']
345 def delete_flow_classifier(self
,flow_classifier_id
):
346 result
= self
.rest_api_handler(L2PortChainDriver
.FLOW_CLASSIFIERS_URL
+'/{}'.format(flow_classifier_id
), 'DELETE')
347 result
.raise_for_status()
348 self
.log
.debug("Delete flow classifier response received is status code: %s",
351 def get_flow_classifier(self
,flow_classifier_id
):
352 result
= self
.rest_api_handler(L2PortChainDriver
.FLOW_CLASSIFIERS_URL
+'/{}'.format(flow_classifier_id
), 'GET')
353 result
.raise_for_status()
354 self
.log
.debug("Get flow classifier response received is status code: %s, response: %s",