示例#1
0
    def _process_properties(self):
        """Process test properties block"""

        if self.config is None:
            return
        if "properties" not in self.config:
            return
        properties = self.config["properties"]
        minversion = properties.get("minversion", ["1.4"])

        if not isinstance(minversion, list):
            minversion = [minversion]
        self.minversion = [AsteriskVersion(ver) for ver in minversion]

        maxversion = properties.get("maxversion", [])
        if not isinstance(maxversion, list):
            maxversion = [maxversion]
        self.maxversion = [AsteriskVersion(ver) for ver in maxversion]

        self.expect_pass = (properties.get("expectedResult", self.expect_pass)
                            and properties.get("expected-result",
                                               self.expect_pass))
        if "tags" in properties:
            self.tags = properties["tags"]
        if "features" in properties:
            self.features = set(properties["features"])
        if "forced-version" in properties:
            self.forced_version = AsteriskVersion(properties["forced-version"])

        for ver in self.minversion:
            if ver.feature:
                self.features.add(ver.feature)

        for feature in self.features:
            self.feature_check[feature] = False
示例#2
0
    def _process_properties(self):
        """Process test properties block"""

        self.minversion = AsteriskVersion("1.4")
        if self.config == None:
            return
        if "properties" not in self.config:
            return
        properties = self.config["properties"]
        if "minversion" in properties:
            self.minversion = AsteriskVersion(properties["minversion"])
            if self.minversion.feature:
                self.features.append(self.minversion.feature)
                self.feature_check[self.minversion.feature] = False
        if "maxversion" in properties:
            self.maxversion = AsteriskVersion(properties["maxversion"])
        self.expect_pass = (properties.get("expectedResult") or
                            properties.get("expected-result") or
                            True)
        if "tags" in properties:
            self.tags = properties["tags"]
        if "features" in properties:
            self.features.extend(properties["features"])
            for feature in self.features:
                self.feature_check[feature] = False
        if "forced-version" in properties:
            self.forced_version = AsteriskVersion(properties["forced-version"])
示例#3
0
 def _handle_ami_connect(self, ami):
     ''' Handle AMI connect events '''
     if (ami.id != 0):
         return
     ami.registerEvent('Newstate', self._handle_new_state)
     if AsteriskVersion() >= AsteriskVersion('12'):
         ami.registerEvent('AttendedTransfer',
                           self._handle_attended_transfer)
示例#4
0
def main(argv=None):
    """Main entry point for the test run

    Returns:
    0 on successful test run
    1 on any error
    """

    if argv is None:
        args = sys.argv

    if (len(args) < 2):
        LOGGER.error("test_runner requires the full path to the test "
                     "directory to execute")
        return 1
    test_directory = args[1]

    if (len(args) < 3):
        LOGGER.error("test_runner requires the Asterisk version to execute")
        return 1
    ast_version = args[2]

    try:
        AsteriskVersion()
    except OSError:
        # If there is no Asterisk version on the local system, then we need to
        # set the version to the one that was passed into the application.
        AsteriskVersion(default=ast_version)

    LOGGER.info("Starting test run for %s" % test_directory)
    test_config = load_test_config(test_directory)
    if test_config is None:
        return 1

    read_module_paths(test_config, test_directory)

    test_object = create_test_object(test_directory, test_config, ast_version)
    if test_object is None:
        return 1

    # Load other modules that may be specified
    load_test_modules(test_config, test_object, ast_version)

    # Load global modules as well
    if test_object.global_config.config:
        load_test_modules(test_object.global_config.config, test_object,
                          ast_version)

    # Kick off the twisted reactor
    reactor.run()

    LOGGER.info("Test run for %s completed with result %s" %
                (test_directory, str(test_object.passed)))
    if test_object.evaluate_results():
        return 0

    return 1
示例#5
0
    def ami_connect(self, ami):
        # We only care about the UUT's AMI here
        if ami.id != 0:
            return

        self.ami = ami
        self.ami.registerEvent('Hangup', self.log_hangup_time)
        self.ami.registerEvent('TestEvent', self.log_warnings)
        if AsteriskVersion() >= AsteriskVersion('12'):
            self.ami.registerEvent('BridgeEnter', self.log_bridge_enter_time)
        else:
            self.ami.registerEvent('Bridge', self.log_bridge_time)
