def update_service_container_metrics(self, id, device_id, end_time): coll = self.get_collection('service-container-metric') coll = coll['serviceContainerMetrics'] device_id = "device/{0}".format(device_id) end_time = float(end_time) // 1000000000 end_time = datetime.utcfromtimestamp(end_time) mlsec = end_time.microsecond json_end_time = end_time.strftime( '%Y-%m-%dT%H:%M:%S.%f{:02d}Z'.format(mlsec)) scm_id = None for item in coll: dev_id = item['device_id']['href'] cont_id = item['container_id'] if dev_id == device_id and cont_id == id: scm_id = item['id'] break if scm_id: url = self.cimi_url + '/' + scm_id data = {'stop_time': json_end_time} res = requests.put(url, headers=CIMI_SEC_HEADERS, verify=SSL_VERIFY, json=data) if res.status_code != 200: LOG.error(res.json()) return res else: return ""
def get_subgraph(node_id): """ Returns the subgraph using a node id as the start. """ LOG.info("Retrieving Subgraph with url %s", request.url) timestamp = request.args.get("timestamp") time_frame = request.args.get("timeframe", 0) geo = _bool(request.args.get("geo", False)) # filter arguments. filter_these = _bool(request.args.get("filter-these", True)) filter_node = request.args.get("filter-nodes", []) # Fetch the subgraph. subgraph = LANDSCAPE.graph_db.get_subgraph(node_id, timestmp=timestamp, timeframe=time_frame) if not subgraph: err_msg = "Node with ID '{}', not in the landscape.".format(node_id) LOG.error(err_msg) abort(400, err_msg) if filter_node: filter_node = ast.literal_eval(filter_node) subgraph = util_graph.filter_nodes(subgraph, filter_node, filter_these) if geo: subgraph = Geo.extract_geo(subgraph) return Response(subgraph, mimetype=MIME)
def get_collection(self, collection, from_date=None, limit=None, updates=False): # try: fieldName = "created" if updates is True: fieldName = "updated" date_filter = "" if from_date: date_filter = '&$filter=' + fieldName + '>"%s"' % from_date limit_filter = "" if limit: limit_filter = "&$last={}".format(limit) url = self.cimi_url + '/' + collection + '?$orderby=' + \ fieldName + ':desc' + date_filter + limit_filter # print url res = requests.get(url, headers={'slipstream-authn-info': 'internal ADMIN'}, verify=SSL_VERIFY) if res.status_code == 200: return res.json() LOG.error("Request failed: " + str(res.status_code)) LOG.error("Response: " + str(res.json())) return dict()
def add_new_device(): """ Adds a new device to the physical layer """ LOG.info("Accessing URL %s", request.url) now_ts = time.time() error_log = [] if not request.data: err_msg = "No device data in body" abort(400, err_msg) LOG.debug(request.data) data = ast.literal_eval(request.data) # get config manager from landscaper.utilities import configuration conf_manager = configuration.ConfigurationManager() conf_manager.add_section('physical_layer') conf_manager.add_section('general') # save file to disk from landscaper.collector.cimi_physicalhost_collector import CimiPhysicalCollector cimi_updater = CimiPhysicalCollector(None, conf_manager, None, None) cimi_updater.generate_files(data) if error_log: err_msg = "Error with the following nodes:" + str(error_log) abort(400, err_msg) return Response(status=201, mimetype=MIME)
def get_swarm_manager(docker_conf): """ Retrieves Docker client object or error :return client object or error """ # if docker_conf[2] and docker_conf[3]: # tls_config = docker.tls.TLSConfig( # client_cert=(docker_conf[2], docker_conf[3]) # ) # else: # tls_config = False # # manager_address = ContainerCollectorV1.get_connection_string(docker_conf) # client = docker.DockerClient(base_url=manager_address, tls=tls_config) client = docker.from_env() try: if client.swarm.init(): LOG.info("Node joined swarm") except docker.errors.APIError: LOG.info("Node already part of swarm") try: return client except KeyError as err: raise err
def add_node(self, node_id, identity, state, timestmp): """ Add a node to the Neo4j database, which involves adding the identity node and also the state node and then creating a relationship between them. :param node_id: The id of the identity node. :param identity: The identity node. :param state: State node. :param timestmp: Epoch timestamp of when the node was created. :return: An instance of the py2neo neo4j node. """ identity = _format_node(identity) identity['name'] = node_id iden_node = Node(identity.get('category', 'UNDEFINED'), **identity) existing_node = self.get_node_by_uuid(node_id) if existing_node: LOG.warn("Node with UUID: %s already stored in DB", node_id) return existing_node # Store nodes to the database. transaction = self.graph_db.begin() state = _format_node(state) state_label = identity.get('category', 'UNDEFINED') + '_state' state_node = Node(state_label, **state) state_rel = self._create_edge(iden_node, state_node, timestmp, "STATE") transaction.create(iden_node) transaction.create(state_node) transaction.create(state_rel) transaction.commit() return iden_node
def init_graph_db(self): """ Adds all neutron ports, nets and subnets to the graph database. """ LOG.info("[NEUTRON] Adding Neutron components to the landscape.") now_ts = time.time() # Collect Networks networks = self.neutron.list_networks() for net in networks.get('networks', list()): net_id = net.get('id', "UNDEFINED") net_name = net.get('name', "UNDEFINED") self._add_network(net_id, net_name, now_ts) # Collect subnets subnets = self.neutron.list_subnets() for subnet in subnets.get('subnets', list()): subnet_id = subnet.get('id', "UNDEFINED") cidr = subnet.get('cidr', "UNDEFINED") network_id = subnet.get('network_id', "UNDEFINED") self._add_subnet(subnet_id, cidr, network_id, now_ts) # Collect ports ports = self.neutron.list_ports() for port in ports.get('ports', list()): port_id = port.get("id", "UNDEFINED") mac, fixed_ip, device_id, net_id = self._get_port_info(port) self._add_port(port_id, mac, fixed_ip, device_id, net_id, now_ts)
def _remove_physical_machine(self, machine, timestamp): identity = self.graph_db.get_node_by_uuid(machine) if identity: self.graph_db.delete_node(identity, timestamp) LOG.info("Machine : %s deleted from landscape", machine) else: LOG.error("Machine : %s not in the landscape to delete!", machine)
def put_geolocation(): """ Stores the geolocation of the nodes to the database """ LOG.info("Accessing URL %s", request.url) now_ts = time.time() error_log = [] if not request.data: err_msg = "No coordinate data" abort(400, err_msg) data = ast.literal_eval(request.data) for obj in data: LOG.info("Updating coordinates of nodes %s", obj['id']) geo_string = json.dumps(obj['geo']) attrs = {"geo": geo_string} updated, msg = LANDSCAPE.graph_db.update_node(obj['id'], now_ts, extra_attrs=attrs) if not updated: error_log.append((obj["id"], msg)) if error_log: err_msg = "Error with the following nodes:" + str(error_log) abort(400, err_msg) return Response(status=200, mimetype=MIME)
def _get_session_keystone_v3(): """ Returns a keystone session variable. """ from keystoneauth1.identity import v3 from keystoneauth1 import session from keystoneclient.v3 import client user, password, auth_uri, project_name, project_id, user_domain_name = _get_connection_info( '3') auth = v3.Password(auth_url=auth_uri, username=user, password=password, project_id=project_id, user_domain_name=user_domain_name) envs = [ user, password, auth_uri, project_name, project_id, user_domain_name ] msg = "AUTH with user ({e[0]}), password (****), auth_uri ({e[2]}), " \ " project_name ({e[3]}), project_id ({e[4]}) " \ "and user_domain_name ({e[5]}).".format(e=envs) LOG.info(msg) sess = session.Session(auth=auth) return sess
def _check_conn_variables(user, password, auth_uri, project_name, project_id, user_domain_name, keystone_ver): """ Check that the environment variables have been found. Without connection variables to the openstack testbed it is impossible to build a landscape any openstack components and so an exception is thrown. :param user: Username. :param password: Password. :param auth_uri: URI to the Openstack testbed. :param project_name: Tenant Name. :param project_id: Project ID. :param user_domain_name: User domain name. """ envs = [ OS_USERNAME, OS_PASSWORD, OS_PROJECT_NAME, OS_PROJECT_ID, OS_AUTH_URL, OS_USER_DOMAIN_NAME ] msg = "" if keystone_ver == '3': #print [user, password, auth_uri, project_name, project_id, user_domain_name] if not user or not password or not auth_uri or not project_name or not project_id or not user_domain_name: msg = "Environment variables {e[0]}, {e[1]}, {e[2]}, {e[3]}, {e[4]} " \ "and {e[5]} are required".format(e=envs) else: #print [user, password, auth_uri, project_name] if not user or not password or not auth_uri or not project_name: msg = "Environment variables {e[0]}, {e[1]}, {e[2]} and {e[3]} are required".format( e=envs) if len(msg) > 0: LOG.error(msg) raise ValueError(msg)
def init_graph_db(self): """ Adds the instances to the graph database and connects them to the relevant machine nodes. """ LOG.info( "ContainerCollector - Adding Docker infrastructure components to the landscape." ) now_ts = time.time() nodes = [ x for x in self.swarm_manager.nodes.list() if x.attrs["Status"]["State"] == 'ready' ] for node in nodes: node_id = node.attrs["ID"] hostname = node.attrs['Description']['Hostname'] if 'ManagerStatus' in node.attrs: addr = node.attrs['ManagerStatus']['Addr'] else: addr = node.attrs['Status']['Addr'] state_attributes = self._get_instance_info(node) self._add_instance(node_id, addr, hostname, state_attributes, now_ts) LOG.info( "ContainerCollector - Docker infrastructure components added.")
def _add_task(self, task, timestamp): """ Adds a Docker task node to the graph database. :param task: Docker task object. :param timestamp: timestamp. """ LOG.info("[DOCKER] Adding a task node the Graph") if task['DesiredState'] == 'running': identity, state = self._create_docker_task_nodes(task) uuid = task["ID"] node_id = task["NodeID"] container_id = task["Status"]['ContainerStatus']['ContainerID'] service_id = task["ServiceID"] task_node = self.graph_db.add_node(uuid, identity, state, timestamp) LOG.warn(task_node) if task_node is not None: docker_node = self.graph_db.get_node_by_uuid(node_id) container_node = self.graph_db.get_node_by_uuid(container_id) service_node = self.graph_db.get_node_by_uuid(service_id) if docker_node and container_node: self.graph_db.add_edge(container_node, docker_node, timestamp, RELS['docker_container']) if container_node and task_node: self.graph_db.add_edge(task_node, container_node, timestamp, RELS['container_task']) if task_node and service_node: self.graph_db.add_edge(service_node, task_node, timestamp, RELS['task_service'])
def update_graph_db(self, event, body): """ Updates the heat elements in the graph database. :param event: The event that has occurred. :param body: The details of the event that occurred. """ from heatclient.exc import NotFound LOG.info("[HEAT] Processing event received: %s", event) now_ts = time.time() uuid = body.get('payload', dict()).get('stack_identity', 'UNDEFINED') if '/' in uuid: uuid = uuid.rsplit('/', 1)[1] try: stack = self.heat.stacks.get(uuid) if event in ADD_EVENTS: LOG.info("HEAT: Adding stack: %s", stack) self._add_stack(stack, now_ts) elif event in UPDATE_EVENTS: LOG.info("HEAT: Updating stack: %s", stack) self._update_stack(stack, now_ts) elif event in DELETE_EVENTS: LOG.info("HEAT: deleting stack: %s", stack) self._delete_stack(uuid, now_ts) except NotFound: LOG.warn("HEAT: Stack with UUID %s not found", uuid)
def update_graph_db(self, event, body): """ Updates, adds and deletes cinder volumes based on the event type. :param event: Event type. :param body: Event details. """ LOG.info("[CINDER] Cinder event received: %s.", event) now_ts = time.time() uuid = body.get("payload", dict()).get("volume_id", "UNDEFINED") size = body.get("payload", dict()).get("size", "UNDEFINED") hostname = body.get("payload", dict()).get("host", "UNDEFINED") if hostname: if "#" in hostname: hostname = hostname.split("#")[0] if "@" in hostname: hostname = hostname.split('@')[0] attachments = body.get("payload", dict()).get('volume_attachment', []) vm_id = "UNDEFINED" for attachment in attachments: attach_status = attachment.get("attach_status", "UNDEFINED") if attach_status == "attached": vm_id = attachment.get('instance_uuid', "UNDEFINED") if event in DELETE_EVENTS: self._delete_volume(uuid, now_ts) elif event in UPDATE_EVENTS: self._update_volume(uuid, size, hostname, vm_id, now_ts) elif event in ADD_EVENTS: self._add_volume(uuid, size, hostname, vm_id, now_ts)
def init_graph_db(self): LOG.info("[PHYS NETWORK] Adding physical network.") net_description = self._network_description(paths.NETWORK_DESCRIPTION) # Use two loops for inter switch connections. for switch, switch_info in net_description.iteritems(): self._add_switch(switch, switch_info, time.time()) for switch, switch_info in net_description.iteritems(): self._connect_switches(switch, switch_info, time.time())
def on_connection_revived(self): """ Method called when a connection to the broker is successfully made. """ super(OSMQueueConsumer, self).on_connection_revived() self.retry_tracker = 0 info_msg = "Connected to Openstack message queue at address: %s." LOG.info(info_msg, self.connection.as_uri())
def init_graph_db(self): """ Add Volume nodes to the landscape. """ LOG.info("[CINDER] Adding Cinder components to the landscape.") now_ts = time.time() for volume in self.cinder.volumes.list(): volume_id, size, hostname, vm_id = self._get_volume_info(volume) self._add_volume(volume_id, size, hostname, vm_id, now_ts)
def update_graph_db(self, event, body): """ Updates instances. This method is called by the events manager. :param event: The event that has occurred. :param body: The details of the event that occurred. """ LOG.info("Processing event received: %s", event) now_ts = time.time() self._process_event(now_ts, event, body)
def init_graph_db(self): """ Build the physical layer machines and constituent components and add to the graph database. """ LOG.info("Adding physical machines to the landscape...") now_ts = time.time() machines = self.conf_mgr.get_machines() self._add_physical_machine_threads(machines, now_ts) LOG.info("Finished adding physical machines to the landscape.")
def init_graph_db(self): """ Adds the instances to the graph database and connects them to the relevant machine nodes. """ LOG.info("[EDISK] Adding ephemeral_disk components to the landscape.") now_ts = time.time() self._retrieve_instance_disks() for instance_id, disk_obj in self.instance_disks.iteritems(): self.attach_disk_to_instance(instance_id, disk_obj, now_ts)
def _add_service(self, service, timestamp): """ Adds a Docker service node to the graph database. :param service: Docker Service object. :param timestamp: timestamp. """ LOG.info("[DOCKER] Adding a service node the Graph") identity, state = self._create_docker_service_nodes(service) uuid = service.attrs["ID"] # WHY IS THIS DIFFERENT TO CONT? WHY DOCKER WHY? service_node = self.graph_db.add_node(uuid, identity, state, timestamp)
def init_graph_db(self): """ Adds the instances to the graph database and connects them to the relevant machine nodes. """ LOG.info("[NOVA] Adding Nova components to the landscape.") now_ts = time.time() for instance in self.nova.servers.list(): vcpus, mem, name, hostname = self._get_instance_info(instance) self._add_instance(instance.id, vcpus, mem, name, hostname, now_ts)
def _connect_switches(self, switch_id, switch_info, timestamp): switch = self.graph_db.get_node_by_uuid(switch_id) for dev_id in switch_info.get('connected-devices', []): device = self._the_node(dev_id) if device: self.graph_db.add_edge(device, switch, timestamp, "COMMUNICATES") else: LOG.warning("Couldn't connect device '%s' to switch '%s'", dev_id, switch_id)
def _consume_notifications(self): """ Consume notification from Swarm notification queue. """ LOG.info("Attempting to connect to address: %s", self.connection_string) client = self._get_leader_client() for event in client.events(): self._cb_event(event) return
def init_graph_db(self): """ Adds stack nodes to the graph database and connects them to the stack's vms. """ LOG.info("[HEAT] Adding Heat components to the landscape.") now_ts = time.time() for stack in self.heat.stacks.list(): if stack.stack_status == 'CREATE_COMPLETE': self._add_stack(stack, now_ts)
def on_connection_error(self, exc, interval): """ Method called when there is a connection problem. :param exc: The connection exception. :param interval: The interval until the next reconnect attempt is made. """ super(OSMQueueConsumer, self).on_connection_error(exc, interval) self.retry_tracker += 1 err_msg = "Broker connection error: %s. Attempting reconnect " \ "(%s/%s) in %ss." LOG.warning(err_msg, exc, self.retry_tracker, self.connect_max_retries, interval)
def listen_for_events(self): """ Entry point for the child event listener. """ msg = "Connecting to Openstack message queue at address: %s." with Connection(self.connection_string) as conn: consumer = OSMQueueConsumer(conn, self._queues(), self._cb_event) try: LOG.info(msg, self.connection_string) consumer.run() except exceptions.KombuError as exc: LOG.error(exc, exc_info=1)
def generate_files(self, device, dynamic={}): """ Queries the hwloc and cpuinfo methods and writes them to a file :param device: CIMI Device object containing hwloc and cpu_info methods :param dynamic: CIMI device-dynamic object pertaining to the device object :return: True if file successfully saved and hostname, False if errors encountered """ hostname = "" device_id = device['id'] try: hwloc = device.get("hwloc") if hwloc is None: LOG.error("hwLoc data has not been set for this device: " + device_id + ". No HwLoc file will be saved.") return False cpu_info = device.get("cpuinfo") if cpu_info is None: LOG.error("CPU_info data has not been set for this device: " + device_id + ". No CPU_info file will be saved.") if dynamic: hwloc, hostname = self._parse_hwloc(device, hwloc, dynamic) LOG.info("Dynamic data has been set for this device: " + device_id) else: hwloc, hostname = self._parse_hwloc(device, hwloc) LOG.error("Dynamic data has not been set for this device: " + device_id + ". No dynamic file will be saved.") self.device_dict[device_id] = hostname # save the dynamic info to file if dynamic: dynamic_path = os.path.join(paths.DATA_DIR, hostname + "_dynamic.add") self._write_to_file(dynamic_path, json.dumps(dynamic)) # save the cpu info to file if cpu_info: cpu_path = os.path.join(paths.DATA_DIR, hostname + "_cpuinfo.txt") self._write_to_file(cpu_path, cpu_info) # save the hwloc to file hwloc_path = os.path.join(paths.DATA_DIR, hostname + "_hwloc.xml") self._write_to_file(hwloc_path, hwloc) except Exception as ex: LOG.error( "General Error hwloc/cpuinfo for device: {} - Error message: {}" .format(device['id'], ex.message)) return False, None return True, hostname
def _consume_notifications(self): """ Consume notification from Swarm notification queue. """ LOG.info("Subscribing to Docker events...") client = self._get_leader_client() for event in client.events(): LOG.info(event) self._cb_event(event) return