def _process_loop(cls, parent_pid): """Child process loop which handles start/stop events and exceptions.""" with _child_process_wrapper(parent_pid, cls.process_name, cls.device_name, cls._exception_queue): gdm_logger.initialize_child_process_logging(cls.logging_queue) try: cls._start_event.set() except Exception as err: stack_trace = traceback.format_exc() gdm_logger.get_logger().info( "Device {} Process {} error {!r} start event error. {}".format( cls.device_name, cls.process_name, err, stack_trace)) running = cls._pre_run_hook() while running and _parent_is_alive(parent_pid): try: if cls._terminate_event.is_set(): cls._terminate_event.clear() break except IOError: # manager shutdown break running = cls._do_work() try: cls._stop_event.set() except IOError: # manager shutdown pass cls._post_run_hook()
def _process_wrapper(return_queue: multiprocessing.Queue, error_queue: multiprocessing.Queue, logging_queue: multiprocessing.Queue, process_id: str, extension_package_import_paths: Sequence[str], call_spec: CallSpec) -> None: """Executes the provided function in a parallel process.""" gdm_logger.initialize_child_process_logging(logging_queue) short_description = f"{call_spec.function.__name__} in process {os.getpid()}" logger = gdm_logger.get_logger() logger.debug(f"{short_description}: starting execution of {call_spec}...") # The state of the main process (such as registered extension packages) is not # copied over when using "forkserver" or "spawn" as the process start method. for import_path in extension_package_import_paths: try: package_registrar.register(importlib.import_module(import_path)) except (ImportError, errors.PackageRegistrationError) as e: logger.debug( f"{short_description}: failed to import and register GDM " f"extension package with import path {import_path}. " f"Error: {e!r}. Proceeding despite the failure.") manager_inst = manager.Manager() try: return_value = call_spec.function(manager_inst, *call_spec.args, **call_spec.kwargs) logger.debug(f"{short_description}: execution succeeded. " f"Return value: {return_value}.") return_queue.put((process_id, return_value)) except Exception as e: # pylint: disable=broad-except logger.warning( f"{short_description}: execution raised an error: {e!r}.") error_queue.put((process_id, (type(e).__name__, str(e)))) finally: manager_inst.close()
def stop(self): """Stops process if process is running. Raises: RuntimeError: if called when process is not currently running OSError: if a system related exception occurs. Note: When calling this method you should use is_started() to check if process was previously started to prevent raising an error. """ if self.is_started(): if self._process.is_alive(): try: self._terminate_event.set() except IOError: # manager shutdown pass try: stop_event_value = self._stop_event.wait(timeout=5) if not stop_event_value: msg = ("Device {} failed to stop child process {}. " "Stop event was not set.").format( self.device_name, self.process_name) gdm_logger.get_logger().error(msg) raise IOError(msg) except (IOError, ValueError): # manager shutdown failed pass self._process.join(timeout=1) if self._process.is_alive(): self._process.terminate() delattr(self, "_process") else: msg = ( "Device {} failed to stop child process {}. Child process is not " "currently running.").format(self.device_name, self.process_name) gdm_logger.get_logger().error(msg) raise RuntimeError(msg)
These are settable properties, usually initialized as None. They allow users to enable additional capabilities or set properties undetectable via device communication. For example, power_switch_port to enable power cycling via a powerswitch or an alias. @decorators.OptionalProperty def alias(self): """ import functools import inspect import logging import time from gazoo_device import errors from gazoo_device import gdm_logger logger_gdm = gdm_logger.get_logger() # Enable specifying logger levels by decorators.<LEVEL> (so users don't have to import logging) NONE = None DEBUG = logging.DEBUG INFO = logging.INFO WARNING = logging.WARNING ERROR = logging.ERROR CRITICAL = logging.CRITICAL MESSAGES = { "START": "{device_name} starting {class_name}.{method_name}", "SKIP": "{device_name} {class_name}.{method_name} skipped. {skip_reason}.", "SUCCESS":
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Default implementation of the PwRPC (Pigweed RPC) lighting capability.""" from typing import Any, Callable, Optional from gazoo_device import decorators from gazoo_device import errors from gazoo_device import gdm_logger from gazoo_device.capabilities.interfaces import pwrpc_light_base from gazoo_device.protos import lighting_service_pb2 from gazoo_device.switchboard.transports import pigweed_rpc_transport from gazoo_device.utility import pwrpc_utils _LIGHTING_COLOR_PROTO_CLASS = "gazoo_device.protos.lighting_service_pb2.LightingColor" logger = gdm_logger.get_logger() class PwRPCLightDefault(pwrpc_light_base.PwRPCLightBase): """Pigweed RPC lighting capability for devices communicating over PwRPC.""" def __init__(self, device_name: str, switchboard_call: Callable[..., Any], rpc_timeout_s: int): """Creates an instance of the PwRPCLightDefault capability. Args: device_name: Device name used for logging. switchboard_call: The switchboard.call method. rpc_timeout_s: Timeout (s) for RPC call.
# distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Module for log parser.""" import codecs import os import time from gazoo_device import errors from gazoo_device import gdm_logger from gazoo_device.switchboard import data_framer from gazoo_device.switchboard import log_process logger = gdm_logger.get_logger("log_parser") DISPLAY_REFRESH = 3.0 # secs class LogParser(object): """Provides ability to search for specific events in log file.""" def __init__(self, parser_obj, log_path, display_refresh=DISPLAY_REFRESH): """Initialize LogParser class using provided information. Args: parser_obj (Parser): Instance of class Parser log_path (str): Path to log filename containing raw, log event data display_refresh (float): Number of seconds to wait prior to refresh of display
NOTE: the WebSocket protocol is NOT the same thing as a TCP/IP socket. The WebSocket protocol is an application layer (layer 7) protocol. See https://tools.ietf.org/html/rfc6455. TCP socket is a transport layer (layer 4) protocol. For a transport based on a TCP socket, see tcp_transport.py. """ import traceback from gazoo_device import gdm_logger from gazoo_device.switchboard import transport_properties from gazoo_device.switchboard.transports import tcp_transport from gazoo_device.switchboard.transports import transport_base import websocket logger = gdm_logger.get_logger(__file__) class WebSocketTransport(transport_base.TransportBase): """WebSocket-based transport.""" def __init__(self, comms_address, connect_timeout=tcp_transport.CONNECT_TIMEOUT, auto_reopen=False, open_on_start=True): """Initialize the WebSocketTransport. Args: comms_address (str): websocket URL to connect to (ws://...) connect_timeout (float): timeout on the socket instance before
def target(logging_queue): gdm_logger.initialize_child_process_logging(logging_queue) logger = gdm_logger.get_logger() logger.info("I'm a child process with PID {}".format(os.getpid()))
import logging import os from typing import Any, Dict, List, Sequence from gazoo_device import custom_types from gazoo_device import errors from gazoo_device import gdm_logger from gazoo_device import manager # Device properties for gazoo device analytics. _DEVICE_PROPS = ("name", "device_type", "model", "platform", "serial_number", "wifi_mac_address", "firmware_version", "firmware_branch", "firmware_type", "alias", "communication_address", "secondary_communication_address", "build_date", "initial_code_name") _LOGGER = gdm_logger.get_logger() _MANAGER_INSTANCE = None def _set_auxiliary_props(properties: Dict[str, Any], device_name: str): """Sets props on device instance to the values in the config. Label is translated to Alias and dimensions are left out. Args: properties: Dictionary of device properties and their values. device_name: Id of the device instance. """ for prop_name, value in properties.items(): if prop_name in ["id", "dimensions"]: continue