示例#6
0
 def _create_application_event_instances(self, channel_id, events):
     for event_config in events:
         minversion = event_config.get('minversion')
         maxversion = event_config.get('maxversion')
         if (minversion is not None
                 and AsteriskVersion() < AsteriskVersion(minversion)):
             continue
         if (maxversion is not None
                 and AsteriskVersion() >= AsteriskVersion(maxversion)):
             continue
         ae_instance = ApplicationEventInstance(channel_id, event_config,
                                                self)
         self._event_instances.append(ae_instance)
示例#7
0
def get_vars(ari, channel_id):
    resp = ari.get('channels', channel_id, 'variable', variable='DP_SHELL')
    actual = resp.json()["value"]
    eq('works', actual)

    if AsteriskVersion() >= AsteriskVersion('13'):
        ari.set_allow_errors(True)
    resp = ari.get('channels', channel_id, 'variable', variable='SHELL(echo -n fail)')
    if AsteriskVersion() >= AsteriskVersion('13'):
        ari.set_allow_errors(False)
        eq(500, resp.status_code)
    else:
        eq(200, resp.status_code)
        eq(resp.json().get('value'), '')
示例#8
0
def handle_parkedcall(ami, event):
    running_version = AsteriskVersion()

    if running_version >= AsteriskVersion("12.0.0"):
        parkee = event.get('parkeechannel')
    else:
        parkee = event.get('channel')

    if parkee is None:
        LOGGER.error("Receved ParkedCall event without a Parkee.\n")
        return False

    LOGGER.info("Hanging up channel: %s" % parkee)
    ami.hangup(parkee)

    return True
示例#9
0
    def __init__(self, module_config, test_object):
        self.ami = None
        self.alice_ami = None
        self.parked_channel = None
        test_object.register_ami_observer(self.ami_connect)
        self.test_object = test_object

        running_version = AsteriskVersion()
        if (running_version < AsteriskVersion("12.0.0")):
            self.asterisk12Events = False
        else:
            self.asterisk12Events = True

        self.calls = []
        self.calls.append({
            'parker': 'SIP/alice',
            'lot': 'parkinglot_test1',
            'slot': '401',
            'status': 'ANSWER',
            'post': False
        })
        self.calls.append({
            'parker': 'SIP/alice',
            'lot': 'parkinglot_test1',
            'slot': '402',
            'status': 'NOANSWER',
            'post': True
        })
        self.calls.append({
            'parker': 'SIP/alice',
            'lot': 'parkinglot_test1',
            'slot': '403',
            'status': 'BUSY',
            'post': True
        })
        self.current_call = None
        self.current_call_post = False

        # Automatically fail if we don't remove this token.
        self.fail_token = \
            self.test_object.create_fail_token("This test should fail all the "
                                               "time right now.")

        self.userevents_received = 0
        self.failures_logged = 0
        self.parks_received = 0
示例#10
0
 def __send_stop_gracefully():
     """Send a core stop gracefully CLI command"""
     LOGGER.debug('sending stop gracefully')
     if self.ast_version and self.ast_version < AsteriskVersion("1.6.0"):
         cli_deferred = self.cli_exec("stop gracefully")
     else:
         cli_deferred = self.cli_exec("core stop gracefully")
     cli_deferred.addCallbacks(__stop_gracefully_callback, __stop_gracefully_error)
示例#11
0
    def __init__(self, module_config, test_object):
        ''' Constructor

        Keyword Arguments:
        module_config The module configuration
        test_object The test object. Must be of type BridgeTestCase
        '''
        self.module_config = module_config
        self.test_object = test_object
        self._current_feature = None

        self.test_object.register_feature_start_observer(self._handle_feature_start)
        if AsteriskVersion() >= AsteriskVersion('12'):
            self.test_object.register_ami_observer(self._handle_ami_connect)
        else:
            self.test_object.register_feature_end_observer(self._handle_feature_end)

        if (Transfer.__singleton_instance == None):
            Transfer.__singleton_instance = self
