| #!/usr/bin/env python |
| |
| |
| # |
| # Copyright (c) 2016-2017 Nest Labs, Inc. |
| # All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # 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. |
| # |
| |
| # |
| # @file |
| # Implements WeaveTime class that tests Weave Time sync among Weave Nodes. |
| # |
| |
| import os |
| import sys |
| import time |
| |
| from happy.ReturnMsg import ReturnMsg |
| from happy.Utils import * |
| from happy.HappyNode import HappyNode |
| from happy.HappyNetwork import HappyNetwork |
| from plugin.WeaveTest import WeaveTest |
| |
| |
| options = { |
| "client": None, |
| "coordinator": None, |
| "server": None, |
| "mode": "auto", |
| "quiet": False, |
| "tap": None, |
| "skip_service_end": False, |
| "skip_coordinator_end": False, |
| "client_faults": False, |
| "server_faults": False, |
| "coordinator_faults": False, |
| "iterations": None, |
| "test_tag": "", |
| "plaid_server_env": {}, |
| "plaid_coordinator_env": {}, |
| "plaid_client_env": {} |
| } |
| |
| gsync_succeeded_str = "Sync Succeeded" |
| |
| def option(): |
| return options.copy() |
| |
| |
| class WeaveTime(HappyNode, HappyNetwork, WeaveTest): |
| """ |
| weave-time [-h --help] [-q --quiet] [-c --client <NAME>] [-o --coordinator <NAME>] |
| [-s --server <NAME>] [-m --mode <MODE>] [-p --tap <TAP_INTERFACE>] |
| [--client_faults <fault-injection configuration>] |
| [--server_faults <fault-injection configuration>] |
| [--coordinator_faults <fault-injection configuration>] |
| |
| commands to test Time profile: |
| $ weave-time -c node01 -o node02 -s node03 -m auto |
| Time sync test among client(node01), coordinator(node02) and server(node03) with Auto mode (multicast) |
| |
| $ weave-time -c node01 -o node02 -s node03 -m local |
| Time sync test among client(node01), coordinator(node02) and server(node03) with Local mode (udp) |
| |
| $ weave-time -c node01 -o node02 -s node03 -m service |
| Time sync test among client(node01), coordinator(node02) and server(node03) with Service mode (tcp) |
| |
| Note: |
| There are four time sync modes: |
| - auto: time sync via Multicast (default) |
| - local: time sync with local nodes via UDP |
| - service: time sync with Service via TCP |
| - service-over-tunnel: time sync with service via WRM over a weave tunnel |
| |
| return: |
| 0 success |
| 1 failure |
| |
| """ |
| def __init__(self, opts = options): |
| HappyNode.__init__(self) |
| HappyNetwork.__init__(self) |
| WeaveTest.__init__(self) |
| |
| self.__dict__.update(options) |
| self.__dict__.update(opts) |
| |
| self.client_process_tag = "WEAVE-TIME-CLIENT" + opts.get("test_tag") |
| self.coordinator_process_tag = "WEAVE-TIME-COORDINATOR" + opts.get("test_tag") |
| self.server_process_tag = "WEAVE-TIME-SERVER" + opts.get("test_tag") |
| |
| self.client_node_id = None |
| self.coordinator_node_id = None |
| self.server_node_id = None |
| |
| |
| def __pre_check(self): |
| # Check if Weave Time client node is given. |
| if self.client == None: |
| emsg = "Missing name or address of the Weave Time client node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| # Check if Weave Time coordinator node is given. |
| if not self.skip_coordinator_end and self.coordinator == None: |
| emsg = "Missing name or address of the Weave Time coordinator node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| # Check if Weave Time server node is given. |
| if self.server == None: |
| emsg = "Missing name or address of the Weave Time server node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| |
| # Make sure that fabric was created |
| if self.getFabricId() == None: |
| emsg = "Weave Fabric has not been created yet." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| # Check if Weave Time client node exists. |
| if self._nodeExists(self.client): |
| self.client_node_id = self.client |
| |
| # Check if Weave Time coordinator node exists. |
| if self._nodeExists(self.coordinator): |
| self.coordinator_node_id = self.coordinator |
| |
| # Check if Weave Time server node exists. |
| if self._nodeExists(self.server): |
| self.server_node_id = self.server |
| |
| if self.client_node_id == None: |
| emsg = "Unknown identity of the client node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| if not self.skip_coordinator_end and self.coordinator_node_id == None: |
| emsg = "Unknown identity of the coordinator node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| if self.server_node_id == None and self.server != "service": |
| emsg = "Unknown identity of the server node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| self.client_ip = self.getNodeWeaveIPAddress(self.client_node_id) |
| self.client_weave_id = self.getWeaveNodeID(self.client_node_id) |
| |
| if not self.skip_coordinator_end: |
| self.coordinator_ip = self.getNodeWeaveIPAddress(self.coordinator_node_id) |
| self.coordinator_weave_id = self.getWeaveNodeID(self.coordinator_node_id) |
| |
| if self.server == "service": |
| self.skip_service_end = True |
| self.server_weave_id = self.getServiceWeaveID("Time") |
| self.server_ip = self.getServiceWeaveIPAddress("Time") |
| else: |
| self.server_ip = self.getNodeWeaveIPAddress(self.server_node_id) |
| self.server_weave_id = self.getWeaveNodeID(self.server_node_id) |
| |
| # Check if all unknowns were found |
| |
| if self.client_ip == None: |
| emsg = "Could not find IP address of the client node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| if not self.skip_coordinator_end and self.coordinator_ip == None: |
| emsg = "Could not find IP address of the coordinator node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| if self.server_ip == None: |
| emsg = "Could not find IP address of the server node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| if self.client_weave_id == None: |
| emsg = "Could not find Weave node ID of the client node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| if not self.skip_coordinator_end and self.coordinator_weave_id == None: |
| emsg = "Could not find Weave node ID of the coordinator node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| if self.server_weave_id == None: |
| emsg = "Could not find Weave node ID of the server node." |
| self.logger.error("[localhost] WeaveTime: %s" % (emsg)) |
| sys.exit(1) |
| |
| |
| def __process_results(self, output): |
| pass_test = False |
| |
| for line in output.split("\n"): |
| if gsync_succeeded_str in line: |
| pass_test = True |
| break |
| |
| if not self.skip_coordinator_end: |
| print "weave-time test among server %s (%s), client %s (%s), coordinator %s (%s) with %s mode: " % \ |
| (self.server_node_id, self.server_ip, |
| self.client_node_id, self.client_ip, |
| self.coordinator_node_id, self.coordinator_ip, self.mode) |
| else: |
| print "weave-time test among server %s (%s), client %s (%s) with %s mode: " % \ |
| (self.server_node_id, self.server_ip, |
| self.client_node_id, self.client_ip, self.mode) |
| |
| if self.quiet == False: |
| if pass_test: |
| print hgreen("Time sync succeeded") |
| else: |
| print hred("Time sync failed") |
| |
| return (pass_test, output) |
| |
| |
| def __start_client_side(self, block_until_sync_succeeds=False): |
| cmd = self.getWeaveMockDevicePath() |
| if not cmd: |
| return |
| |
| cmd += " --debug-resource-usage --print-fault-counters" |
| cmd += " --node-addr " + self.client_ip |
| cmd += " --time-sync-client" |
| |
| if self.mode == "local": |
| cmd += " --time-sync-mode-local" |
| |
| elif self.mode == "service": ## TCP |
| cmd += " --time-sync-mode-service" |
| |
| elif self.mode == "service-over-tunnel": ## WRM over tunnel |
| cmd += " --time-sync-mode-service-over-tunnel" |
| cmd += " --ts-server-node-id " + self.server_weave_id |
| cmd += " --ts-server-node-addr " + self.server_ip |
| |
| else: |
| cmd += " --time-sync-mode-auto" |
| |
| if self.tap: |
| cmd += " --interface " + self.tap |
| |
| if self.client_faults: |
| cmd += " --faults " + self.client_faults |
| |
| if self.iterations: |
| cmd += " --iterations " + str(self.iterations) |
| |
| if block_until_sync_succeeds: |
| wait_str = gsync_succeeded_str |
| else: |
| # block until client weave node is ready to service weave events |
| wait_str = self.ready_to_service_events_str |
| |
| self.start_weave_process(self.client_node_id, cmd, self.client_process_tag, sync_on_output=wait_str, env=self.plaid_client_env) |
| |
| |
| def __stop_client_side(self): |
| self.stop_weave_process(self.client_node_id, self.client_process_tag) |
| |
| |
| def __start_coordinator_side(self): |
| cmd = self.getWeaveMockDevicePath() |
| if not cmd: |
| return |
| |
| cmd += " --debug-resource-usage --print-fault-counters" |
| cmd += " --node-addr " + self.coordinator_ip |
| cmd += " --time-sync-coordinator" |
| |
| if self.tap: |
| cmd += " --interface " + self.tap |
| |
| if self.coordinator_faults: |
| cmd += " --faults " + self.coordinator_faults |
| |
| self.start_weave_process(self.coordinator_node_id, cmd, self.coordinator_process_tag, sync_on_output=self.ready_to_service_events_str, env=self.plaid_coordinator_env) |
| |
| def __stop_coordinator_side(self): |
| self.stop_weave_process(self.coordinator_node_id, self.coordinator_process_tag) |
| |
| |
| def __start_server_side(self): |
| cmd = self.getWeaveMockDevicePath() |
| if not cmd: |
| return |
| |
| cmd += " --debug-resource-usage --print-fault-counters" |
| cmd += " --node-addr " + self.server_ip |
| cmd += " --time-sync-server" |
| |
| if self.tap: |
| cmd += " --interface " + self.tap |
| |
| if self.server_faults: |
| cmd += " --faults " + self.server_faults |
| |
| self.start_weave_process(self.server_node_id, cmd, self.server_process_tag, sync_on_output=self.ready_to_service_events_str, env=self.plaid_server_env) |
| |
| def __stop_server_side(self): |
| self.stop_weave_process(self.server_node_id, self.server_process_tag) |
| |
| |
| def run(self): |
| self.logger.debug("[localhost] WeaveTime: Run.") |
| |
| self.__pre_check() |
| |
| # Note: in all the cases below, we don't need any synchronous and |
| # deterministic wait time to allow for a time sync operation to complete |
| # because the __start_client_code will block until it performs a |
| # successful time sync operation (if block_until_sync_succeeds=True) |
| |
| # Note: in all cases below that includes a server simulated by a happy |
| # weave node, a server restart is implicity performed as part of |
| # fault-injection. This will prompt the server node to send a time |
| # change notification and test if clients handle it successfully. There |
| # is, hence, no need to explicitly run a server-restart test |
| # (regardless of the sync mode). |
| |
| # Note: in cases where __start_client_side is invoked with |
| # block_until_sync_succeeds=True, the order in which the nodes are |
| # created is important. Be sure to start the server and coordinator |
| # nodes before the client node is initialized, otherwise, the test will |
| # fail because the client node won't get any responses from the server |
| # and/or coordinator nodes (because they would not have been created). |
| |
| if self.mode == "service-over-tunnel": |
| # this test presumes that the service node (mock or real), the |
| # border router and the tunnel between them have already been |
| # created and initialized successfully. |
| self.__start_client_side(block_until_sync_succeeds=True) |
| |
| else: |
| # mode in ["auto", "local", "service"] |
| self.__start_server_side() |
| self.__start_coordinator_side() |
| self.__start_client_side(block_until_sync_succeeds=True) |
| |
| self.__stop_client_side() |
| |
| if not self.skip_coordinator_end: |
| self.__stop_coordinator_side() |
| |
| if not self.skip_service_end: |
| self.__stop_server_side() |
| |
| client_output_value, client_output_data = \ |
| self.get_test_output(self.client_node_id, self.client_process_tag, True) |
| client_strace_value, client_strace_data = \ |
| self.get_test_strace(self.client_node_id, self.client_process_tag, True) |
| |
| if self.skip_coordinator_end: |
| coordinator_output_data = "" |
| coordinator_output_value = 0 |
| coordinator_strace_data = "" |
| coordinator_strace_value = 0 |
| else: |
| coordinator_output_value, coordinator_output_data = \ |
| self.get_test_output(self.coordinator_node_id, self.coordinator_process_tag, True) |
| coordinator_strace_value, coordinator_strace_data = \ |
| self.get_test_strace(self.coordinator_node_id, self.coordinator_process_tag, True) |
| |
| if self.skip_service_end: |
| server_output_data = "" |
| server_output_value = 0 |
| server_strace_data = "" |
| server_strace_value = 0 |
| else: |
| server_output_value, server_output_data = \ |
| self.get_test_output(self.server_node_id, self.server_process_tag, True) |
| server_strace_value, server_strace_data = \ |
| self.get_test_strace(self.server_node_id, self.server_process_tag, True) |
| |
| avg, results = self.__process_results(client_output_data) |
| |
| data = {} |
| data["client_output"] = client_output_data |
| data["client_strace"] = client_strace_data |
| data["coordinator_output"] = coordinator_output_data |
| data["coordinator_strace"] = coordinator_strace_data |
| data["server_output"] = server_output_data |
| data["server_strace"] = server_strace_data |
| |
| self.logger.debug("[localhost] WeaveTime: Done.") |
| |
| return ReturnMsg(avg, data) |
| |