510 lines
16 KiB
Python
510 lines
16 KiB
Python
# Copyright (c) 2015 OpenStack Foundation.
|
|
#
|
|
# All Rights Reserved.
|
|
#
|
|
# 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 eventlet
|
|
import netaddr
|
|
import time
|
|
|
|
from oslo_log import log
|
|
from oslo_serialization import jsonutils
|
|
from oslo_utils import timeutils
|
|
|
|
from neutron.i18n import _LI
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
class DbUpdate(object):
|
|
"""Encapsulates a DB update
|
|
|
|
An instance of this object carries the information necessary to prioritize
|
|
and process a request to update a DB entry.
|
|
Lower value is higher priority !
|
|
"""
|
|
def __init__(self, table, key, action, value, priority=5,
|
|
timestamp=None):
|
|
self.priority = priority
|
|
self.timestamp = timestamp
|
|
if not timestamp:
|
|
self.timestamp = timeutils.utcnow()
|
|
self.key = key
|
|
self.action = action
|
|
self.table = table
|
|
self.value = value
|
|
|
|
def __lt__(self, other):
|
|
"""Implements priority among updates
|
|
|
|
Lower numerical priority always gets precedence. When comparing two
|
|
updates of the same priority then the one with the earlier timestamp
|
|
gets procedence. In the unlikely event that the timestamps are also
|
|
equal it falls back to a simple comparison of ids meaning the
|
|
precedence is essentially random.
|
|
"""
|
|
if self.priority != other.priority:
|
|
return self.priority < other.priority
|
|
if self.timestamp != other.timestamp:
|
|
return self.timestamp < other.timestamp
|
|
return self.key < other.key
|
|
|
|
|
|
class NbApi(object):
|
|
|
|
def __init__(self, db_driver):
|
|
super(NbApi, self).__init__()
|
|
self.driver = db_driver
|
|
self.controller = None
|
|
self._queue = eventlet.queue.PriorityQueue()
|
|
self.db_apply_failed = False
|
|
|
|
def initialize(self, db_ip='127.0.0.1', db_port=4001):
|
|
self.driver.initialize(db_ip, db_port)
|
|
|
|
def support_publish_subscribe(self):
|
|
return self.driver.support_publish_subscribe()
|
|
|
|
def allocate_tunnel_key(self):
|
|
return self.driver.allocate_unique_key()
|
|
|
|
def register_notification_callback(self, controller):
|
|
self.controller = controller
|
|
LOG.info(_LI("DB configuration sync finished, waiting for changes"))
|
|
self.driver.register_notification_callback(
|
|
self.db_change_callback)
|
|
self._read_db_changes_from_queue()
|
|
|
|
def db_change_callback(self, table, key, action, value):
|
|
update = DbUpdate(table, key, action, value)
|
|
self._queue.put(update)
|
|
|
|
def _read_db_changes_from_queue(self):
|
|
while True:
|
|
if not self.db_apply_failed:
|
|
self.next_update = self._queue.get()
|
|
try:
|
|
self.apply_db_change(self.next_update.table,
|
|
self.next_update.key,
|
|
self.next_update.action,
|
|
self.next_update.value)
|
|
self.db_apply_failed = False
|
|
except Exception as e:
|
|
if "ofport is 0" not in e.message:
|
|
LOG.warning(e)
|
|
self.db_apply_failed = True
|
|
time.sleep(1)
|
|
|
|
def apply_db_change(self, table, key, action, value):
|
|
if action == 'sync':
|
|
self.controller.run_sync()
|
|
return
|
|
|
|
self.controller.vswitch_api.sync()
|
|
if 'lport' == table:
|
|
if action == 'set' or action == 'create':
|
|
lport = LogicalPort(value)
|
|
self.controller.logical_port_updated(lport)
|
|
else:
|
|
lport_id = key
|
|
self.controller.logical_port_deleted(lport_id)
|
|
if 'lrouter' == table:
|
|
if action == 'set' or action == 'create':
|
|
lrouter = LogicalRouter(value)
|
|
self.controller.router_updated(lrouter)
|
|
else:
|
|
lrouter_id = key
|
|
self.controller.router_deleted(lrouter_id)
|
|
if 'chassis' == table:
|
|
if action == 'set' or action == 'create':
|
|
chassis = Chassis(value)
|
|
self.controller.chassis_created(chassis)
|
|
else:
|
|
chassis_id = key
|
|
self.controller.chassis_deleted(chassis_id)
|
|
if 'lswitch' == table:
|
|
if action == 'set' or action == 'create':
|
|
lswitch = LogicalSwitch(value)
|
|
self.controller.logical_switch_updated(lswitch)
|
|
else:
|
|
lswitch_id = key
|
|
self.controller.logical_switch_deleted(lswitch_id)
|
|
|
|
def sync(self):
|
|
pass
|
|
|
|
def create_security_group(self, name, **columns):
|
|
secgroup = {}
|
|
secgroup['name'] = name
|
|
for col, val in columns.items():
|
|
secgroup[col] = val
|
|
secgroup_json = jsonutils.dumps(secgroup)
|
|
self.driver.create_key('secgroup', name, secgroup_json)
|
|
|
|
def delete_security_group(self, name):
|
|
self.driver.delete_key('secgroup', name)
|
|
|
|
def add_security_group_rules(self, sg_name, new_rules):
|
|
secgroup_json = self.driver.get_key('secgroup', sg_name)
|
|
secgroup = jsonutils.loads(secgroup_json)
|
|
rules = secgroup.get('rules', [])
|
|
rules.extend(new_rules)
|
|
secgroup['rules'] = rules
|
|
secgroup_json = jsonutils.dumps(secgroup)
|
|
self.driver.set_key('secgroup', sg_name, secgroup_json)
|
|
|
|
def delete_security_group_rule(self, sg_name, sgr_id):
|
|
secgroup_json = self.driver.get_key('secgroup', sg_name)
|
|
secgroup = jsonutils.loads(secgroup_json)
|
|
rules = secgroup.get('rules')
|
|
new_rules = []
|
|
for rule in rules:
|
|
if rule['id'] != sgr_id:
|
|
new_rules.append(rule)
|
|
secgroup['rules'] = new_rules
|
|
secgroup_json = jsonutils.dumps(secgroup)
|
|
self.driver.set_key('secgroup', sg_name, secgroup_json)
|
|
|
|
def get_chassis(self, name):
|
|
try:
|
|
chassis_value = self.driver.get_key('chassis', name)
|
|
return Chassis(chassis_value)
|
|
except Exception:
|
|
return None
|
|
|
|
def get_all_chassis(self):
|
|
res = []
|
|
for entry_value in self.driver.get_all_entries('chassis'):
|
|
res.append(Chassis(entry_value))
|
|
return res
|
|
|
|
def add_chassis(self, name, ip, tunnel_type):
|
|
chassis = {'name': name, 'ip': ip,
|
|
'tunnel_type': tunnel_type}
|
|
chassis_json = jsonutils.dumps(chassis)
|
|
self.driver.create_key('chassis', name, chassis_json)
|
|
|
|
def get_lswitch(self, name):
|
|
try:
|
|
lswitch_value = self.driver.get_key('lswitch', name)
|
|
return LogicalSwitch(lswitch_value)
|
|
except Exception:
|
|
return None
|
|
|
|
def add_subnet(self, id, lswitch_name, **columns):
|
|
lswitch_json = self.driver.get_key('lswitch', lswitch_name)
|
|
lswitch = jsonutils.loads(lswitch_json)
|
|
|
|
subnet = {}
|
|
subnet['id'] = id
|
|
subnet['lswitch'] = lswitch_name
|
|
for col, val in columns.items():
|
|
subnet[col] = val
|
|
|
|
subnets = lswitch.get('subnets', [])
|
|
subnets.append(subnet)
|
|
lswitch['subnets'] = subnets
|
|
lswitch_json = jsonutils.dumps(lswitch)
|
|
self.driver.set_key('lswitch', lswitch_name, lswitch_json)
|
|
|
|
def update_subnet(self, id, lswitch_name, **columns):
|
|
lswitch_json = self.driver.get_key('lswitch', lswitch_name)
|
|
lswitch = jsonutils.loads(lswitch_json)
|
|
subnet = None
|
|
for s in lswitch.get('subnets', []):
|
|
if s['id'] == id:
|
|
subnet = s
|
|
|
|
for col, val in columns.items():
|
|
subnet[col] = val
|
|
|
|
lswitch_json = jsonutils.dumps(lswitch)
|
|
self.driver.set_key('lswitch', lswitch_name, lswitch_json)
|
|
|
|
def delete_subnet(self, id, lswitch_name):
|
|
lswitch_json = self.driver.get_key('lswitch', lswitch_name)
|
|
lswitch = jsonutils.loads(lswitch_json)
|
|
|
|
new_ports = []
|
|
for subnet in lswitch.get('subnets', []):
|
|
if subnet['id'] != id:
|
|
new_ports.append(subnet)
|
|
|
|
lswitch['subnets'] = new_ports
|
|
lswitch_json = jsonutils.dumps(lswitch)
|
|
self.driver.set_key('lswitch', lswitch_name, lswitch_json)
|
|
|
|
def get_logical_port(self, port_id):
|
|
try:
|
|
port_value = self.driver.get_key('lport', port_id)
|
|
return LogicalPort(port_value)
|
|
except Exception:
|
|
return None
|
|
|
|
def get_all_logical_ports(self):
|
|
res = []
|
|
for lport_value in self.driver.get_all_entries('lport'):
|
|
lport = LogicalPort(lport_value)
|
|
if lport.get_chassis() is None:
|
|
continue
|
|
res.append(lport)
|
|
return res
|
|
|
|
def create_lswitch(self, name, **columns):
|
|
lswitch = {}
|
|
lswitch['name'] = name
|
|
for col, val in columns.items():
|
|
lswitch[col] = val
|
|
lswitch_json = jsonutils.dumps(lswitch)
|
|
self.driver.create_key('lswitch', name, lswitch_json)
|
|
|
|
def update_lswitch(self, name, **columns):
|
|
lswitch_json = self.driver.get_key('lswitch', name)
|
|
lswitch = jsonutils.loads(lswitch_json)
|
|
for col, val in columns.items():
|
|
lswitch[col] = val
|
|
lswitch_json = jsonutils.dumps(lswitch)
|
|
self.driver.set_key('lswitch', name, lswitch_json)
|
|
|
|
def delete_lswitch(self, name):
|
|
self.driver.delete_key('lswitch', name)
|
|
|
|
def create_lport(self, name, lswitch_name, **columns):
|
|
lport = {}
|
|
lport['name'] = name
|
|
lport['lswitch'] = lswitch_name
|
|
for col, val in columns.items():
|
|
lport[col] = val
|
|
lport_json = jsonutils.dumps(lport)
|
|
self.driver.create_key('lport', name, lport_json)
|
|
|
|
def update_lport(self, name, **columns):
|
|
lport_json = self.driver.get_key('lport', name)
|
|
lport = jsonutils.loads(lport_json)
|
|
for col, val in columns.items():
|
|
lport[col] = val
|
|
lport_json = jsonutils.dumps(lport)
|
|
self.driver.set_key('lport', name, lport_json)
|
|
|
|
def delete_lport(self, name):
|
|
self.driver.delete_key('lport', name)
|
|
|
|
def create_lrouter(self, name, **columns):
|
|
lrouter = {}
|
|
lrouter['name'] = name
|
|
for col, val in columns.items():
|
|
lrouter[col] = val
|
|
lrouter_json = jsonutils.dumps(lrouter)
|
|
self.driver.create_key('lrouter', name, lrouter_json)
|
|
|
|
def delete_lrouter(self, name):
|
|
self.driver.delete_key('lrouter', name)
|
|
|
|
def add_lrouter_port(self, name, lrouter_name, lswitch, **columns):
|
|
lrouter_json = self.driver.get_key('lrouter', lrouter_name)
|
|
lrouter = jsonutils.loads(lrouter_json)
|
|
|
|
lrouter_port = {}
|
|
lrouter_port['name'] = name
|
|
lrouter_port['lrouter'] = lrouter_name
|
|
lrouter_port['lswitch'] = lswitch
|
|
for col, val in columns.items():
|
|
lrouter_port[col] = val
|
|
|
|
router_ports = lrouter.get('ports', [])
|
|
router_ports.append(lrouter_port)
|
|
lrouter['ports'] = router_ports
|
|
lrouter_json = jsonutils.dumps(lrouter)
|
|
self.driver.set_key('lrouter', lrouter_name, lrouter_json)
|
|
|
|
def delete_lrouter_port(self, lrouter_name, lswitch):
|
|
lrouter_json = self.driver.get_key('lrouter', lrouter_name)
|
|
lrouter = jsonutils.loads(lrouter_json)
|
|
|
|
new_ports = []
|
|
for port in lrouter.get('ports', []):
|
|
if port['lswitch'] != lswitch:
|
|
new_ports.append(port)
|
|
|
|
lrouter['ports'] = new_ports
|
|
lrouter_json = jsonutils.dumps(lrouter)
|
|
self.driver.set_key('lrouter', lrouter_name, lrouter_json)
|
|
|
|
def get_routers(self):
|
|
res = []
|
|
for lrouter_value in self.driver.get_all_entries('lrouter'):
|
|
res.append(LogicalRouter(lrouter_value))
|
|
return res
|
|
|
|
def get_all_logical_switches(self):
|
|
res = []
|
|
for lswitch_value in self.driver.get_all_entries('lswitch'):
|
|
res.append(LogicalSwitch(lswitch_value))
|
|
return res
|
|
|
|
|
|
class Chassis(object):
|
|
|
|
def __init__(self, value):
|
|
self.chassis = jsonutils.loads(value)
|
|
|
|
def get_name(self):
|
|
return self.chassis['name']
|
|
|
|
def get_ip(self):
|
|
return self.chassis['ip']
|
|
|
|
def get_encap_type(self):
|
|
return self.chassis['tunnel_type']
|
|
|
|
def __str__(self):
|
|
return self.chassis.__str__()
|
|
|
|
|
|
class LogicalSwitch(object):
|
|
|
|
def __init__(self, value):
|
|
self.lswitch = jsonutils.loads(value)
|
|
|
|
def get_id(self):
|
|
return self.lswitch['name']
|
|
|
|
def get_subnets(self):
|
|
res = []
|
|
for subnet in self.lswitch['subnets']:
|
|
res.append(Subnet(subnet))
|
|
return res
|
|
|
|
def __str__(self):
|
|
return self.lswitch.__str__()
|
|
|
|
def __eq__(self, other):
|
|
if isinstance(other, self.__class__):
|
|
return self.lswitch == other.lswitch
|
|
else:
|
|
return False
|
|
|
|
|
|
class Subnet(object):
|
|
|
|
def __init__(self, value):
|
|
self.subnet = value
|
|
|
|
def enable_dhcp(self):
|
|
return self.subnet['enable_dhcp']
|
|
|
|
def get_dhcp_server_address(self):
|
|
return self.subnet['dhcp_ip']
|
|
|
|
def get_cidr(self):
|
|
return self.subnet['cidr']
|
|
|
|
def get_gateway_ip(self):
|
|
return self.subnet['gateway_ip']
|
|
|
|
def get_dns_name_servers(self):
|
|
return self.subnet['dns_nameservers']
|
|
|
|
|
|
class LogicalPort(object):
|
|
|
|
def __init__(self, value):
|
|
self.external_dict = {}
|
|
self.lport = jsonutils.loads(value)
|
|
|
|
def get_id(self):
|
|
return self.lport.get('name')
|
|
|
|
def get_ip(self):
|
|
return self.lport['ips'][0]
|
|
|
|
def get_mac(self):
|
|
return self.lport['macs'][0]
|
|
|
|
def get_chassis(self):
|
|
return self.lport.get('chassis')
|
|
|
|
def get_lswitch_id(self):
|
|
return self.lport.get('lswitch')
|
|
|
|
def get_tunnel_key(self):
|
|
return int(self.lport['tunnel_key'])
|
|
|
|
def set_external_value(self, key, value):
|
|
self.external_dict[key] = value
|
|
|
|
def get_external_value(self, key):
|
|
return self.external_dict.get(key)
|
|
|
|
def get_device_owner(self):
|
|
return self.lport.get('device_owner')
|
|
|
|
def __str__(self):
|
|
return self.lport.__str__() + self.external_dict.__str__()
|
|
|
|
|
|
class LogicalRouter(object):
|
|
|
|
def __init__(self, value):
|
|
self.lrouter = jsonutils.loads(value)
|
|
|
|
def get_name(self):
|
|
return self.lrouter.get('name')
|
|
|
|
def get_ports(self):
|
|
res = []
|
|
for port in self.lrouter.get('ports'):
|
|
res.append(LogicalRouterPort(port))
|
|
return res
|
|
|
|
def __str__(self):
|
|
return self.lrouter.__str__()
|
|
|
|
|
|
class LogicalRouterPort(object):
|
|
|
|
def __init__(self, value):
|
|
self.router_port = value
|
|
self.cidr = netaddr.IPNetwork(self.router_port['network'])
|
|
|
|
def get_name(self):
|
|
return self.router_port.get('name')
|
|
|
|
def get_ip(self):
|
|
return str(self.cidr.ip)
|
|
|
|
def get_cidr_network(self):
|
|
return str(self.cidr.network)
|
|
|
|
def get_cidr_netmask(self):
|
|
return str(self.cidr.netmask)
|
|
|
|
def get_mac(self):
|
|
return self.router_port.get('mac')
|
|
|
|
def get_lswitch_id(self):
|
|
return self.router_port['lswitch']
|
|
|
|
def get_network(self):
|
|
return self.router_port['network']
|
|
|
|
def get_tunnel_key(self):
|
|
return self.router_port['tunnel_key']
|
|
|
|
def __eq__(self, other):
|
|
return self.get_name() == other.get_name()
|
|
|
|
def __str__(self):
|
|
return self.router_port.__str__()
|