示例#12
0
    def __init__(self, module_config, test_object):
        self.ami = None
        self.parked_channel = None
        test_object.register_ami_observer(self.ami_connect)
        self.test_object = test_object

        running_version = AsteriskVersion()

        self.calls = []
        self.calls.append({'parker': 'alice', 'parkee': 'bob'})
        self.calls.append({'parker': 'bob', 'parkee': 'alice'})

        # Parking events for this test vary with Asterisk 12 and
        # up from prior versions.
        if (running_version < AsteriskVersion("12.0.0")):
            self.asterisk12Events = False
        else:
            self.asterisk12Events = True

        self.parking_events_received = 0

        return
示例#13
0
    def __init__(self, module_config, test_object):
        self.ami = None
        self.parked_channel = None
        test_object.register_ami_observer(self.ami_connect)
        self.test_object = test_object

        running_version = AsteriskVersion()
        if (running_version < AsteriskVersion("12.0.0")):
            self.asterisk12Events = False
        else:
            self.asterisk12Events = True

        self.calls = []
        self.calls.append({
            'test': '1',
            'parker': 'SIP/alice',
            'lot': 'parkinglot_test1',
            'slot': '401'
        })
        self.calls.append({
            'test': '2',
            'parker': 'SIP/alice',
            'lot': 'parkinglot_test2',
            'slot': '501'
        })
        self.calls.append({
            'test': '3',
            'parker': 'SIP/alice',
            'lot': 'parkinglot_test3',
            'slot': '601'
        })
        self.userevents_received = 0
        self.passed_dialplan = 0
        self.failures_logged = 0
        self.fail_token = \
            self.test_object.create_fail_token("No success indicated by "
                                               "Executioner.")
        return
示例#14
0
def check_module_version(module_spec, ast_version):
    """Check the module configuration for minversion and maxversion and check
    if the Asterisk version meets the version(s) if found

    Keyword Arguments:
    module_spec A dictionary of a pluggable module configuration
    ast_version A string containing the Asterisk version

    Returns:
    False if minversion or maxversion are found and do not meet the Asterisk
    version, True otherwise
    """

    modminversion = module_spec.get('minversion')
    modmaxversion = module_spec.get('maxversion')
    if (modminversion is not None
            and AsteriskVersion(ast_version) < AsteriskVersion(modminversion)):
        return False
    if (modmaxversion is not None and
            AsteriskVersion(ast_version) >= AsteriskVersion(modmaxversion)):
        return False

    return True
示例#15
0
    def check_duration(self, ami_uut, ami_alice, ami_bob):
        if not self.bridge_time or not self.end_time:
            LOGGER.error("We didn't get the notifications for duration")
            self.test_object.set_passed(False)

        duration = self.end_time - self.bridge_time

        if (abs(duration - self.current_call['timeout']) > TOLERANCE):
            LOGGER.error("Call duration was %f but we expected %f (+/- 0.5 sec)"
                    % (duration, self.current_call['timeout']))
            self.test_object.set_passed(False)

        if self.current_call['numwarnings'] != self.num_warnings:
            LOGGER.error("We expected %d warnings but got %d" %
                    (self.current_call['numwarnings'], self.num_warnings))
            self.test_object.set_passed(False)

        max_triggers = 1
        if self.current_call['hangup_style'] == 'BRIDGE_TIMELIMIT' \
            and AsteriskVersion() >= AsteriskVersion('12'):
            max_triggers = 2

        if 1 != self.num_hangup_triggers and max_triggers != self.num_hangup_triggers:
            if max_triggers == 2:
                LOGGER.error("We expected 1 or 2 hangup triggers but got %d" %
                        (self.num_hangup_triggers))
            else:
                LOGGER.error("We expected 1 hangup trigger but got %d" %
                        (self.num_hangup_triggers))
            self.test_object.set_passed(False)

        # Reset the variables for the next call
        self.bridge_enters_received = self.num_hangup_triggers = 0
        self.bridge_time = self.end_time = self.num_warnings = 0
        if len(self.calls) != 0:
            self.current_call = self.calls.pop(0)
示例#16
0
    def __init__(self, test_object, accounts):
        super(Transfer, self).__init__()
        self.ami = test_object.ami[0]

        if AsteriskVersion() < AsteriskVersion('12'):
            self.bridge_state = BridgeStateEleven(test_object, self, self.ami)
        else:
            self.bridge_state = BridgeStateTwelve(test_object, self, self.ami)

        self.bob_call_answered = False
        self.carol_call_answered = False

        bob = accounts.get('bob').account
        bob.set_callback(TransferAccountCallback(bob))

        carol = accounts.get('carol').account
        carol.set_callback(TransferAccountCallback(carol))

        self.alice = accounts.get('alice').account
        self.call_to_bob = None
        self.call_to_carol = None

        self.test_object = test_object
        self.state = INIT
