buffet_config.py revision a2b3d9fee5ae16d22819168daa5d1e2df6e63e0d
1# Copyright 2015 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import dbus
6import logging
7import random
8import string
9import time
10
11from autotest_lib.client.common_lib import error
12from autotest_lib.client.common_lib import utils
13from autotest_lib.client.common_lib.cros import dbus_send
14from autotest_lib.client.common_lib.cros.fake_device_server import oauth
15from autotest_lib.client.common_lib.cros.fake_device_server import server
16
17TEST_CONFIG_PATH = '/tmp/buffet.fake.conf'
18TEST_STATE_PATH = '/tmp/buffet.fake.state'
19
20SERVICE_NAME = 'org.chromium.Buffet'
21
22COMMAND_INTERFACE = 'org.chromium.Buffet.Command'
23MANAGER_INTERFACE = 'org.chromium.Buffet.Manager'
24MANAGER_OBJECT_PATH = '/org/chromium/Buffet/Manager'
25OBJECT_MANAGER_PATH = '/org/chromium/Buffet'
26
27MANAGER_PROPERTY_STATUS = 'Status'
28MANAGER_PROPERTY_DEVICE_ID = 'DeviceId'
29STATUS_UNCONFIGURED = 'unconfigured'
30STATUS_CONNECTING = 'connecting'
31STATUS_CONNECTED = 'connected'
32STATUS_INVALID_CREDENTIALS = 'invalid_credentials'
33
34TEST_MESSAGE = 'Hello world!'
35
36LOCAL_SERVER_PORT = server.PORT
37LOCAL_OAUTH_URL = 'http://localhost:%d/%s/' % (LOCAL_SERVER_PORT,
38                                               oauth.OAUTH_PATH)
39LOCAL_SERVICE_URL = 'http://localhost:%d/' % LOCAL_SERVER_PORT
40TEST_API_KEY = oauth.TEST_API_KEY
41
42def build_unique_device_name():
43    """@return a test-unique name for a device."""
44    RAND_CHARS = string.ascii_lowercase + string.digits
45    NUM_RAND_CHARS = 16
46    rand_token = ''.join([random.choice(RAND_CHARS)
47                          for _ in range(NUM_RAND_CHARS)])
48    name = 'CrOS_%s' % rand_token
49    logging.debug('Generated unique device name %s', name)
50    return name
51
52
53TEST_CONFIG = {
54    'client_id': 'this_is_my_client_id',
55    'client_secret': 'this_is_my_client_secret',
56    'api_key': TEST_API_KEY,
57    'oauth_url': LOCAL_OAUTH_URL,
58    'service_url': LOCAL_SERVICE_URL,
59    'model_id': 'AATST',
60    'wifi_bootstrapping_mode': 'off',
61    'gcd_bootstrapping_mode': 'off',
62    'monitor_timeout_seconds': 120,
63    'connect_timeout_seconds': 60,
64    'bootstrap_timeout_seconds': 300,
65    'name': build_unique_device_name()
66}
67
68
69def bool_to_flag(value):
70    """Converts boolean value into lowercase string
71
72    @param value: Boolean value.
73    @return lower case string: 'true' or 'false'.
74
75    """
76    return ('%s' % value).lower()
77
78
79def format_options(options, separator):
80    """Format dictionary as key1=value1{separator}key2=value2{separator}..
81
82    @param options: Dictionary with options.
83    @param separator: String to be used as separator between key=value strings.
84    @return formated string.
85
86    """
87    return separator.join(['%s=%s' % (k, v) for (k, v) in options.iteritems()])
88
89
90def naive_restart(host=None):
91    """Restart Buffet without configuring it in any way.
92
93    @param host: Host object if we're interested in a remote host.
94
95    """
96    run = utils.run if host is None else host.run
97    run('stop buffet', ignore_status=True)
98    run('start buffet')
99
100
101
102class BuffetConfig(object):
103    """An object that knows how to restart buffet in various configurations."""
104
105    def __init__(self,
106                 log_verbosity=None,
107                 test_definitions_dir=None,
108                 enable_xmpp=False,
109                 enable_ping=True,
110                 disable_pairing_security=True,
111                 device_whitelist=None,
112                 options=None):
113        self.enable_xmpp = enable_xmpp
114        self.log_verbosity = log_verbosity
115        self.test_definitions_dir = test_definitions_dir
116        self.enable_ping = enable_ping
117        self.disable_pairing_security = disable_pairing_security
118        self.device_whitelist = device_whitelist
119        self.options = TEST_CONFIG.copy()
120        if options:
121            self.options.update(options)
122
123
124    def restart_with_config(self,
125                            host=None,
126                            timeout_seconds=10,
127                            clean_state=True):
128        """Restart Buffet with this configuration.
129
130        @param host: Host object if we're interested in a remote host.
131        @param timeout_seconds: number of seconds to wait for Buffet to
132                come up.
133        @param clean_state: boolean True to remove all existing state.
134
135        """
136        run = utils.run if host is None else host.run
137        run('stop buffet', ignore_status=True)
138        flags = {
139            'BUFFET_ENABLE_XMPP': 'true' if self.enable_xmpp else 'false',
140            'BUFFET_CONFIG_PATH': TEST_CONFIG_PATH,
141            'BUFFET_STATE_PATH': TEST_STATE_PATH,
142            'BUFFET_ENABLE_PING': bool_to_flag(self.enable_ping),
143            'BUFFET_DISABLE_SECURITY':
144                    bool_to_flag(self.disable_pairing_security),
145        }
146        if self.log_verbosity:
147            flags['BUFFET_LOG_LEVEL'] = self.log_verbosity
148
149        # Go through this convoluted shell magic here because we need to
150        # create this file on both remote and local hosts (see how run() is
151        # defined).
152        run('cat <<EOF >%s\n%s\nEOF\n' % (TEST_CONFIG_PATH,
153                                          format_options(self.options, '\n')))
154
155        if clean_state:
156            run('echo > %s' % TEST_STATE_PATH)
157            run('chown buffet:buffet %s' % TEST_STATE_PATH)
158
159        if self.test_definitions_dir:
160            flags['BUFFET_TEST_DEFINITIONS_PATH'] = self.test_definitions_dir
161
162        if self.device_whitelist:
163            flags['BUFFET_DEVICE_WHITELIST'] = ','.join(self.device_whitelist)
164
165        run('start buffet %s' % format_options(flags, ' '))
166        start_time = time.time()
167        while time.time() - start_time < timeout_seconds:
168            result = dbus_send.dbus_send(
169                    SERVICE_NAME, MANAGER_INTERFACE, MANAGER_OBJECT_PATH,
170                    'TestMethod', args=[dbus.String(TEST_MESSAGE)],
171                    host=host, tolerate_failures=True)
172            if result and result.response == TEST_MESSAGE:
173                return
174            time.sleep(0.5)
175
176        raise error.TestFail('Buffet failed to restart in time.')
177