From 7e83d0876ddb84a45e130eeba28bc40ef53c074b Mon Sep 17 00:00:00 2001 From: Yaron Yogev Date: Thu, 27 Jul 2017 09:02:54 +0300 Subject: Calipso initial release for OPNFV Change-Id: I7210c244b0c10fa80bfa8c77cb86c9d6ddf8bc88 Signed-off-by: Yaron Yogev --- app/discover/fetchers/db/__init__.py | 9 ++ app/discover/fetchers/db/db_access.py | 142 ++++++++++++++++ .../fetchers/db/db_fetch_aggregate_hosts.py | 36 +++++ app/discover/fetchers/db/db_fetch_aggregates.py | 21 +++ .../fetchers/db/db_fetch_availability_zones.py | 22 +++ .../fetchers/db/db_fetch_az_network_hosts.py | 31 ++++ .../fetchers/db/db_fetch_host_instances.py | 15 ++ .../fetchers/db/db_fetch_host_network_agents.py | 35 ++++ app/discover/fetchers/db/db_fetch_instances.py | 60 +++++++ app/discover/fetchers/db/db_fetch_oteps.py | 81 ++++++++++ app/discover/fetchers/db/db_fetch_port.py | 34 ++++ app/discover/fetchers/db/db_fetch_vedges_ovs.py | 178 +++++++++++++++++++++ app/discover/fetchers/db/db_fetch_vedges_vpp.py | 56 +++++++ 13 files changed, 720 insertions(+) create mode 100644 app/discover/fetchers/db/__init__.py create mode 100644 app/discover/fetchers/db/db_access.py create mode 100644 app/discover/fetchers/db/db_fetch_aggregate_hosts.py create mode 100644 app/discover/fetchers/db/db_fetch_aggregates.py create mode 100644 app/discover/fetchers/db/db_fetch_availability_zones.py create mode 100644 app/discover/fetchers/db/db_fetch_az_network_hosts.py create mode 100644 app/discover/fetchers/db/db_fetch_host_instances.py create mode 100644 app/discover/fetchers/db/db_fetch_host_network_agents.py create mode 100644 app/discover/fetchers/db/db_fetch_instances.py create mode 100644 app/discover/fetchers/db/db_fetch_oteps.py create mode 100644 app/discover/fetchers/db/db_fetch_port.py create mode 100644 app/discover/fetchers/db/db_fetch_vedges_ovs.py create mode 100644 app/discover/fetchers/db/db_fetch_vedges_vpp.py (limited to 'app/discover/fetchers/db') diff --git a/app/discover/fetchers/db/__init__.py b/app/discover/fetchers/db/__init__.py new file mode 100644 index 0000000..b0637e9 --- /dev/null +++ b/app/discover/fetchers/db/__init__.py @@ -0,0 +1,9 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### diff --git a/app/discover/fetchers/db/db_access.py b/app/discover/fetchers/db/db_access.py new file mode 100644 index 0000000..00bd776 --- /dev/null +++ b/app/discover/fetchers/db/db_access.py @@ -0,0 +1,142 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +import mysql.connector + +from discover.configuration import Configuration +from discover.fetcher import Fetcher +from utils.string_utils import jsonify + + +class DbAccess(Fetcher): + conn = None + query_count_per_con = 0 + + # connection timeout set to 30 seconds, + # due to problems over long connections + TIMEOUT = 30 + + def __init__(self): + super().__init__() + self.config = Configuration() + self.conf = self.config.get("mysql") + self.connect_to_db() + cursor = DbAccess.conn.cursor(dictionary=True) + try: + # check if DB schema 'neutron' exists + cursor.execute("SELECT COUNT(*) FROM neutron.agents") + for row in cursor: + pass + self.neutron_db = "neutron" + except (AttributeError, mysql.connector.errors.ProgrammingError): + self.neutron_db = "ml2_neutron" + + def db_connect(self, _host, _port, _user, _password, _database): + if DbAccess.conn: + return + try: + connector = mysql.connector + DbAccess.conn = connector.connect(host=_host, port=_port, + connection_timeout=self.TIMEOUT, + user=_user, + password=_password, + database=_database, + raise_on_warnings=True) + DbAccess.conn.ping(True) # auto-reconnect if necessary + except: + self.log.critical("failed to connect to MySQL DB") + return + DbAccess.query_count_per_con = 0 + + def connect_to_db(self, force=False): + if DbAccess.conn: + if not force: + return + self.log.info("DbAccess: ****** forcing reconnect, " + + "query count: %s ******", + DbAccess.query_count_per_con) + DbAccess.conn = None + self.conf = self.config.get("mysql") + cnf = self.conf + cnf['schema'] = cnf['schema'] if 'schema' in cnf else 'nova' + self.db_connect(cnf["host"], cnf["port"], + cnf["user"], cnf["password"], + cnf["schema"]) + + def get_objects_list_for_id(self, query, object_type, id): + self.connect_to_db(DbAccess.query_count_per_con >= 25) + DbAccess.query_count_per_con += 1 + self.log.debug("query count: %s, running query:\n%s\n", + str(DbAccess.query_count_per_con), query) + + cursor = DbAccess.conn.cursor(dictionary=True) + try: + if id: + cursor.execute(query, [str(id)]) + else: + cursor.execute(query) + except (AttributeError, mysql.connector.errors.OperationalError) as e: + self.log.error(e) + self.connect_to_db(True) + # try again to run the query + cursor = DbAccess.conn.cursor(dictionary=True) + if id: + cursor.execute(query, [str(id)]) + else: + cursor.execute(query) + + rows = [] + for row in cursor: + rows.append(row) + return rows + + def get_objects_list(self, query, object_type): + return self.get_objects_list_for_id(query, object_type, None) + + def get_objects(self, qry, type, id): + return jsonify(self.get_objects_list(qry, type)) + + def get(self, id): + # return list of available fetch types + ret = { + "description": "List of available fetch calls for this interface", + "types": { + "regions": "Regions of this environment", + "projects": "Projects (tenants) of this environment", + "availability_zones": "Availability zones", + "aggregates": "Host aggregates", + "aggregate_hosts": "Hosts in aggregate X (parameter: id)", + "az_hosts": "Host in availability_zone X (parameter: id)" + } + } + return jsonify(ret) + + def exec(self, query, table, field, values): + try: + cursor = DbAccess.conn.cursor(dictionary=True) + cursor.execute(query, [table, field, values]) + except (AttributeError, mysql.connector.errors.OperationalError) as e: + self.log.error(e) + self.connect_to_db(True) + # try again to run the query + cursor = DbAccess.conn.cursor(dictionary=True) + cursor.execute(query, [table, field, values]) + + rows = [] + for row in cursor: + rows.append(row) + return rows + + def set(self, table, field, values): + query = """INSERT INTO %s %s VALUES %s""" + return self.exec(query, table, field, values) + + def delete(self, table, field, values): + query = """DELETE FROM %s WHERE %s=%s""" + return self.exec(query, table, field, values) diff --git a/app/discover/fetchers/db/db_fetch_aggregate_hosts.py b/app/discover/fetchers/db/db_fetch_aggregate_hosts.py new file mode 100644 index 0000000..59ba5d0 --- /dev/null +++ b/app/discover/fetchers/db/db_fetch_aggregate_hosts.py @@ -0,0 +1,36 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +import bson + +from discover.fetchers.db.db_access import DbAccess +from utils.inventory_mgr import InventoryMgr + + +class DbFetchAggregateHosts(DbAccess): + def get(self, id): + query = """ + SELECT CONCAT('aggregate-', a.name, '-', host) AS id, host AS name + FROM nova.aggregate_hosts ah + JOIN nova.aggregates a ON a.id = ah.aggregate_id + WHERE ah.deleted = 0 AND aggregate_id = %s + """ + hosts = self.get_objects_list_for_id(query, "host", id) + if hosts: + inv = InventoryMgr() + for host_rec in hosts: + host_id = host_rec['name'] + host = inv.get_by_id(self.get_env(), host_id) + if not host: + self.log.error('unable to find host {} ' + 'from aggregate {} in inventory' + .format(host_id, id)) + continue + host_rec['ref_id'] = bson.ObjectId(host['_id']) + return hosts diff --git a/app/discover/fetchers/db/db_fetch_aggregates.py b/app/discover/fetchers/db/db_fetch_aggregates.py new file mode 100644 index 0000000..da0720b --- /dev/null +++ b/app/discover/fetchers/db/db_fetch_aggregates.py @@ -0,0 +1,21 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +from discover.fetchers.db.db_access import DbAccess + + +class DbFetchAggregates(DbAccess): + def get(self, id): + return self.get_objects_list( + """ + SELECT id, name + FROM nova.aggregates + WHERE deleted = 0 + """, + "host aggregate") diff --git a/app/discover/fetchers/db/db_fetch_availability_zones.py b/app/discover/fetchers/db/db_fetch_availability_zones.py new file mode 100644 index 0000000..763d777 --- /dev/null +++ b/app/discover/fetchers/db/db_fetch_availability_zones.py @@ -0,0 +1,22 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +from discover.fetchers.db.db_access import DbAccess + +class DbFetchAvailabilityZones(DbAccess): + + def get(self, id): + query = """ + SELECT DISTINCT availability_zone, + availability_zone AS id, COUNT(DISTINCT host) AS descendants + FROM nova.instances + WHERE availability_zone IS NOT NULL + GROUP BY availability_zone + """ + return self.get_objects_list(query, "availability zone") diff --git a/app/discover/fetchers/db/db_fetch_az_network_hosts.py b/app/discover/fetchers/db/db_fetch_az_network_hosts.py new file mode 100644 index 0000000..09043ea --- /dev/null +++ b/app/discover/fetchers/db/db_fetch_az_network_hosts.py @@ -0,0 +1,31 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +import json + +from discover.fetchers.db.db_access import DbAccess + + +class DbFetchAZNetworkHosts(DbAccess): + + def get(self, id): + query = """ + SELECT DISTINCT host, host AS id, configurations + FROM neutron.agents + WHERE agent_type = 'Metadata agent' + """ + results = self.get_objects_list(query, "host") + for r in results: + self.set_host_details(r) + return results + + def set_host_details(self, r): + config = json.loads(r["configurations"]) + r["ip_address"] = config["nova_metadata_ip"] + r["host_type"] = "Network Node" diff --git a/app/discover/fetchers/db/db_fetch_host_instances.py b/app/discover/fetchers/db/db_fetch_host_instances.py new file mode 100644 index 0000000..2245c4a --- /dev/null +++ b/app/discover/fetchers/db/db_fetch_host_instances.py @@ -0,0 +1,15 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +from discover.fetchers.db.db_fetch_instances import DbFetchInstances + +class DbFetchHostInstances(DbFetchInstances): + + def get(self, id): + return self.get_instances("host", id) diff --git a/app/discover/fetchers/db/db_fetch_host_network_agents.py b/app/discover/fetchers/db/db_fetch_host_network_agents.py new file mode 100644 index 0000000..c323573 --- /dev/null +++ b/app/discover/fetchers/db/db_fetch_host_network_agents.py @@ -0,0 +1,35 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +import json + +from discover.fetchers.db.db_access import DbAccess +from utils.inventory_mgr import InventoryMgr + + +class DbFetchHostNetworkAgents(DbAccess): + def __init__(self): + super().__init__() + self.inv = InventoryMgr() + self.env_config = self.config.get_env_config() + + def get(self, id): + query = """ + SELECT * FROM {}.agents + WHERE host = %s + """.format(self.neutron_db) + host_id = id[:-1 * len("-network_agents")] + results = self.get_objects_list_for_id(query, "network_agent", host_id) + mechanism_drivers = self.env_config['mechanism_drivers'] + id_prefix = mechanism_drivers[0] if mechanism_drivers else 'network_agent' + for o in results: + o["configurations"] = json.loads(o["configurations"]) + o["name"] = o["binary"] + o['id'] = id_prefix + '-' + o['id'] + return results diff --git a/app/discover/fetchers/db/db_fetch_instances.py b/app/discover/fetchers/db/db_fetch_instances.py new file mode 100644 index 0000000..54c4114 --- /dev/null +++ b/app/discover/fetchers/db/db_fetch_instances.py @@ -0,0 +1,60 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +import json + +from discover.fetchers.db.db_access import DbAccess + + +class DbFetchInstances(DbAccess): + def get_instance_data(self, instances): + instances_hash = {} + for doc in instances: + instances_hash[doc["id"]] = doc + + query = """ + SELECT DISTINCT i.uuid AS id, i.display_name AS name, + i.host AS host, host_ip AS ip_address, + network_info, project_id, + IF(p.name IS NULL, "Unknown", p.name) AS project + FROM nova.instances i + LEFT JOIN keystone.project p ON p.id = i.project_id + JOIN nova.instance_info_caches ic ON i.uuid = ic.instance_uuid + JOIN nova.compute_nodes cn ON i.node = cn.hypervisor_hostname + WHERE i.deleted = 0 + """ + results = self.get_objects_list(query, "instance") + for result in results: + id = result["id"] + if id not in instances_hash: + continue + self.build_instance_details(result) + doc = instances_hash[id] + doc.update(result) + + def build_instance_details(self, result): + network_info_str = result.pop("network_info", None) + result["network_info"] = json.loads(network_info_str) + + # add network as an array to allow constraint checking + # when building clique + networks = [] + for net in result["network_info"]: + if "network" not in net or "id" not in net["network"]: + continue + network_id = net["network"]["id"] + if network_id in networks: + continue + networks.append(network_id) + result["network"] = networks + + result["type"] = "instance" + result["parent_type"] = "instances_folder" + result["parent_id"] = result["host"] + "-instances" + result["in_project-" + result.pop("project", None)] = "1" diff --git a/app/discover/fetchers/db/db_fetch_oteps.py b/app/discover/fetchers/db/db_fetch_oteps.py new file mode 100644 index 0000000..9055c11 --- /dev/null +++ b/app/discover/fetchers/db/db_fetch_oteps.py @@ -0,0 +1,81 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +import re + +from discover.fetchers.cli.cli_access import CliAccess +from discover.fetchers.db.db_access import DbAccess +from utils.inventory_mgr import InventoryMgr +from utils.singleton import Singleton + + +class DbFetchOteps(DbAccess, CliAccess, metaclass=Singleton): + def __init__(self): + super().__init__() + self.inv = InventoryMgr() + self.port_re = re.compile("^\s*port (\d+): ([^(]+)( \(internal\))?$") + + def get(self, id): + vedge = self.inv.get_by_id(self.get_env(), id) + tunnel_type = None + if "configurations" not in vedge: + return [] + if "tunnel_types" not in vedge["configurations"]: + return [] + if not vedge["configurations"]["tunnel_types"]: + return [] + tunnel_type = vedge["configurations"]["tunnel_types"][0] + host_id = vedge["host"] + table_name = "neutron.ml2_" + tunnel_type + "_endpoints" + env_config = self.config.get_env_config() + distribution = env_config["distribution"] + if distribution == "Canonical-icehouse": + # for Icehouse, we only get IP address from the DB, so take the + # host IP address and from the host data in Mongo + host = self.inv.get_by_id(self.get_env(), host_id) + results = [{"host": host_id, "ip_address": host["ip_address"]}] + else: + results = self.get_objects_list_for_id( + """ + SELECT * + FROM {} + WHERE host = %s + """.format(table_name), + "vedge", host_id) + for doc in results: + doc["id"] = host_id + "-otep" + doc["name"] = doc["id"] + doc["host"] = host_id + doc["overlay_type"] = tunnel_type + doc["ports"] = vedge["tunnel_ports"] if "tunnel_ports" in vedge else [] + if "udp_port" not in doc: + doc["udp_port"] = "67" + self.get_vconnector(doc, host_id, vedge) + + return results + + # find matching vConnector by tunneling_ip of vEdge + # look for that IP address in ifconfig for the host + def get_vconnector(self, doc, host_id, vedge): + tunneling_ip = vedge["configurations"]["tunneling_ip"] + ifconfig_lines = self.run_fetch_lines("ifconfig", host_id) + interface = None + ip_string = " " * 10 + "inet addr:" + tunneling_ip + " " + vconnector = None + for l in ifconfig_lines: + if l.startswith(" "): + if interface and l.startswith(ip_string): + vconnector = interface + break + else: + if " " in l: + interface = l[:l.index(" ")] + + if vconnector: + doc["vconnector"] = vconnector diff --git a/app/discover/fetchers/db/db_fetch_port.py b/app/discover/fetchers/db/db_fetch_port.py new file mode 100644 index 0000000..2cb814a --- /dev/null +++ b/app/discover/fetchers/db/db_fetch_port.py @@ -0,0 +1,34 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +from discover.fetchers.db.db_access import DbAccess +from utils.inventory_mgr import InventoryMgr + + +class DbFetchPort(DbAccess): + def __init__(self): + super().__init__() + self.inv = InventoryMgr() + self.env_config = self.config.get_env_config() + + def get(self, id=None): + query = """SELECT * FROM {}.ports where network_id = %s""" \ + .format(self.neutron_db) + return self.get_objects_list_for_id(query, "port", id) + + def get_id(self, id=None): + query = """SELECT id FROM {}.ports where network_id = %s""" \ + .format(self.neutron_db) + result = self.get_objects_list_for_id(query, "port", id) + return result[0]['id'] if result != [] else None + + def get_id_by_field(self, id, search=''): + query = """SELECT id FROM neutron.ports where network_id = %s AND """ + search + result = self.get_objects_list_for_id(query, "port", id) + return result[0]['id'] if result != [] else None \ No newline at end of file diff --git a/app/discover/fetchers/db/db_fetch_vedges_ovs.py b/app/discover/fetchers/db/db_fetch_vedges_ovs.py new file mode 100644 index 0000000..24cc9f8 --- /dev/null +++ b/app/discover/fetchers/db/db_fetch_vedges_ovs.py @@ -0,0 +1,178 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +import json + +import re + +from discover.fetchers.cli.cli_access import CliAccess +from discover.fetchers.db.db_access import DbAccess +from utils.inventory_mgr import InventoryMgr +from utils.singleton import Singleton + + +class DbFetchVedgesOvs(DbAccess, CliAccess, metaclass=Singleton): + def __init__(self): + super().__init__() + self.inv = InventoryMgr() + self.port_re = re.compile("^\s*port (\d+): ([^(]+)( \(internal\))?$") + self.port_line_header_prefix = " " * 8 + "Port " + + def get(self, id): + host_id = id[:id.rindex('-')] + results = self.get_objects_list_for_id( + """ + SELECT * + FROM {}.agents + WHERE host = %s AND agent_type = 'Open vSwitch agent' + """.format(self.neutron_db), + "vedge", host_id) + host = self.inv.get_by_id(self.get_env(), host_id) + if not host: + self.log.error("unable to find host in inventory: %s", host_id) + return [] + host_types = host["host_type"] + if "Network" not in host_types and "Compute" not in host_types: + return [] + vsctl_lines = self.run_fetch_lines("ovs-vsctl show", host["id"]) + ports = self.fetch_ports(host, vsctl_lines) + for doc in results: + doc["name"] = doc["host"] + "-OVS" + doc["configurations"] = json.loads(doc["configurations"]) + doc["ports"] = ports + doc["tunnel_ports"] = self.get_overlay_tunnels(doc, vsctl_lines) + return results + + def fetch_ports(self, host, vsctl_lines): + host_types = host["host_type"] + if "Network" not in host_types and "Compute" not in host_types: + return {} + ports = self.fetch_ports_from_dpctl(host["id"]) + self.fetch_port_tags_from_vsctl(vsctl_lines, ports) + return ports + + def fetch_ports_from_dpctl(self, host_id): + cmd = "ovs-dpctl show" + lines = self.run_fetch_lines(cmd, host_id) + ports = {} + for l in lines: + port_matches = self.port_re.match(l) + if not port_matches: + continue + port = {} + id = port_matches.group(1) + name = port_matches.group(2) + is_internal = port_matches.group(3) == " (internal)" + port["internal"] = is_internal + port["id"] = id + port["name"] = name + ports[name] = port + return ports + + # from ovs-vsctl, fetch tags of ports + # example format of ovs-vsctl output for a specific port: + # Port "tap9f94d28e-7b" + # tag: 5 + # Interface "tap9f94d28e-7b" + # type: internal + def fetch_port_tags_from_vsctl(self, vsctl_lines, ports): + port = None + for l in vsctl_lines: + if l.startswith(self.port_line_header_prefix): + port = None + port_name = l[len(self.port_line_header_prefix):] + # remove quotes from port name + if '"' in port_name: + port_name = port_name[1:][:-1] + if port_name in ports: + port = ports[port_name] + continue + if not port: + continue + if l.startswith(" " * 12 + "tag: "): + port["tag"] = l[l.index(":") + 2:] + ports[port["name"]] = port + return ports + + def get_overlay_tunnels(self, doc, vsctl_lines): + if doc["agent_type"] != "Open vSwitch agent": + return {} + if "tunneling_ip" not in doc["configurations"]: + return {} + if not doc["configurations"]["tunneling_ip"]: + self.get_bridge_pnic(doc) + return {} + + # read the 'br-tun' interface ports + # this will be used later in the OTEP + tunnel_bridge_header = " " * 4 + "Bridge br-tun" + try: + br_tun_loc = vsctl_lines.index(tunnel_bridge_header) + except ValueError: + return [] + lines = vsctl_lines[br_tun_loc + 1:] + tunnel_ports = {} + port = None + for l in lines: + # if we have only 4 or less spaces in the beginng, + # the br-tun section ended so return + if not l.startswith(" " * 5): + break + if l.startswith(self.port_line_header_prefix): + if port: + tunnel_ports[port["name"]] = port + name = l[len(self.port_line_header_prefix):].strip('" ') + port = {"name": name} + elif port and l.startswith(" " * 12 + "Interface "): + interface = l[10 + len("Interface ") + 1:].strip('" ') + port["interface"] = interface + elif port and l.startswith(" " * 16): + colon_pos = l.index(":") + attr = l[:colon_pos].strip() + val = l[colon_pos + 2:].strip('" ') + if attr == "options": + opts = val.strip('{}') + val = {} + for opt in opts.split(", "): + opt_name = opt[:opt.index("=")] + opt_val = opt[opt.index("=") + 1:].strip('" ') + val[opt_name] = opt_val + port[attr] = val + if port: + tunnel_ports[port["name"]] = port + return tunnel_ports + + def get_bridge_pnic(self, doc): + conf = doc["configurations"] + if "bridge_mappings" not in conf or not conf["bridge_mappings"]: + return + for v in conf["bridge_mappings"].values(): br = v + ifaces_list_lines = self.run_fetch_lines("ovs-vsctl list-ifaces " + br, + doc["host"]) + br_pnic_postfix = br + "--br-" + interface = "" + for l in ifaces_list_lines: + if l.startswith(br_pnic_postfix): + interface = l[len(br_pnic_postfix):] + break + if not interface: + return + doc["pnic"] = interface + # add port ID to pNIC + pnic = self.inv.find_items({ + "environment": self.get_env(), + "type": "pnic", + "host": doc["host"], + "name": interface + }, get_single=True) + if not pnic: + return + port = doc["ports"][interface] + pnic["port_id"] = port["id"] + self.inv.set(pnic) diff --git a/app/discover/fetchers/db/db_fetch_vedges_vpp.py b/app/discover/fetchers/db/db_fetch_vedges_vpp.py new file mode 100644 index 0000000..a1c659e --- /dev/null +++ b/app/discover/fetchers/db/db_fetch_vedges_vpp.py @@ -0,0 +1,56 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +from discover.fetchers.cli.cli_access import CliAccess +from discover.fetchers.db.db_access import DbAccess +from utils.inventory_mgr import InventoryMgr +from utils.singleton import Singleton + + +class DbFetchVedgesVpp(DbAccess, CliAccess, metaclass=Singleton): + def __init__(self): + super().__init__() + self.inv = InventoryMgr() + + def get(self, id): + host_id = id[:id.rindex('-')] + vedge = { + 'host': host_id, + 'id': host_id + '-VPP', + 'name': 'VPP-' + host_id, + 'agent_type': 'VPP' + } + ver = self.run_fetch_lines('vppctl show ver', host_id) + if ver: + ver = ver[0] + vedge['binary'] = ver[:ver.index(' ', ver.index(' ') + 1)] + host = self.inv.get_by_id(self.get_env(), host_id) + if not host: + self.log.error("unable to find host in inventory: %s", host_id) + return [] + host_types = host["host_type"] + if "Network" not in host_types and "Compute" not in host_types: + return [] + interfaces = self.run_fetch_lines('vppctl show int', host_id) + vedge['ports'] = self.fetch_ports(interfaces) + return [vedge] + + def fetch_ports(self, interfaces): + ports = {} + for i in interfaces: + if not i or i.startswith(' '): + continue + parts = i.split() + port = { + 'id': parts[1], + 'state': parts[2], + 'name': parts[0] + } + ports[port['name']] = port + return ports -- cgit 1.2.3-korg