1d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu#!/usr/bin/env python
2d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
3d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu# Use of this source code is governed by a BSD-style license that can be
5d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu# found in the LICENSE file.
6d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
7d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport argparse
8d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport dbus
9d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport logging
10d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport os
11d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport signal
12d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport sys
13d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport traceback
14d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
15d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport at_transceiver
16d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport global_state
17d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport modem_configuration
18d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport task_loop
19d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport wardmodem_exceptions as wme
20d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
21d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuimport common
22d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhufrom autotest_lib.client.bin import utils
23d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhufrom autotest_lib.client.common_lib import error
24d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhufrom autotest_lib.client.cros.cellular import net_interface
25d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
26d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh PrabhuSTATE_MACHINE_DIR_NAME = 'state_machines'
27d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
28d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuclass WardModem(object):
29d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    """
30d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    The main wardmodem object that replaces a physical modem.
31d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
32d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    What it does:
33d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        - Loads configuration data.
34d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        - Accepts custom state machines from the test.
35d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        - Builds objects and ties them together.
36d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        - Exposes objects for further customization
37d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
38d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    What it does not do:
39d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        - Tweak the different knobs provided by internal objects that it exposes
40d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu          for further customization.
41d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu          That is the responsibility of the WardModemContext.
42d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        - Care about setting up / tearing down environment.
43d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu          Again, see WardModemContext.
44d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    """
45d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
46d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def __init__(self,
47d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                 replaced_modem = None,
48d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                 state_machines = None,
49d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                 modem_at_port_dev_name = None):
50d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
51d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @param replaced_modem: Name of the modem being emulated. If left None,
52d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                the base modem will be emulated. A list of valid modems can be
53d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                found in the module modem_configuration
54d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
55d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @param state_machines: Objects of subtypes of StateMachine that override
56d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                any state machine defined in the configuration files for the
57d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                same well-known-name.
58d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
59d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @param modem_at_port_dev_name: The full path to the primary AT port of
60d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                the physical modem. This is needed only if we're running in a
61d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                mode where we pass on modemmanager commands to the modem. This
62d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                should be a string of the form '/dev/XXX'
63d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
64d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
65d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._logger = logging.getLogger(__name__)
66d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
67d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if not state_machines:
68d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            state_machines = []
69d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if modem_at_port_dev_name and (
70d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                type(modem_at_port_dev_name) is not str or
71d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                modem_at_port_dev_name[0:5] != '/dev/'):
72d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            raise wme.WardModemSetupException(
73d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                    'Modem device name must be of the form "/dev/XXX", '
74d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                    'where XXX is the udev device.')
75d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
76d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # The modem that wardmodem is intended to replace.
77d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._replaced_modem = replaced_modem
78d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
79d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # Pseudo net interface exported to shill.
80d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._net_interface = net_interface.PseudoNetInterface()
81d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
82d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # The internal task loop object. Readable through a property.
83d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._task_loop = task_loop.TaskLoop()
84d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
85d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # The global state object shared by all state machines.
86d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._state = global_state.GlobalState()
87d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
88d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # The configuration object for the replaced modem.
89d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._modem_conf = modem_configuration.ModemConfiguration(
90d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                replaced_modem)
91d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
92d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._create_transceiver(modem_at_port_dev_name)
93d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._setup_state_machines(state_machines)
94d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
95d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._started = False
96d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
97d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
98d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def start(self):
99d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
100d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Turns on the wardmodem.
101d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
102d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        This call is blocking. It will return after |stop| is called.
103d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
104d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
105d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._logger.info('Starting wardmodem...')
106d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._net_interface.Setup()
107d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._task_loop.start()
108d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
109d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
110d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def stop(self):
111d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
112d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Stops wardmodem and cleanup.
113d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
114d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
115d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # We need to delete a bunch of stuff *before* the task_loop can be
116d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # stopped.
117d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._logger.info('Stopping wardmodem.')
118d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._net_interface.Teardown()
119d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        del self._transceiver
120d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        os.close(self._wm_at_port)
121d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        os.close(self._mm_at_port)
122d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if self._modem_at_port:
123d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            os.close(self._modem_at_port)
124d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self.task_loop.stop()
125d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
126d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
127d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    @property
128d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def modem(self):
129d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
130d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        The physical modem being replaced [read-only].
131d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
132d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @return string representing the replaced modem.
133d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
134d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
135d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return self._replaced_modem
136d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
137d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
138d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    @property
139d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def modem_conf(self):
140d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
141d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        The ModemConfiguration object loaded for the replaced modem [read-only].
142d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
143d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @return A ModemConfiguration object.
144d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
145d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
146d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return self._modem_conf
147d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
148d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    @property
149d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def transceiver(self):
150d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
151d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        The ATTransceiver that will orchestrate communication [read-only].
152d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
153d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @return ATTransceiver object.
154d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
155d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
156d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return self._transceiver
157d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
158d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
159d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    @property
160d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def task_loop(self):
161d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
162d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        The main loop for asynchronous operations [read-only].
163d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
164d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @return TaskLoop object.
165d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
166d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
167d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return self._task_loop
168d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
169d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
170d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    @property
171d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def state(self):
172d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
173d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        The global state object that must by shared by all state machines.
174d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
175d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @return GlobalState object.
176d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
177d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
178d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return self._state
179d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
180d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
181d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    @property
182d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def mm_at_port_pts_name(self):
183d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
184d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Name of the pty terminal to be used by modemmanager.
185d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
186d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @return A string of the form 'pts/X' where X is the pty number.
187d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
188d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
189d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        fullname = os.ttyname(self._mm_at_port)
190d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # fullname is of the form /dev/pts/X where X is a pts number.
191d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # We want to return just the pts/X part.
192d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        assert fullname[0:5] == '/dev/'
193d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return fullname[5:]
194d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
195d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
196d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def _create_transceiver(self, modem_at_port_dev_name):
197d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
198d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Opens a pty pair and initialize ATTransceiver.
199d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
200d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @param modem_at_port_dev_name: The device name of the primary port.
201d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
202d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
203d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._modem_at_port = None
204d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if modem_at_port_dev_name:
205d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            try:
206d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._modem_at_port = os.open(modem_at_port_dev_name,
207d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                              os.O_RDWR)
208d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            except (TypeError, OSError) as e:
209d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                logging.warning('Could not open modem_port |%s|\nError:\n%s',
210d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                modem_at_port_dev_name, e)
211d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
212d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._wm_at_port, self._mm_at_port = os.openpty()
213d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._transceiver = at_transceiver.ATTransceiver(self._wm_at_port,
214d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                                         self._modem_conf,
215d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                                         self._modem_at_port)
216d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
217d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def _setup_state_machines(self, client_machines):
218d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
219d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Creates the state machines looking at sources in the right order.
220d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
221d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @param client_machines: The client provided state machine objects.
222d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
223d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
224d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # A local list of state machines created
225d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        state_machines = []
226d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
227d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # Create the state machines comprising the wardmodem.
228d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # Highest priority is given to the client provided state machines. The
229d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # remaining will be instantiated based on |replaced_modem|.
230d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        for sm in client_machines:
231d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if sm.get_well_known_name() in state_machines:
232d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                raise wme.SetupError('Multiple state machines provided with '
233d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                     'well-known-name |%s|' %
234d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                     sm.get_well_known_name)
235d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            state_machines.append(sm.get_well_known_name())
236d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._transceiver.register_state_machine(sm)
237d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._logger.debug('Added client specified machine {%s --> %s}',
238d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                               sm.get_well_known_name(),
239d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                               sm.__class__.__name__)
240d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # Now instantiate modem specific state machines.
241d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        for sm_module in self._modem_conf.plugin_state_machines:
242d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            sm = self._create_state_machine(sm_module)
243d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if sm.get_well_known_name() not in state_machines:
244d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                state_machines.append(sm.get_well_known_name())
245d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._transceiver.register_state_machine(sm)
246d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.debug(
247d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                        'Added modem specific machine {%s --> %s}',
248d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                        sm.get_well_known_name(),
249d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                        sm.__class__.__name__)
250d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # Finally instantiate generic state machines.
251d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        for sm_module in self._modem_conf.base_state_machines:
252d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            sm = self._create_state_machine(sm_module)
253d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if sm.get_well_known_name() not in state_machines:
254d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                state_machines.append(sm.get_well_known_name())
255d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._transceiver.register_state_machine(sm)
256d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.debug('Added default machine {%s --> %s}',
257d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                   sm.get_well_known_name(),
258d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                   sm.__class__.__name__)
259d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._logger.info('Loaded state machines: %s', str(state_machines))
260d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
261d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # Also setup the fallback state machine
262d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._transceiver.register_fallback_state_machine(
263d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._modem_conf.fallback_machine,
264d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._modem_conf.fallback_function)
265d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
266d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
267d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def _create_state_machine(self, module_name):
268d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
269d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Creates a state machine object given the |module_name|.
270d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
271d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        There is a specific naming convention for these state machine
272d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        definitions. If |module_name| is new_and_shiny_machine, the state
273d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        machine class must be named NewAndShinyMachine.
274d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
275d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @param module_name: The name of the module from which the state machine
276d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                should be created.
277d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
278d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @returns An object of type new_and_shiny_machine.NewAndShinyMachine, if
279d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                it exists.
280d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
281d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @raises WardModemSetupError if |module_name| is malformed or the object
282d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                creation fails.
283d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
284d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
285d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # Obtain the name of the state machine class from module_name.
286d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # viz, convert my_module_name --> MyModuleName
287d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        parts = module_name.split('_')
288d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        parts = [x.title() for x in parts]
289d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        class_name = ''.join(parts)
290d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
291d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._import_state_machine_module_as_sm(module_name)
292d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return getattr(sm, class_name)(
293d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._state,
294d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._transceiver,
295d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._modem_conf)
296d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
297d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
298d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def _import_state_machine_module_as_sm(self, module_name):
299d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        global sm
300d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if module_name == 'call_machine':
301d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            from state_machines import call_machine as sm
302d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        elif module_name == 'call_machine_e362':
303d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            from state_machines import call_machine_e362 as sm
304d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        elif module_name == 'level_indicators_machine':
305d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            from state_machines import level_indicators_machine as sm
306d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        elif module_name == 'modem_power_level_machine':
307d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            from state_machines import modem_power_level_machine as sm
308d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        elif module_name == 'network_identity_machine':
309d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            from state_machines import network_identity_machine as sm
310d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        elif module_name == 'network_operator_machine':
311d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            from state_machines import network_operator_machine as sm
312d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        elif module_name == 'network_registration_machine':
313d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            from state_machines import network_registration_machine as sm
314d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        elif module_name == 'request_response':
315d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            from state_machines import request_response as sm
316d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        else:
317d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            raise wme.WardModemSetupException('Unknown state machine module: '
318d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                              '%s' % module_name)
319d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
320d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
321d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuclass WardModemContext(object):
322d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    """
323d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    Setup wardmodem according to the options provided.
324d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
325d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    This context should be used by everyone to interact with WardModem.
326d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    This context will
327d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    (1) Setup wardmodem, setting the correct options on the internals exposed by
328d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        the wardmodem object.
329d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    (2) Manage the modemmanager instance during the context's lifetime.
330d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
331d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    """
332d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
333d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    MODEMMANAGER_RESTART_TIMEOUT = 60
334d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
335d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def __init__(self, use_wardmodem=True, detach=True, args=None):
336d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
337d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @param use_wardmodem: If False, this context is a no-op. Otherwise, the
338d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                whole wardmodem magic is done.
339d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
340d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @param detach: A bool flag indicating whether wardmodem should be run in
341d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                its own process. If True, |start| will return immediately,
342d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                starting WardModem in its own process; Otherwise, |start| will
343d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                block until |stop| is called.
344d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
345d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @param args: Options to setup WardModem. This is a list of string
346d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                command line arguments accepted by the parser defined in
347d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                |get_option_parser|.
348d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                TODO(pprabhu) Also except a dict of options to ease
349d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                customization in tests.
350d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
351d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
352d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._logger = logging.getLogger(__name__)
353d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._logger.info('Initializing wardmodem context.')
354d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
355d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._use_wardmodem = use_wardmodem
356d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if not self._use_wardmodem:
357d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._logger.info('WardModemContext directed to do nothing. '
358d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                              'All wardmodem actions are no-op.')
359d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._logger.debug('........... Welcome to the real world Neo.')
360d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            return
361d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
362d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._logger.debug('Wardmodem arguments: detach: %s, args: %s',
363d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                           detach, str(args))
364d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
365d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._started = False
366d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._wardmodem = None
367d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._was_mm_running = False
368d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._detach = detach
369d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        option_parser = self._get_option_parser()
370d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
371d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # XXX:HACK For some reason, parse_args picks up argv when the context is
372d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # created by an autotest test. Workaround: stash away the argv.
373d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        argv_stash = sys.argv
374d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        sys.argv = ['wardmodem']
375d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._options = option_parser.parse_args(args)
376d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        sys.argv = argv_stash
377d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
378d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
379d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def __enter__(self):
380d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self.start()
381d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return self
382d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
383d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
384d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def __exit__(self, type, value, traceback):
385d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self.stop()
386d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # Don't supress any exceptions raised in the 'with' block
387d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return False
388d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
389d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
390d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def start(self):
391d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
392d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Start the WardModem, setting up the correct environment.
393d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
394d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        If |detach| was True, this call will return immediately, running
395d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        WardModem in its own process; Otherwise, this call will block and only
396d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return when |stop| is called.
397d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
398d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
399d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if not self._use_wardmodem:
400d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            return
401d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
402d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if self._started:
403d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            raise wme.WardModemSetupException(
404d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                    'Attempted to re-enter an already active wardmodem '
405d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                    'context.')
406d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
407d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._started = True
408d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._wardmodem = WardModem(
409d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._options.modem,
410d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                modem_at_port_dev_name=self._options.modem_port)
411d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if not self._prepare_wardmodem(self._options):
412d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            raise wme.WardModemSetupException(
413d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                    'Contradictory wardmodem setup options detected.')
414d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
415d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._prepare_mm()
416d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
417d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if not self._detach:
418d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._wardmodem.start()
419d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            return
420d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
421d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._logger.debug('Will fork wardmodem process.')
422d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._child = os.fork()
423d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if self._child == 0:
424d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            # Setup a way to stop the child.
425d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            def _exit_child(signum, frame):
426d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.info('Signal handler called with signal %s',
427d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                  signum)
428d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._cleanup()
429d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                os._exit(0)
430d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            signal.signal(signal.SIGINT, _exit_child)
431d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            signal.signal(signal.SIGTERM, _exit_child)
432d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            # In detach mode, all uncaught exceptions raised by wardmodem
433d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            # will be thrown here. Since this is a child process, they will
434d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            # be lost.
435d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            # At least log them before throwing them again, so that we know
436d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            # something went wrong in wardmodem.
437d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            try:
438d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._wardmodem.start()
439d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            except Exception as e:
440d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                traceback.print_exc()
441d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                raise
442d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
443d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        else:
444d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            # Wait here for the child to start before continuing.
445d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            # We will continue once we know that modemmanager process has
446d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            # detected the wardmodem device, and has exported it on its dbus
447d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            # interface.
448d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            utils.poll_for_condition(
449d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                    self._check_for_modem,
450d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                    exception=wme.WardModemSetupException(
451d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                            'Could not cleanly restart modemmanager.'),
452d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                    timeout=self.MODEMMANAGER_RESTART_TIMEOUT,
453d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                    sleep_interval=1)
454d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._logger.debug('Continuing the main process outside '
455d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                               'wardmodem.')
456d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
457d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
458d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def stop(self):
459d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
460d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Stops WardModem, restore environment to its previous state.
461d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
462d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
463d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if not self._use_wardmodem:
464d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            return
465d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
466d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if not self._started:
467d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._logger.warning('No wardmodem instance running! '
468d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                 'Nothing to stop.')
469d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            return
470d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
471d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if self._detach:
472d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._logger.debug('Attempting to kill child wardmodem process.')
473d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if self._child != 0:
474d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                os.kill(self._child, signal.SIGINT)
475d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                os.waitpid(self._child, 0)
476d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._child = 0
477d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._logger.debug('Finished waiting on child wardmodem process '
478d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                               'to finish.')
479d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        else:
480d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._cleanup()
481d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._started = False
482d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
483d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
484d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def _cleanup(self):
485d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # Restore mm before turning off wardmodem.
486d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._restore_mm()
487d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._wardmodem.stop()
488d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._logger.info('Bye, Bye!')
489d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
490d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
491d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def _prepare_wardmodem(self, options):
492d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
493d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Tweaks the internals exposed by WardModem post-creation according to the
494d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        options provided.
495d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
496d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        @param options: is an object returned by argparse.
497d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
498d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
499d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if options.modem:
500d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if options.pass_through_mode:
501d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.warning('Ignoring "modem" in pass-through-mode.')
502d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
503d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if options.at_terminator:
504d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._wardmodem.transceiver.at_terminator = options.at_terminator
505d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
506d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if options.pass_through_mode:
507d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._wardmodem.transceiver.mode = \
508d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                    at_transceiver.ATTransceiverMode.PASS_THROUGH
509d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
510d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if options.bridge_mode:
511d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._wardmodem.transceiver.mode = \
512d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                    at_transceiver.ATTransceiverMode.SPLIT_VERIFY
513d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
514d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if options.modem_port:
515d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if not options.pass_through_mode and not options.bridge_mode:
516d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.warning('Ignoring "modem-port" in normal mode.')
517d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        else:
518d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if options.pass_through_mode or options.bridge_mode:
519d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.error('"modem-port" needed in %s mode.' %
520d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                              'bridge-mode' if options.bridge_mode else
521d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                              'pass-through-mode')
522d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                return False
523d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
524d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if options.fast:
525d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if options.pass_through_mode:
526d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.warning('Ignoring "fast" in pass-through-mode')
527d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            else:
528d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._wardmodem.task_loop.ignore_delays = True
529d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
530d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if options.randomize_delays:
531d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if options.fast:
532d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.warning('Ignoring option "randomize-delays" '
533d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                '"fast" overrides "randomize-delays".')
534d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if options.pass_through_mode:
535d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.warning('Ignoring "randomize-delays" in '
536d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                     'pass-through-mode')
537d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if not options.fast and not options.pass_through_mode:
538d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._wardmodem.task_loop.random_delays = True
539d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
540d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if options.max_randomized_delay:
541d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if (options.fast or not options.randomize_delays or
542d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                options.pass_through_mode):
543d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.warning('Ignoring "max_randomized_delays"')
544d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            else:
545d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._wardmodem.task_loop.max_random_delay_ms = \
546d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                        options.max_randomized_delay
547d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
548d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return True
549d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
550d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
551d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def _prepare_mm(self):
552d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
553d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Starts modemmanager in test mode listening to the WardModem specified
554d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        pty end-point.
555d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
556d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
557d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._was_mm_running = False
558d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        try:
559d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            result = utils.run('pgrep ModemManager')
560d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            if result.stdout:
561d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._was_mm_running = True
562d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        except error.CmdError:
563d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            pass
564d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        try:
565d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            utils.run('initctl stop modemmanager')
566d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        except error.CmdError:
567d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            pass
568d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
569d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        mm_opts = ''
570d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        mm_opts += '--log-level=DEBUG '
571d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        mm_opts += '--timestamps '
572d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        mm_opts += '--test '
573d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        mm_opts += '--debug '
574d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        mm_opts += '--test-plugin=' + self._wardmodem.modem_conf.mm_plugin + ' '
575d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        mm_opts += '--test-at-port="' + self._wardmodem.mm_at_port_pts_name + \
576d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                   '" '
577d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        mm_opts += '--test-net-port=' + \
578d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                   net_interface.PseudoNetInterface.IFACE_NAME + ' '
579d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        result = utils.run('ModemManager %s &' % mm_opts)
580d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        self._logger.debug('ModemManager stderr:\n%s', result.stderr)
581d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
582d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
583d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def _check_for_modem(self):
584d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        bus = dbus.SystemBus()
585d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        try:
586d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            manager = bus.get_object('org.freedesktop.ModemManager1',
587d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                      '/org/freedesktop/ModemManager1')
588d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            imanager = dbus.Interface(manager,
589d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                      'org.freedesktop.DBus.ObjectManager')
590d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            modems = imanager.GetManagedObjects()
591d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        except dbus.exceptions.DBusException as e:
592d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            # The ObjectManager interface on modemmanager is not up yet.
593d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            return False
594d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        # Check if a modem with the test at port has been exported
595d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if self._wardmodem.mm_at_port_pts_name in str(modems):
596d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            return True
597d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        else:
598d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            return False
599d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
600d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
601d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def _restore_mm(self):
602d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
603d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Stops the test instance of modemmanager and restore it to previous
604d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        state.
605d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
606d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
607d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        result = None
608d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        try:
609d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            result = utils.run('pgrep ModemManager')
610d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._logger.warning('ModemManager in test mode still running! '
611d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                 'Killing it ourselves.')
612d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            try:
613d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                utils.run('pkill -9 ModemManager')
614d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            except error.CmdError:
615d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.warning('Failed to kill test ModemManager.')
616d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        except error.CmdError:
617d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            self._logger.debug('As expected: ModemManager in test mode killed.')
618d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        if self._was_mm_running:
619d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            try:
620d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                utils.run('initctl start modemmanager')
621d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            except error.CmdError:
622d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                self._logger.warning('Failed to restart modemmanager service.')
623d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
624d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
625d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    def _get_option_parser(self):
626d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
627d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        Build an argparse parser to accept options from the user/test to tweak
628d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        WardModem post-creation.
629d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
630d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        """
631d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        parser = argparse.ArgumentParser(
632d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                description='Run the wardmodem modem emulator.')
633d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
634d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        modem_group = parser.add_argument_group(
635d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                'Modem',
636d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                'Description of the modem to emulate.')
637d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        modem_group.add_argument(
638d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                '--modem',
639d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                help='The modem to emulate.')
640d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        modem_group.add_argument('--at-terminator',
641d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                                 help='The string terminator to use.')
642d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
643d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        physical_modem_group = parser.add_argument_group(
644d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                'Physical modem',
645d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                'Interaction with the physical modem on-board.')
646d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        physical_modem_group.add_argument(
647d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                '--pass-through-mode',
648d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                default=False,
649d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                nargs='?',
650d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                const=True,
651d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                help='Act as a transparent channel between the modem manager '
652d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                     'and the physical modem. "--modem-port" option required.')
653d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        physical_modem_group.add_argument(
654d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                '--bridge-mode',
655d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                default=False,
656d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                nargs='?',
657d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                const=True,
658d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                help='Should we also setup a bridge with the real modem? Note '
659d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                     'that the responses generated by wardmodem state machines '
660d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                     'take precedence over those received from the physical '
661d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                     'modem. The bridge is used for a soft-verification: A '
662d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                     'warning is generated if the responses do not match. '
663d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                     '"--modem-port" option required.')
664d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        physical_modem_group.add_argument(
665d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                '--modem-port',
666d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                help='The primary port used by the real modem. ')
667d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
668d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        behaviour_group = parser.add_argument_group(
669d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                'Behaviour',
670d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                'Tweak the behaviour of running wardmodem.')
671d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        behaviour_group.add_argument(
672d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                '--fast',
673d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                default=False,
674d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                nargs='?',
675d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                const=True,
676d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                help='Run the emulator with minimum delay between operations.')
677d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        behaviour_group.add_argument(
678d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                '--randomize-delays',
679d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                default=False,
680d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                nargs='?',
681d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                const=True,
682d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                help='Run emulator with randomized delays between operations.')
683d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        behaviour_group.add_argument(
684d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                '--max-randomized-delay',
685d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                type=int,
686d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                help='The maximum randomized delay added between operations in '
687d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                     '"randomize-delays" mode.')
688d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
689d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        return parser
690d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
691d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
692d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu# ##############################################################################
693d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu# Run WardModem as a script.
694d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu# ##############################################################################
695d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu_wardmodem_context = None
696d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
697d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh PrabhuSIGNAL_TO_NAMES_DICT = \
698d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        dict((getattr(signal, n), n)
699d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu             for n in dir(signal) if n.startswith('SIG') and '_' not in n)
700d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
701d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhudef exit_wardmodem_script(signum, frame):
702d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    """
703d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    Signal handler to intercept Keyboard interrupt and stop the WardModem.
704d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
705d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    @param signum: The signal that was sent to the script
706d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
707d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    @param frame: Current stack frame [ignored].
708d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
709d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    """
710d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    global _wardmodem_context
711d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    if signum == signal.SIGINT:
712d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        logging.info('Captured Ctrl-C. Exiting wardmodem.')
713d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        _wardmodem_context.stop()
714d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    else:
715d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        logging.warning('Captured unexpected signal: %s',
716d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                        SIGNAL_TO_NAMES_DICT.get(signum, str(signum)))
717d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
718d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
719d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhudef main():
720d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    """
721d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    Entry function to wardmodem script.
722d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
723d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    """
724d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    global _wardmodem_context
725d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    # HACK: I should not have logged anything before getting here, but
726d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    # basicConfig wasn't doing anything: So, attempt to clean config.
727d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    root = logging.getLogger()
728d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    if root.handlers:
729d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu        for handler in root.handlers:
730d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu            root.removeHandler(handler)
731d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    logger_format = ('[%(asctime)-15s][%(filename)s:%(lineno)s:%(levelname)s] '
732d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                     '%(message)s')
733d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    logging.basicConfig(format=logger_format,
734d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                        level=logging.DEBUG)
735d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
736d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    _wardmodem_context = WardModemContext(True, False, sys.argv[1:])
737d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    logging.info('\n####################################################\n'
738d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                 'Running wardmodem, hit Ctrl+C to exit.\n'
739d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu                 '####################################################\n')
740d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
741d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    signal.signal(signal.SIGINT, exit_wardmodem_script)
742d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    _wardmodem_context.start()
743d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
744d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu
745d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhuif __name__ == '__main__':
746d618de7330b3564b76ac22d369cbbe162e4b5753Prathmesh Prabhu    main()
747