aboutsummaryrefslogtreecommitdiffstats
path: root/app/discover/fetchers/db
diff options
context:
space:
mode:
Diffstat (limited to 'app/discover/fetchers/db')
-rw-r--r--app/discover/fetchers/db/__init__.py9
-rw-r--r--app/discover/fetchers/db/db_access.py142
-rw-r--r--app/discover/fetchers/db/db_fetch_aggregate_hosts.py36
-rw-r--r--app/discover/fetchers/db/db_fetch_aggregates.py21
-rw-r--r--app/discover/fetchers/db/db_fetch_availability_zones.py22
-rw-r--r--app/discover/fetchers/db/db_fetch_az_network_hosts.py31
-rw-r--r--app/discover/fetchers/db/db_fetch_host_instances.py15
-rw-r--r--app/discover/fetchers/db/db_fetch_host_network_agents.py35
-rw-r--r--app/discover/fetchers/db/db_fetch_instances.py60
-rw-r--r--app/discover/fetchers/db/db_fetch_oteps.py81
-rw-r--r--app/discover/fetchers/db/db_fetch_port.py34
-rw-r--r--app/discover/fetchers/db/db_fetch_vedges_ovs.py178
-rw-r--r--app/discover/fetchers/db/db_fetch_vedges_vpp.py56
13 files changed, 720 insertions, 0 deletions
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