# 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__()