289 lines
10 KiB
Python
289 lines
10 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import requests
|
|
from requests import auth
|
|
|
|
from neutron._i18n import _LI
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from oslo_serialization import jsonutils
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
cfg.CONF.import_opt(
|
|
'odl_username',
|
|
'gbpservice.neutron.services.grouppolicy.drivers.odl.config',
|
|
group='odl_driver'
|
|
)
|
|
cfg.CONF.import_opt(
|
|
'odl_password',
|
|
'gbpservice.neutron.services.grouppolicy.drivers.odl.config',
|
|
group='odl_driver'
|
|
)
|
|
cfg.CONF.import_opt(
|
|
'odl_host',
|
|
'gbpservice.neutron.services.grouppolicy.drivers.odl.config',
|
|
group='odl_driver'
|
|
)
|
|
cfg.CONF.import_opt(
|
|
'odl_port',
|
|
'gbpservice.neutron.services.grouppolicy.drivers.odl.config',
|
|
group='odl_driver'
|
|
)
|
|
|
|
|
|
class OdlManager(object):
|
|
"""Class to manage ODL translations and workflow.
|
|
|
|
This class manages translation from Neutron objects to APIC
|
|
managed objects and contains workflows to implement these
|
|
translations.
|
|
"""
|
|
|
|
def __init__(self):
|
|
|
|
LOG.info(_LI("Configured ODL username: %s"),
|
|
cfg.CONF.odl_driver.odl_username)
|
|
LOG.info(_LI("Configured ODL password: %s"),
|
|
cfg.CONF.odl_driver.odl_password)
|
|
LOG.info(_LI("Configured ODL host: %s"),
|
|
cfg.CONF.odl_driver.odl_host)
|
|
LOG.info(_LI("Configured ODL port: %s"),
|
|
cfg.CONF.odl_driver.odl_port)
|
|
|
|
self._username = cfg.CONF.odl_driver.odl_username
|
|
self._password = cfg.CONF.odl_driver.odl_password
|
|
self._host = cfg.CONF.odl_driver.odl_host
|
|
self._port = cfg.CONF.odl_driver.odl_port
|
|
self._headers = {
|
|
'Content-type': 'application/yang.data+json',
|
|
'Accept': 'application/yang.data+json',
|
|
}
|
|
|
|
self._base_url = (
|
|
"http://%(host)s:%(port)s/restconf" %
|
|
{'host': self._host, 'port': self._port}
|
|
)
|
|
self._reg_ep_url = (
|
|
self._base_url +
|
|
'/operations/endpoint:register-endpoint'
|
|
)
|
|
self._unreg_ep_url = (
|
|
self._base_url +
|
|
'/operations/endpoint:unregister-endpoint'
|
|
)
|
|
self._policy_url = (
|
|
self._base_url +
|
|
'/config/policy:tenants/policy:tenant/%(tenant_id)s'
|
|
)
|
|
self._action_url = (
|
|
self._policy_url +
|
|
'/subject-feature-instances/action-instance/%(action)s'
|
|
)
|
|
self._classifier_url = (
|
|
self._policy_url +
|
|
'/subject-feature-instances/classifier-instance/%(classifier)s'
|
|
)
|
|
self._l3ctx_url = (
|
|
self._policy_url +
|
|
'/l3-context/%(l3ctx)s'
|
|
)
|
|
self._l2bd_url = (
|
|
self._policy_url +
|
|
'/l2-bridge-domain/%(l2bd)s'
|
|
)
|
|
self._l2fd_url = (
|
|
self._policy_url +
|
|
'/l2-flood-domain/%(l2fd)s'
|
|
)
|
|
self._epg_url = (
|
|
self._policy_url +
|
|
'/policy:endpoint-group/%(epg)s'
|
|
)
|
|
self._subnet_url = (
|
|
self._policy_url +
|
|
'/subnet/%(subnet)s'
|
|
)
|
|
self._contract_url = (
|
|
self._policy_url +
|
|
'/policy:contract/%(contract)s'
|
|
)
|
|
|
|
def _convert2ascii(self, obj):
|
|
if isinstance(obj, dict):
|
|
return {self._convert2ascii(key): self._convert2ascii(value) for
|
|
key, value in obj.iteritems()}
|
|
elif isinstance(obj, list):
|
|
return [self._convert2ascii(element) for element in obj]
|
|
elif isinstance(obj, unicode):
|
|
return obj.encode('ascii', 'ignore')
|
|
else:
|
|
return obj
|
|
|
|
def _sendjson(self, method, url, headers, obj=None):
|
|
"""Send json to the ODL controller."""
|
|
|
|
medium = self._convert2ascii(obj) if obj else None
|
|
url = self._convert2ascii(url)
|
|
data = (
|
|
jsonutils.dumps(medium, indent=4, sort_keys=True) if medium
|
|
else None
|
|
)
|
|
LOG.debug("=========================================================")
|
|
LOG.debug("Sending METHOD (%(method)s) URL (%(url)s)",
|
|
{'method': method, 'url': url})
|
|
LOG.debug("(%(data)s)", {'data': data})
|
|
LOG.debug("=========================================================")
|
|
r = requests.request(
|
|
method,
|
|
url=url,
|
|
headers=headers,
|
|
data=data,
|
|
auth=auth.HTTPBasicAuth(self._username,
|
|
self._password)
|
|
)
|
|
r.raise_for_status()
|
|
|
|
def _is_tenant_created(self, tenant_id):
|
|
url = self._convert2ascii(self._policy_url % {'tenant_id': tenant_id})
|
|
r = requests.request(
|
|
'get',
|
|
url=url,
|
|
headers=self._headers,
|
|
auth=auth.HTTPBasicAuth(self._username,
|
|
self._password)
|
|
)
|
|
if r.status_code == 200:
|
|
return True
|
|
elif r.status_code == 404:
|
|
return False
|
|
else:
|
|
r.raise_for_status()
|
|
|
|
def register_endpoints(self, endpoints):
|
|
for ep in endpoints:
|
|
data = {"input": ep}
|
|
self._sendjson('post', self._reg_ep_url, self._headers, data)
|
|
|
|
def unregister_endpoints(self, endpoints):
|
|
for ep in endpoints:
|
|
data = {"input": ep}
|
|
self._sendjson('post', self._unreg_ep_url, self._headers, data)
|
|
|
|
def create_update_tenant(self, tenant_id, tenant):
|
|
url = (self._policy_url % {'tenant_id': tenant_id})
|
|
data = {"tenant": tenant}
|
|
self._sendjson('put', url, self._headers, data)
|
|
|
|
def create_action(self, tenant_id, action):
|
|
"""Create policy action"""
|
|
self._touch_tenant(tenant_id)
|
|
url = (self._action_url %
|
|
{'tenant_id': tenant_id, 'action': action['name']})
|
|
data = {"action-instance": action}
|
|
self._sendjson('put', url, self._headers, data)
|
|
|
|
def delete_action(self, tenant_id, action):
|
|
"""Delete policy action"""
|
|
url = (self._action_url %
|
|
{'tenant_id': tenant_id, 'action': action['name']})
|
|
self._sendjson('delete', url, self._headers)
|
|
|
|
def create_classifier(self, tenant_id, classifier):
|
|
"""Create policy classifier"""
|
|
self._touch_tenant(tenant_id)
|
|
url = (self._classifier_url %
|
|
{'tenant_id': tenant_id, 'classifier': classifier['name']})
|
|
data = {"classifier-instance": classifier}
|
|
self._sendjson('put', url, self._headers, data)
|
|
|
|
def delete_classifier(self, tenant_id, classifier):
|
|
"""Delete policy classifier"""
|
|
url = (self._classifier_url %
|
|
{'tenant_id': tenant_id, 'classifier': classifier['name']})
|
|
self._sendjson('delete', url, self._headers)
|
|
|
|
def create_update_l3_context(self, tenant_id, l3ctx):
|
|
self._touch_tenant(tenant_id)
|
|
url = (self._l3ctx_url %
|
|
{'tenant_id': tenant_id, 'l3ctx': l3ctx['id']})
|
|
data = {"l3-context": l3ctx}
|
|
self._sendjson('put', url, self._headers, data)
|
|
|
|
def delete_l3_context(self, tenant_id, l3ctx):
|
|
url = (self._l3ctx_url %
|
|
{'tenant_id': tenant_id, 'l3ctx': l3ctx['id']})
|
|
self._sendjson('delete', url, self._headers)
|
|
|
|
def create_update_l2_bridge_domain(self, tenant_id, l2bd):
|
|
self._touch_tenant(tenant_id)
|
|
url = (self._l2bd_url %
|
|
{'tenant_id': tenant_id, 'l2bd': l2bd['id']})
|
|
data = {"l2-bridge-domain": l2bd}
|
|
self._sendjson('put', url, self._headers, data)
|
|
|
|
def delete_l2_bridge_domain(self, tenant_id, l2bd):
|
|
url = (self._l2bd_url %
|
|
{'tenant_id': tenant_id, 'l2bd': l2bd['id']})
|
|
self._sendjson('delete', url, self._headers)
|
|
|
|
def create_update_l2_flood_domain(self, tenant_id, l2fd):
|
|
self._touch_tenant(tenant_id)
|
|
url = (self._l2fd_url %
|
|
{'tenant_id': tenant_id, 'l2fd': l2fd['id']})
|
|
data = {"l2-flood-domain": l2fd}
|
|
self._sendjson('put', url, self._headers, data)
|
|
|
|
def delete_l2_flood_domain(self, tenant_id, l2fd):
|
|
url = (self._l2fd_url %
|
|
{'tenant_id': tenant_id, 'l2fd': l2fd['id']})
|
|
self._sendjson('delete', url, self._headers)
|
|
|
|
def create_update_endpoint_group(self, tenant_id, epg):
|
|
self._touch_tenant(tenant_id)
|
|
url = (self._epg_url %
|
|
{'tenant_id': tenant_id, 'epg': epg['id']})
|
|
data = {"endpoint-group": epg}
|
|
self._sendjson('put', url, self._headers, data)
|
|
|
|
def delete_endpoint_group(self, tenant_id, epg):
|
|
url = (self._epg_url %
|
|
{'tenant_id': tenant_id, 'epg': epg['id']})
|
|
self._sendjson('delete', url, self._headers)
|
|
|
|
def create_update_subnet(self, tenant_id, subnet):
|
|
self._touch_tenant(tenant_id)
|
|
url = (self._subnet_url %
|
|
{'tenant_id': tenant_id, 'subnet': subnet['id']})
|
|
data = {"subnet": subnet}
|
|
self._sendjson('put', url, self._headers, data)
|
|
|
|
def delete_subnet(self, tenant_id, subnet):
|
|
url = (self._subnet_url %
|
|
{'tenant_id': tenant_id, 'subnet': subnet['id']})
|
|
self._sendjson('delete', url, self._headers)
|
|
|
|
def create_update_contract(self, tenant_id, contract):
|
|
url = (self._contract_url %
|
|
{'tenant_id': tenant_id, 'contract': contract['id']})
|
|
data = {"contract": contract}
|
|
self._sendjson('put', url, self._headers, data)
|
|
|
|
def _touch_tenant(self, tenant_id):
|
|
tenant = {
|
|
"id": tenant_id
|
|
}
|
|
if not self._is_tenant_created(tenant_id):
|
|
self.create_update_tenant(tenant_id, tenant)
|