示例#17
0
    def cli_originate(self, argstr):
        """Starts a call from the CLI and links it to an application or
        context.

        Must pass a valid argument string in the following form:

        <tech/data> application <appname> appdata
        <tech/data> extension <exten>@<context>

        If no context is specified, the 'default' context will be
        used. If no extension is given, the 's' extension will be used.

        Keyword Arguments:
        argstr Arguments to be passed to the originate

        Returns:
        A deferred object that can be used to listen for command completion

        Example Usage:
        asterisk.originate("Local/a_exten@context extension b_exten@context")
        """

        args = argstr.split()
        raise_error = False
        if len(args) != 3 and len(args) != 4:
            raise_error = True
            LOGGER.error("Wrong number of arguments.")
        if args[1] != "extension" and args[1] != "application":
            raise_error = True
            LOGGER.error('2nd argument must be "extension" or "application"')
        if args[0].find("/") == -1:
            raise_error = True
            LOGGER.error('Channel dial string must be in the form "tech/data".')
        if raise_error is True:
            raise Exception(
                "Cannot originate call!\n"
                "Argument string must be in one of these forms:\n"
                "<tech/data> application <appname> appdata\n"
                "<tech/data> extension <exten>@<context>")

        if self.ast_version and self.ast_version < AsteriskVersion("1.6.2"):
            return self.cli_exec("originate %s" % argstr)
        else:
            return self.cli_exec("channel originate %s" % argstr)
示例#18
0
class ThreadTestCondition(TestCondition):
    """Base class for the thread pre-/post-test conditions

    This class is a base class for the pre- and post-test condition classes that
    check thread usage in Asterisk.  It provides common functionality for
    parsing out the results of the 'core show threads' Asterisk command
    """

    _ast_version = AsteriskVersion()
    _ast_version_10 = AsteriskVersion("10")

    def __init__(self, test_config):
        """Constructor

        Keyword Arguments:
        test_config The condition test configuration
        """
        super(ThreadTestCondition, self).__init__(test_config)

        # ast_threads is a list of tuples, where the first entry is the host
        # IP of the Asterisk instance, and the second entry is a list of
        # tuples of thread ID and thread name
        self.ast_threads = []

        self.ignored_threads = (test_config.config.get('ignoredThreads')
                                or test_config.config.get('ignored-threads')
                                or [])

        # Core show threads is not available if LOW_MEMORY is turned on
        self.add_build_option("LOW_MEMORY", "0")

    def parse_threads(self, ast, threads):
        """Parse the thread values from the result of the Asterisk command

        Keyword Arguments:
        ast The Asterisk instance to check
        threads The result of 'core show threads'
        """
        ast_host = ast.host
        thread_list = []
        tokens = threads.split('\n')

        for line in tokens:
            if 'threads listed' in line or 'Asterisk ending' in line:
                continue

            # get the name and thread ID - strip off the cli_exec / pthread ID
            initial_partition = line.partition(' ')

            #In v10 and greater, the result of core show threads introduces the
            # Asterisk thread ID immediately after the pthread ID.  Use that if
            # it's available.

            if (ThreadTestCondition._ast_version >=
                    ThreadTestCondition._ast_version_10):
                initial_partition = initial_partition[2].partition(' ')
            thread_id = initial_partition[0]
            thread_name = initial_partition[2].partition(' ')[0]
            if (thread_id != "" and thread_name != ""
                    and thread_name not in self.ignored_threads):
                LOGGER.debug("Tracking thread %s[%s]" %
                             (thread_name, thread_id))
                thread_list.append((thread_id, thread_name))

        if (len(thread_list) > 0):
            self.ast_threads.append((ast_host, thread_list))
