Source code for caspia.gateway.configurator

# pylint: disable=too-many-instance-attributes
import asyncio
import collections
import logging

import aiopollen

import caspia.node
from caspia.gateway import errors, services
from caspia.gateway.rules import RuleActivationContext, activate_rules
from caspia.node import components
from caspia.node import utils as node_utils
from caspia.toolbox import monitor

logger = logging.getLogger(__name__)


[docs]class Configurator: def __init__(self, hwid_map, network, lookup, loop, work_dir, rules_filter): self.loop = loop self.network = network self.hwid_map = hwid_map self.lookup = lookup self.component_dependencies = dict() self.requested_rules = [] self.active_rules = [] self.active_rules_context = None self.work_dir = work_dir self.lock = asyncio.Lock(loop=self.loop) self.rules_filter = rules_filter self._nodes_configured = False @property def nodes_configured(self): return self._nodes_configured @property def gateway_services(self): return (s for s in self.lookup.services.values() if isinstance(s, services.GatewayService))
[docs] async def prepare_nodes(self): """Make sure all nodes are reachable through known CID.""" for name in self.hwid_map: try: await self._prepare_node(name) except Exception as e: # pylint: disable=broad-except monitor.record_metric('caspia-gateway:nodes-err', 1) logger.error('Failed to prepare node %r: %s', name, repr(e))
async def _prepare_node(self, name): hwid = self.hwid_map[name] logger.debug('Looking for node %s', name) node = self.network.get_node(name) real_hwid = None async def check_node(): nonlocal real_hwid real_hwid = await node.system.read_hwid() if real_hwid == hwid: logger.debug('Node %s is ready with can id %03X', name, node.can_id) else: msg = ('Node %s could not be reached, because node' ' with different HWID has its CAN ID') logger.warning(msg, name) raise errors.CanIDReservationError(msg % name) try: await check_node() except aiopollen.errors.TimeoutError: logger.info('Node %s is not responding. Going to find it by hwid.', name) await node_utils.assign_can_id_by_hwid(hwid, node.can_id, self.network.pollen_client) logger.info('New CAN ID was assigned to %s, checking now.', name) await check_node() except errors.CanIDReservationError: logger.info('Trying to recover: going to change HWID of the' ' conflicting node to 0x1FF and then retry') await node_utils.assign_can_id_by_hwid(real_hwid, 0x1FF, self.network.pollen_client) await node_utils.assign_can_id_by_hwid(hwid, node.can_id, self.network.pollen_client) logger.info('New CAN ID was assigned to %s, checking now.', name) await check_node()
[docs] async def configure(self): logger.info('Configuration requested') with await self.lock: logger.info('Configuration is starting') await self.prepare_nodes() await self._configure_base() await self._configure_rules() await self._apply_configuration() logger.info('Configuration is done') self.network.cidmng.save() self._nodes_configured = True
async def _configure_base(self): """Configure base of all nodes.""" logger.debug('Preparing base configuration') self.network.reset_configuration() for name, node in self.network.nodes.items(): node.drop_components() node.system.config = components.System.Config(self.network.cidmng.cid_of(name)) for service in self.gateway_services: service.configure() self._update_component_dependencies() logger.debug('Base configuration is ready') async def _configure_rules(self): """Add extra configurations (like events on button press etc).""" if self.active_rules_context: await self.active_rules_context.cleanup() self.active_rules_context = RuleActivationContext(self.loop, self, rules_filter=self.rules_filter) self.active_rules = activate_rules(self.requested_rules, self.active_rules_context) logger.debug('Active rules: %s', self.active_rules) async def _apply_configuration(self): """Upload configuration to the nodes.""" for name, node in self.network.nodes.items(): try: was_needed = await node.configure() if was_needed: logger.info('Configuration of %s was updated', name) else: logger.debug('Configuration of %s was up-to-date', name) except caspia.node.errors.InvalidConfigurationError: monitor.record_metric('caspia-gateway:nodes-err', 1) logger.exception('Invalid configuration of %r', name) except aiopollen.errors.TimeoutError: monitor.record_metric('caspia-gateway:nodes-err', 1) logger.exception('Failed to configure %s, node is not responding.', name) except Exception as e: # pylint: disable=broad-except monitor.record_metric('caspia-gateway:nodes-err', 1) logger.exception('Failed to configure %r: %s', name, repr(e)) def _update_component_dependencies(self): component_dependencies = collections.defaultdict(set) for service in self.gateway_services: for component in service.dependant_components: component_dependencies[component].add(service) self.component_dependencies = component_dependencies