def start(self, port, connection_timeout): """ Run the maintenance service, accepts a connection. Starts a serial redirector when a connection is accepted. """ LOGGER.info('Starting maintenance socket on port ' + str(port)) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(connection_timeout) sock = System.get_ssl_socket( sock, private_key_filename=self._privatekey_filename, certificate_filename=self._certificate_filename) sock.bind(('', port)) sock.listen(1) try: LOGGER.info('Waiting for maintenance connection.') connection, addr = sock.accept() self.handle_connection(connection, str(addr)) LOGGER.info( 'Maintenance session ended, closing maintenance socket') sock.close() except socket.timeout: LOGGER.info('Maintenance socket timed out, closing.') sock.close() except Exception: LOGGER.error('Error in maintenance service: %s\n', traceback.format_exc()) sock.close()
def _run_socket_server(self, port): connection_timeout = MaintenanceController.SOCKET_TIMEOUT logger.info('Starting maintenance socket on port %s', port) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(connection_timeout) sock = System.get_ssl_socket( sock, private_key_filename=self._privatekey_filename, certificate_filename=self._certificate_filename) sock.bind(('', port)) sock.listen(1) try: logger.info('Waiting for maintenance connection.') self._connection, address = sock.accept() logger.info('Maintenance connection from %s', str(address)) self._handle_connection() logger.info( 'Maintenance session ended, closing maintenance socket') sock.close() except socket.timeout: logger.info('Maintenance socket timed out, closing.') sock.close() except Exception: logger.exception('Error in maintenance service') sock.close()
def writer(self): """ Reads from the socket and writes to the serial port. """ while not self.__stopped: try: try: data = self.__connection.recv(1024) if not data: LOGGER.info( 'Stopping maintenance mode due to no data.') break if data.startswith('exit'): LOGGER.info('Stopping maintenance mode due to exit.') break self.__gateway_api.send_maintenance_data(data) except Exception as exception: if System.handle_socket_exception(self.__connection, exception, LOGGER): continue else: break except: LOGGER.error('Exception in maintenance mode: %s\n', traceback.format_exc()) break self.__stopped = True self.__reader_thread.join()
def start(self, port, connection_timeout): """ Run the maintenance service, accepts a connection. Starts a serial redirector when a connection is accepted. """ LOGGER.info('Starting maintenance socket on port %s', port) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(connection_timeout) sock = System.get_ssl_socket(sock, private_key_filename=self._privatekey_filename, certificate_filename=self._certificate_filename) sock.bind(('', port)) sock.listen(1) try: LOGGER.info('Waiting for maintenance connection.') connection, addr = sock.accept() self.handle_connection(connection, str(addr)) LOGGER.info('Maintenance session ended, closing maintenance socket') sock.close() except socket.timeout: LOGGER.info('Maintenance socket timed out, closing.') sock.close() except Exception: LOGGER.error('Error in maintenance service: %s\n', traceback.format_exc()) sock.close()
class VpnController(object): """ Contains methods to check the vpn status, start and stop the vpn. """ vpn_service = System.get_vpn_service() start_cmd = "systemctl start " + vpn_service + " > /dev/null" stop_cmd = "systemctl stop " + vpn_service + " > /dev/null" check_cmd = "systemctl is-active " + vpn_service + " > /dev/null" def __init__(self): self.vpn_connected = False t_vpn_connected = Thread(target=self._vpn_connected) t_vpn_connected.daemon = True t_vpn_connected.start() @staticmethod def start_vpn(): """ Start openvpn """ logger.info('Starting VPN') return subprocess.call(VpnController.start_cmd, shell=True) == 0 @staticmethod def stop_vpn(): """ Stop openvpn """ logger.info('Stopping VPN') return subprocess.call(VpnController.stop_cmd, shell=True) == 0 @staticmethod def check_vpn(): """ Check if openvpn is running """ return subprocess.call(VpnController.check_cmd, shell=True) == 0 def _vpn_connected(self): """ Checks if the VPN tunnel is connected """ while True: try: routes = subprocess.check_output( 'ip r | grep tun | grep via || true', shell=True).strip() # example output: # 10.0.0.0/24 via 10.37.0.5 dev tun0\n # 10.37.0.1 via 10.37.0.5 dev tun0 result = False if routes: vpn_servers = [ route.split(' ')[0] for route in routes.split('\n') if '/' not in route ] for vpn_server in vpn_servers: if VPNService.ping(vpn_server, verbose=False): result = True break self.vpn_connected = result except Exception as ex: logger.info( 'Exception occured during vpn connectivity test: {0}'. format(ex)) self.vpn_connected = False time.sleep(5)
def setup_logger(log_level=logging.INFO, enable_update_logging=False): """ Setup the OpenMotics logger. :param log_level: Sets the main log level for OpenMotics logging to the default StreamHandler/SysLogHandler :param enable_update_logging: Enables logging to the `update_log` file. This will always log in DEBUG """ import constants from platform_utils import System # Remove all log handlers (since python2 `defaultConfig` has no `force` flag) root_logger = logging.getLogger() while root_logger.handlers: root_logger.removeHandler(root_logger.handlers[0]) # Setup basic stream handler logging.basicConfig(format=Logs.LOG_FORMAT, level=logging.INFO) openmotics_log_level = log_level # Alter some system loggers requests_logger = logging.getLogger( 'requests.packages.urllib3.connectionpool') requests_logger.setLevel(logging.WARNING) update_handler = None if enable_update_logging: update_handler = handlers.RotatingFileHandler( constants.get_update_log_location(), maxBytes=3 * 1024**2, backupCount=2) update_handler.setLevel(logging.DEBUG) update_handler.setFormatter(logging.Formatter(Logs.LOG_FORMAT)) openmotics_log_level = min(log_level, logging.DEBUG) syslog_handler = None if System.get_operating_system().get('ID') == System.OS.BUILDROOT: syslog_handler = handlers.SysLogHandler(address='/dev/log') syslog_handler.setLevel(log_level) syslog_handler.setFormatter(logging.Formatter(Logs.LOG_FORMAT)) for logger_namespace in ['openmotics', 'gateway']: _logger = logging.getLogger(logger_namespace) _logger.setLevel(openmotics_log_level) _logger.propagate = True for extra_handler in [update_handler, syslog_handler]: # Add extra handlers, where available if extra_handler is not None: _logger.addHandler(extra_handler)
def setup_logger(): """ Setup the OpenMotics logger. """ logger.setLevel(logging.INFO) logger.propagate = False handler = logging.StreamHandler() handler.setLevel(logging.INFO) handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")) logger.addHandler(handler) if System.get_operating_system().get('ID') == System.OS.BUILDROOT: syslog_handler = logging.handlers.SysLogHandler(address='/dev/log') syslog_handler.setLevel(logging.INFO) syslog_handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")) logger.addHandler(syslog_handler)
def _handle_connection(self): """ Handles one incoming connection. """ try: self._connection.settimeout(1) self._connection.sendall( 'Activating maintenance mode, waiting for other actions to complete ...\n' ) self._activate() self._connection.sendall('Connected\n') while self._maintenance_communicator.is_active(): try: try: data = self._connection.recv(1024) if not data: logger.info( 'Stopping maintenance mode due to no data.') break if data.startswith('exit'): logger.info( 'Stopping maintenance mode due to exit.') break self._maintenance_communicator.write(data) except Exception as exception: if System.handle_socket_exception( self._connection, exception, logger): continue else: logger.exception( 'Unexpected exception receiving connection data' ) break except Exception: logger.exception('Exception in maintenance mode') break except InMaintenanceModeException: self._connection.sendall('Maintenance mode already active.\n') finally: self._deactivate() logger.info('Maintenance mode deactivated') self._connection.close() self._connection = None
def update_gateway_backend(tarball, date, version): try: backup_dir = os.path.join(PREFIX, 'backup') python_dir = os.path.join(PREFIX, 'python') etc_dir = os.path.join(PREFIX, 'etc') cmd(['mkdir', '-p', backup_dir]) # TODO: symlink, blue green deployment cmd(['mkdir', '-p', os.path.join(backup_dir, date)]) cmd(['mkdir', '-p', os.path.join(backup_dir, date, 'hex')]) cmd(['mv', python_dir, os.path.join(backup_dir, date)]) cmd(['cp', '-r', etc_dir, os.path.join(backup_dir, date)]) cmd('cp {0} {1} || true'.format(os.path.join(PREFIX, '*.hex'), os.path.join(backup_dir, date, 'hex')), shell=True) # Add the '|| true' to the end of the command to be sure it exits with exit code 0! # Cleanup for old versions. old_dist_dir = os.path.join(PREFIX, 'dist-packages') if os.path.exists(old_dist_dir): cmd(['mv', old_dist_dir, os.path.join(backup_dir, date)]) logger.info('Extracting gateway') cmd(['mkdir', '-p', python_dir]) run_tarball_extract(tarball, python_dir) cmd(['sync']) plugins = glob.glob('{}/{}/python/plugins/*/'.format(backup_dir, date)) if plugins: logger.info('Restoring plugins') for plugin in plugins: cmd(['cp', '-r', plugin, os.path.join(python_dir, 'plugins')]) logger.info('Running post-update') system_os = System.get_operating_system().get('ID') if system_os != System.OS.BUILDROOT: cmd(['bash', os.path.join(python_dir, 'post-update.sh')]) cmd(['sync']) except Exception as exc: logger.exception('Updating Gateway service failed') return exc
def test_get_ip_address(self): expected_ip_address = "192.168.0.126" platform_specifics = { 'ANGSTROM': { 'operating_system': { 'ID': 'angstrom' }, 'ifconfig_output': ANGSTROM_IFCONFIG_OUTPUT }, 'DEBIAN': { 'operating_system': { 'ID': 'debian' }, 'ifconfig_output': DEBIAN_IFCONFIG_OUTPUT } } for platform, details in platform_specifics.items(): with mock.patch.object(subprocess, 'check_output', return_value=details['ifconfig_output']),\ mock.patch.object(System, 'get_operating_system', return_value=details['operating_system']): ip_address = System.get_ip_address() self.assertEqual(expected_ip_address, ip_address)
class VpnController(object): """ Contains methods to check the vpn status, start and stop the vpn. """ vpn_service = System.get_vpn_service() start_cmd = "systemctl start " + vpn_service + " > /dev/null" stop_cmd = "systemctl stop " + vpn_service + " > /dev/null" check_cmd = "systemctl is-active " + vpn_service + " > /dev/null" def __init__(self): pass @staticmethod def start_vpn(): """ Start openvpn """ return subprocess.call(VpnController.start_cmd, shell=True) == 0 @staticmethod def stop_vpn(): """ Stop openvpn """ return subprocess.call(VpnController.stop_cmd, shell=True) == 0 @staticmethod def check_vpn(): """ Check if openvpn is running """ return subprocess.call(VpnController.check_cmd, shell=True) == 0 @staticmethod def vpn_connected(): """ Checks if the VPN tunnel is connected """ try: route = subprocess.check_output('ip r | grep tun | grep via || true', shell=True).strip() if not route: return False vpn_server = route.split(' ')[0] return ping(vpn_server) except Exception as ex: LOGGER.log('Exception occured during vpn connectivity test: {0}'.format(ex)) return False
def writer(self): """ Reads from the socket and writes to the serial port. """ while not self.__stopped: try: try: data = self.__connection.recv(1024) if not data: LOGGER.info('Stopping maintenance mode due to no data.') break if data.startswith('exit'): LOGGER.info('Stopping maintenance mode due to exit.') break self.__gateway_api.send_maintenance_data(data) except Exception as exception: if System.handle_socket_exception(self.__connection, exception, LOGGER): continue else: break except Exception: LOGGER.error('Exception in maintenance mode: %s\n', traceback.format_exc()) break self.__stopped = True self.__reader_thread.join()
def start( master_controller=INJECTED, # type: MasterController maintenance_controller=INJECTED, # type: MaintenanceController power_communicator=INJECTED, # type: PowerCommunicator power_serial=INJECTED, # type: RS485 metrics_controller=INJECTED, # type: MetricsController passthrough_service=INJECTED, # type: PassthroughService scheduling_controller=INJECTED, # type: SchedulingController metrics_collector=INJECTED, # type: MetricsCollector web_service=INJECTED, # type: WebService web_interface=INJECTED, # type: WebInterface watchdog=INJECTED, # type: Watchdog plugin_controller=INJECTED, # type: PluginController event_sender=INJECTED, # type: EventSender thermostat_controller=INJECTED, # type: ThermostatController output_controller=INJECTED, # type: OutputController input_controller=INJECTED, # type: InputController pulse_counter_controller=INJECTED, # type: PulseCounterController sensor_controller=INJECTED, # type: SensorController shutter_controller=INJECTED, # type: ShutterController group_action_controller=INJECTED, # type: GroupActionController frontpanel_controller=INJECTED, # type: FrontpanelController module_controller=INJECTED, # type: ModuleController user_controller=INJECTED, # type: UserController ventilation_controller=INJECTED, # type: VentilationController pubsub=INJECTED # type: PubSub ): """ Main function. """ logger.info('Starting OM core service...') # MasterController should be running master_controller.start() # Sync ORM with sources of thruth output_controller.run_sync_orm() input_controller.run_sync_orm() pulse_counter_controller.run_sync_orm() sensor_controller.run_sync_orm() shutter_controller.run_sync_orm() # Execute data migration(s) # TODO: Make the master communication work before executing the migrations (needs eeprom use or other) if not System.get_operating_system().get('ID') == System.OS.BUILDROOT: FeatureMigrator.migrate() RoomsMigrator.migrate() InputMigrator.migrate() ScheduleMigrator.migrate() UserMigrator.migrate() ConfigMigrator.migrate() # Start rest of the stack maintenance_controller.start() if power_communicator: power_serial.start() power_communicator.start() metrics_controller.start() if passthrough_service: passthrough_service.start() scheduling_controller.start() user_controller.start() module_controller.start() thermostat_controller.start() ventilation_controller.start() metrics_collector.start() web_service.start() if frontpanel_controller: frontpanel_controller.start() event_sender.start() watchdog.start() plugin_controller.start() output_controller.start() input_controller.start() pulse_counter_controller.start() sensor_controller.start() shutter_controller.start() group_action_controller.start() pubsub.start() web_interface.set_service_state(True) signal_request = {'stop': False} def stop(signum, frame): """ This function is called on SIGTERM. """ _ = signum, frame logger.info('Stopping OM core service...') watchdog.stop() output_controller.stop() input_controller.stop() pulse_counter_controller.stop() sensor_controller.stop() shutter_controller.stop() group_action_controller.stop() web_service.stop() if power_communicator: power_communicator.stop() master_controller.stop() maintenance_controller.stop() metrics_collector.stop() metrics_controller.stop() user_controller.stop() ventilation_controller.start() thermostat_controller.stop() plugin_controller.stop() if frontpanel_controller: frontpanel_controller.stop() event_sender.stop() pubsub.stop() logger.info('Stopping OM core service... Done') signal_request['stop'] = True try: import prctl prctl.set_name('omservice') except ImportError: pass signal(SIGTERM, stop) logger.info('Starting OM core service... Done') while not signal_request['stop']: time.sleep(1)
def get_local_ip_address(self): """ Get the local ip address. """ _ = self # Needs to be an instance method return System.get_ip_address()
# GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """ Tool to bootload the slave modules (output, dimmer, input and temperature). @author: fryckbos """ import argparse from ConfigParser import ConfigParser from serial import Serial from platform_utils import System System.import_eggs() import constants import master.master_api as master_api from master.master_communicator import MasterCommunicator from master.eeprom_controller import EepromFile, EepromAddress import intelhex import time import sys import traceback def create_bl_action(cmd, input): """ Create a bootload action, this uses the command and the inputs to calculate the crc for the action, the crc is added to input.
# License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """ The main module for the OpenMotics """ from __future__ import absolute_import from platform_utils import System System.import_libs() import constants import logging import logging.handlers import time from signal import SIGTERM, signal from bus.om_bus_client import MessageClient from bus.om_bus_service import MessageService from gateway.initialize import initialize from gateway.migrations.rooms import RoomsMigrator from gateway.migrations.features_data_migrations import FeatureMigrator from gateway.migrations.inputs import InputMigrator from gateway.migrations.schedules import ScheduleMigrator from gateway.migrations.users import UserMigrator
def start_services(): for service in SUPERVISOR_SERVICES: try: cmd_wait_output(System.run_service_action('start', service)) except Exception: logger.warning('Starting {} failed'.format(service))
def _restart(): # type: () -> None logger.info('Restarting for factory reset...') System.restart_service('openmotics')
def stop_services(): for service in SUPERVISOR_SERVICES: proc = System.run_service_action('stop', service) proc.wait()
def check_services(): for service in SUPERVISOR_SERVICES: status_output, _ = cmd_wait_output(System.run_service_action('status', service)) if 'no such process' in status_output.decode('utf-8').lower(): raise Exception('Could not find service "{}"'.format(service))
# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """ The vpn_service asks the OpenMotics cloud it a vpn tunnel should be opened. It starts openvpn if required. On each check the vpn_service sends some status information about the outputs and thermostats to the cloud, to keep the status information in the cloud in sync. """ from platform_utils import System System.import_eggs() import os import glob import gobject import sys import requests import time import subprocess import traceback import constants import threading from collections import deque from ConfigParser import ConfigParser from datetime import datetime
class VpnController(object): """ Contains methods to check the vpn status, start and stop the vpn. """ if System.get_operating_system().get('ID') == System.OS.BUILDROOT: vpn_binary = 'openvpn' config_location = '/etc/openvpn/client/' start_cmd = 'cd {} ; {} --suppress-timestamps --nobind --config vpn.conf > /dev/null'.format( config_location, vpn_binary) stop_cmd = 'killall {} > /dev/null'.format(vpn_binary) check_cmd = 'ps -a | grep {} | grep -v "grep" > /dev/null'.format( vpn_binary) else: vpn_service = System.get_vpn_service() start_cmd = 'systemctl start {0} > /dev/null'.format(vpn_service) stop_cmd = 'systemctl stop {0} > /dev/null'.format(vpn_service) check_cmd = 'systemctl is-active {0} > /dev/null'.format(vpn_service) def __init__(self): self.vpn_connected = False self._vpn_tester = DaemonThread(name='vpnctl', target=self._vpn_connected, interval=5) def start(self): self._vpn_tester.start() @staticmethod def start_vpn(): """ Start openvpn """ logger.info('Starting VPN') return subprocess.call(VpnController.start_cmd, shell=True) == 0 @staticmethod def stop_vpn(): """ Stop openvpn """ logger.info('Stopping VPN') return subprocess.call(VpnController.stop_cmd, shell=True) == 0 @staticmethod def check_vpn(): """ Check if openvpn is running """ return subprocess.call(VpnController.check_cmd, shell=True) == 0 def _vpn_connected(self): """ Checks if the VPN tunnel is connected """ try: routes = subprocess.check_output( 'ip r | grep tun | grep via || true', shell=True).strip() # example output: # 10.0.0.0/24 via 10.37.0.5 dev tun0\n # 10.37.0.1 via 10.37.0.5 dev tun0 result = False if routes: if not isinstance( routes, str): # to ensure python 2 and 3 compatibility routes = routes.decode() vpn_servers = [ route.split(' ')[0] for route in routes.split('\n') if '/' not in route ] for vpn_server in vpn_servers: if TaskExecutor._ping(vpn_server, verbose=False): result = True break self.vpn_connected = result except Exception as ex: logger.info( 'Exception occured during vpn connectivity test: {0}'.format( ex)) self.vpn_connected = False