示例#19
0
    def __init__(self, test_path='', test_config=None):
        """Create a new instance of a TestCase. Must be called by inheriting
        classes.

        Keyword Arguments:
        test_path Optional parameter that specifies the path where this test
                  resides
        test_config Loaded YAML test configuration
        """

        if not len(test_path):
            self.test_name = os.path.dirname(sys.argv[0])
        else:
            self.test_name = test_path

        # We're not using /tmp//full//test//name because it gets so long that
        # it doesn't fit in AF_UNIX paths (limited to around 108 chars) used
        # for the rasterisk CLI connection. As a quick fix, we hash the path
        # using md5, to make it unique enough.
        self.realbase = self.test_name.replace("tests/", "", 1)
        self.base = md5(self.realbase).hexdigest()
        # We provide a symlink to it from a named path.
        named_dir = os.path.join(Asterisk.test_suite_root, self.realbase)
        try:
            os.makedirs(os.path.dirname(named_dir))
        except OSError:
            pass
        try:
            join_path = os.path.relpath(
                os.path.join(Asterisk.test_suite_root, self.base),
                os.path.dirname(named_dir)
            )
            os.symlink(join_path, named_dir)
        except OSError:
            pass

        self.ast = []
        self.ami = []
        self.fastagi = []
        self.base_config_path = None
        self.reactor_timeout = 30
        self.passed = None
        self.fail_tokens = []
        self.timeout_id = None
        self.global_config = TestConfig(os.getcwd())
        self.test_config = TestConfig(self.test_name, self.global_config)
        self.condition_controller = None
        self.pcap = None
        self.pcapfilename = None
        self.create_pcap = False
        self._stopping = False
        self.testlogdir = self._set_test_log_directory()
        self.ast_version = AsteriskVersion()
        self._start_callbacks = []
        self._stop_callbacks = []
        self._ami_callbacks = []
        self._pcap_callbacks = []
        self._stop_deferred = None
        log_full = True
        log_messages = True

        if os.getenv("VALGRIND_ENABLE") == "true":
            self.reactor_timeout *= 20

        # Pull additional configuration from YAML config if possible
        if test_config:
            if 'config-path' in test_config:
                self.base_config_path = test_config['config-path']
            if 'reactor-timeout' in test_config:
                self.reactor_timeout = test_config['reactor-timeout']
            self.ast_conf_options = test_config.get('ast-config-options')
            log_full = test_config.get('log-full', True)
            log_messages = test_config.get('log-messages', True)
        else:
            self.ast_conf_options = None

        os.makedirs(self.testlogdir)

        # Set up logging
        setup_logging(self.testlogdir, log_full, log_messages)

        LOGGER.info("Executing " + self.test_name)

        if PCAP_AVAILABLE and self.create_pcap:
            self.pcapfilename = os.path.join(self.testlogdir, "dumpfile.pcap")
            self.pcap = self.create_pcap_listener(dumpfile=self.pcapfilename)

        self._setup_conditions()

        # Enable twisted logging
        observer = log.PythonLoggingObserver()
        observer.start()

        reactor.callWhenRunning(self._run)
示例#20
0
    def __init__(self, base=None, ast_conf_options=None, host="127.0.0.1",
                 remote_config=None):
        """Construct an Asterisk instance.

        Keyword Arguments:
        base -- This is the root of the files associated with this instance of
                Asterisk. By default, the base is "/tmp/asterisk-testsuite"
                directory. Given a base, it will be appended to the default base
                directory.
        ast_conf_options -- Configuration overrides for asterisk.conf.
        host -- The IP address the Asterisk instance runs on
        remote_config -- Configuration section that defines this as a remote
                         Asterisk instance. If provided, base and
                         ast_conf_options are generally ignored, and the
                         Asterisk instance's configuration is treated as
                         immutable on some remote machine defined by 'host'

        Example Usage:
        self.asterisk = Asterisk(base="manager/login")
        """
        self._start_deferred = None
        self._stop_deferred = None
        self._stop_cancel_tokens = []
        self.directories = {}
        self.protocol = None
        self.process = None
        self.astetcdir = ""
        self.original_astmoddir = ""
        self.ast_version = None
        self.remote_config = remote_config

        # If the process is remote, don't bother
        if not self.remote_config:
            self.ast_version = AsteriskVersion()

        valgrind_env = os.getenv("VALGRIND_ENABLE") or ""
        self.valgrind_enabled = True if "true" in valgrind_env else False

        if base is not None:
            self.base = base
        else:
            self.base = Asterisk.test_suite_root
        if self.localtest_root:
            self.ast_binary = self.localtest_root + "/usr/sbin/asterisk"
        else:
            ast_binary = test_suite_utils.which("asterisk")
            self.ast_binary = ast_binary or "/usr/sbin/asterisk"
        self.host = host

        self._ast_conf_options = ast_conf_options

        if self.remote_config:
            # Pretend as if we made the structure
            self._directory_structure_made = True
            self._configs_installed = True
            self._configs_set_up = True

            # And assume we've got /etc/asterisk/ where we expect it to be
            self.astetcdir = "/etc/asterisk"
        else:
            self._directory_structure_made = False
            self._configs_installed = False
            self._configs_set_up = False

            # Find the system installed asterisk.conf
            ast_confs = [
                os.path.join(self.default_etc_directory, "asterisk.conf"),
                "/usr/local/etc/asterisk/asterisk.conf",
            ]
            self._ast_conf = None
            for config in ast_confs:
                if os.path.exists(config):
                    self._ast_conf = ConfigFile(config)
                    break
            if self._ast_conf is None:
                msg = "Unable to locate asterisk.conf in any known location"
                LOGGER.error(msg)
                raise Exception(msg)

            # Set which astxxx this instance will be
            i = 1
            while True:
                if not os.path.isdir("%s/ast%d" % (self.base, i)):
                    self.base = "%s/ast%d" % (self.base, i)
                    break
                i += 1

            # Get the Asterisk directories from the Asterisk config file
            for cat in self._ast_conf.categories:
                if cat.name == "directories":
                    for (var, val) in cat.options:
                        self.directories[var] = val

            # self.original_astmoddir is for dependency checking only
            if "astmoddir" in self.directories:
                if self.localtest_root:
                    self.original_astmoddir = "%s%s" % (
                        self.localtest_root, self.directories["astmoddir"])
                else:
                    self.original_astmoddir = self.directories["astmoddir"]
示例#21
0
    def __init__(self, base=None, ast_conf_options=None, host="127.0.0.1"):
        """Construct an Asterisk instance.

        Keyword Arguments:
        base -- This is the root of the files associated with this instance of
        Asterisk.  By default, the base is "/tmp/asterisk-testsuite" directory.
        Given a base, it will be appended to the default base directory.

        Example Usage:
        self.asterisk = Asterisk(base="manager/login")
        """
        self._start_deferred = None
        self._stop_deferred = None
        self._stop_cancel_tokens = []
        self.directories = {}
        self.ast_version = AsteriskVersion()
        self.process_protocol = None
        self.process = None
        self.astetcdir = ""
        self.original_astmoddir = ""

        if base is not None:
            self.base = base
        else:
            self.base = Asterisk.test_suite_root
        if self.localtest_root:
            self.ast_binary = self.localtest_root + "/usr/sbin/asterisk"
        else:
            ast_binary = test_suite_utils.which("asterisk")
            self.ast_binary = ast_binary or "/usr/sbin/asterisk"
        self.host = host

        self._ast_conf_options = ast_conf_options
        self._directory_structure_made = False
        self._configs_installed = False
        self._configs_set_up = False

        # Find the system installed asterisk.conf
        ast_confs = [
            os.path.join(self.default_etc_directory, "asterisk.conf"),
            "/usr/local/etc/asterisk/asterisk.conf",
        ]
        self._ast_conf = None
        for config in ast_confs:
            if os.path.exists(config):
                self._ast_conf = ConfigFile(config)
                break
        if self._ast_conf is None:
            msg = "Unable to locate asterisk.conf in any known location"
            LOGGER.error(msg)
            raise Exception(msg)

        # Set which astxxx this instance will be
        i = 1
        while True:
            if not os.path.isdir("%s/ast%d" % (self.base, i)):
                self.base = "%s/ast%d" % (self.base, i)
                break
            i += 1

        # Get the Asterisk directories from the Asterisk config file
        for cat in self._ast_conf.categories:
            if cat.name == "directories":
                for (var, val) in cat.options:
                    self.directories[var] = val

        # self.original_astmoddir is for dependency checking only
        if "astmoddir" in self.directories:
            if self.localtest_root:
                self.original_astmoddir = "%s%s" % (
                    self.localtest_root, self.directories["astmoddir"])
            else:
                self.original_astmoddir = self.directories["astmoddir"]