示例#1
0
    def test03(self, steps):
        with steps.start(f"Re-run test to collect Post-state data",
                         continue_=True) as step:
            # Create empty dictionary for storing all route results
            self.post_dic = {}

            # Loop over device dictionary
            for self.name, self.dev_name in self.devices.items():
                log.info(
                    f'*******  Learning and Processing details for {self.name}  *******'
                )
                # create empty list to store route entries emdeded within complete_dic dictionary
                acl_entries = []

                # create enbedded dictionary entry per device
                self.post_dic.update({self.name: []})

                # learn routes from device
                acls = self.dev_name.learn('acl')
                try:
                    acl_entries.append(acls.info)
                    # Add group of routes to dictionary per-device
                    self.post_dic[self.name] = acl_entries
                except AttributeError:
                    pass

        with steps.start(
                f"Compare Pre-state to Post-state to very routes haven't changed",
                continue_=True) as step:
            #Verification
            # perfrom a pre vs post ACL compare
            diff = Diff(self.pre_dic, self.post_dic, exclude="statistics")
            diff.findDiff()
            diff = str(diff)
            if not diff:
                log.info(f'No ACL changes detected - Test Passed')
            else:
                log.info(f'ACL changes detected {diff}')

                for dev in self.devices.keys():
                    log.info(f'ACL Change Summary for device - {dev}')
                    pre_list_of_acl = self.pre_dic[dev]
                    post_list_of_acl = self.post_dic[dev]

                    self.pre_acl_names = {}
                    self.post_acl_names = {}

                    # Start Pre state validation
                    for acl_set in pre_list_of_acl:
                        if 'acls' in acl_set:
                            acls = acl_set['acls']
                            for acl in acls.keys():
                                self.pre_aces = []
                                self.acl_type = acls[acl]['type']
                                try:
                                    aces = acls[acl]['aces']
                                except KeyError:
                                    print(
                                        f"ACL {acl} doesn't have any entries")
                                    self.pre_acl_names.update({
                                        acls[acl]['name']: {
                                            'type': acls[acl]['type'],
                                            'aces': None
                                        }
                                    })
                                    continue

                                for ace in aces.keys():
                                    seq = aces[ace]['name']
                                    self.pre_aces.append(aces[ace])

                                self.pre_acl_names.update({
                                    acls[acl]['name']: {
                                        'type': acls[acl]['type'],
                                        'aces': self.pre_aces
                                    }
                                })
                        else:
                            self.pre_acl_names.update({
                                'name': None,
                                'type': None,
                                'aces': None
                            })

                    #Start Post state validation
                    for acl_set in post_list_of_acl:
                        if 'acls' in acl_set:
                            acls = acl_set['acls']
                            for acl in acls.keys():
                                self.post_aces = []
                                self.acl_type = acls[acl]['type']
                                try:
                                    aces = acls[acl]['aces']
                                except KeyError:
                                    print(
                                        f"ACL {acl} doesn't have any entries")
                                    self.post_acl_names.update({
                                        acls[acl]['name']: {
                                            'type': acls[acl]['type'],
                                            'aces': None
                                        }
                                    })
                                    continue

                                for ace in aces.keys():
                                    seq = aces[ace]['name']
                                    self.post_aces.append(aces[ace])

                                self.post_acl_names.update({
                                    acls[acl]['name']: {
                                        'type': acls[acl]['type'],
                                        'aces': self.post_aces
                                    }
                                })
                        else:
                            self.post_acl_names.update({
                                'name': None,
                                'type': None,
                                'aces': None
                            })

                    # Start comparision

                    #List of ACLs that were removed
                    missing_acls = {
                        x: y
                        for x, y in self.pre_acl_names.items()
                        if x not in self.post_acl_names.keys()
                    }
                    if missing_acls:
                        for miss_acl in missing_acls.keys():
                            log.info(
                                f"Hostname: {dev} --- ACL {miss_acl} is missing"
                            )
                    else:
                        pass

                    # List of ACLs that were added
                    added_acls = {
                        x: y
                        for x, y in self.post_acl_names.items()
                        if x not in self.pre_acl_names.keys()
                    }
                    if added_acls:
                        for add_acl in added_acls.keys():
                            log.info(
                                f" Hostname: {dev} --- ACL {add_acl} was added"
                            )
                    else:
                        pass

                    #Check for modified ACLs
                    #Loop thru pre ACLs as primary
                    for pre_acl_name in self.pre_acl_names.keys():

                        try:
                            # process each pre ACE individually and compare to post
                            pre_aces_list = self.pre_acl_names[pre_acl_name][
                                'aces']
                            nested_lookup.nested_delete(pre_aces_list,
                                                        'statistics',
                                                        in_place=True)

                            #use pre-acl name as key to ensure we're comparing the same ACL name
                            post_aces_list = self.post_acl_names[pre_acl_name][
                                'aces']
                            nested_lookup.nested_delete(post_aces_list,
                                                        'statistics',
                                                        in_place=True)

                        #if ACL is removed and empty KeyError is thrown.
                        except KeyError:
                            continue

                        if pre_aces_list and post_aces_list:
                            for pre_acl in pre_aces_list:
                                if pre_acl in post_aces_list:

                                    pass
                                else:
                                    print((
                                        f"Hostname: {dev} --- ACL {pre_acl_name} seq {pre_acl['name']} has been been modified"
                                    ))

                    # Check for modified ACLs
                    # Loop thru post ACLs as primary
                    for post_acl_name in self.post_acl_names.keys():

                        try:
                            # process each pre ACE individually and compare to post
                            post_aces_list = self.post_acl_names[
                                post_acl_name]['aces']
                            nested_lookup.nested_delete(post_aces_list,
                                                        'statistics',
                                                        in_place=True)

                            # use pre-acl name as key to ensure we're comparing the same ACL name
                            pre_aces_list = self.pre_acl_names[post_acl_name][
                                'aces']
                            nested_lookup.nested_delete(pre_aces_list,
                                                        'statistics',
                                                        in_place=True)

                        #If ACL is removed/empty then KeyError is thrown
                        except KeyError:
                            continue

                        if post_aces_list and pre_aces_list:
                            for post_acl in post_aces_list:
                                if post_acl in pre_aces_list:

                                    pass
                                else:
                                    log.info((
                                        f"Hostname: {dev} --- ACL {post_acl_name} seq {post_acl['name']} has been been modified"
                                    ))

                step.failed()
示例#2
0
    def restore_configuration(self, device, method, abstract, iteration=10,
                              interval=60, compare=False, compare_exclude=[]):
        if method == 'checkpoint':
            # Enable the feature
            for i in range(1,iteration):
                try:
                    out = self.rollback_checkpoint(device=device, name=self.ckname)
                except Exception as e:
                    raise Exception('Unable to rollback config')

                if out and 'Rollback Done' in out:
                    break
                else:
                    log.info('Rollback configuration failed: sleeping {} '
                             'seconds and retrying...'.format(interval))
                    time.sleep(interval)

            else:
                raise Exception('Unable to rollback config')

            # Delete the checkpoint
            self.create_delete_checkpoint(device=device, name=self.ckname,
                                          abstract=abstract, action='delete')

            # Check if checkpoint is successfully deleted
            self.check_checkpoint_status(device=device, name=self.ckname,
                                         expect='delete', abstract=abstract)
        elif method == 'local':
            # reover the deivce with whole running-config
            device.configure(self.run_config)
        elif method == 'config_replace':
            # delete the archive file
            dialog = Dialog([
                Statement(pattern=r'This will apply all necessary.*',
                          action='sendline(Y)',
                          loop_continue=True,
                          continue_timer=False),
                Statement(pattern=r'less than running config.*',
                          action='sendline(Y)',
                          loop_continue=True,
                          continue_timer=False),
                ])

            for i in range(1,iteration):
                # configure replace location:<filename>
                out = device.execute('configure replace {}'.\
                            format(self.to_url), reply=dialog, error_pattern=[])

                if out and 'Rollback Done' in out:
                    break
                else:
                    log.info('Config replace failed: sleeping {} seconds before'
                             ' retrying.'.format(interval))
                    time.sleep(interval)

            else:
                raise Exception('Unable to execute config replace')

            # Compare restored configuration to details in file
            if compare:
                log.info("Comparing current running-config with config-replace file")

                # Default
                exclude = ['device', 'maker', 'diff_ignore', 'callables',
                           '(Current configuration.*)', '(.*Building configuration.*)',
                           '(.*Load for.*)', '(.*Time source.*)']
                if compare_exclude:
                    if isinstance(compare_exclude, str):
                        exclude.extend([compare_exclude])
                    else:
                        exclude.extend(compare_exclude)

                # show run
                show_run_output = device.execute('show running-config')
                show_run_config = Config(show_run_output)
                show_run_config.tree()

                # location:<filename> contents
                more_file = device.execute('more {}'.format(self.to_url))
                more_file_config = Config(more_file)
                more_file_config.tree()

                # Diff 'show run' and config replace file contents
                diff = Diff(show_run_config.config, more_file_config.config, exclude=exclude)
                diff.findDiff()

                # Check for differences
                if len(diff.diffs):
                    log.error("Differences observed betweenrunning-config and "
                              "config-replce file:'{f}' for device {d}:".\
                              format(f=self.to_url, d=device.name))
                    log.error(str(diff.diffs))
                    raise Exception("Comparison between running-config and "
                                    "config-replace file '{f}' failed for device"
                                    " {d}".format(f=self.to_url, d=device.name))
                else:
                    log.info("Comparison between running-config and config-replace"
                             "file '{f}' passed for device {d}".\
                             format(f=self.to_url, d=device.name))

            # Delete location:<filename>
            self.filetransfer = FileUtils.from_device(device)
            self.filename = self.to_url
            self.filetransfer.deletefile(target=self.to_url, device=device)

            # Verify location:<filename> deleted
            dir_output = self.filetransfer.dir(target=self.to_url,device=device)
            for file in dir_output:
                if self.filename in file:
                    break
            else:
                log.info("Successfully deleted '{}'".format(self.to_url))
                return
            raise Exception("Unable to delete '{}'".format(self.to_url))
        else:
            # modify the device via callable function
            # using Conf object
            self.modify_func(device=device, conf=self.conf,
                             values_dict=self.conf_argument,
                             recover=True, **self.specific_args)
示例#3
0
文件: common.py 项目: atifs/genielibs
    def ops_diff(self,
                 ops_learn,
                 ops_compare,
                 exclude=None,
                 ops_modified=None,
                 conf_argument=None):
        '''Diff two ops object with ignoring the keys from the exclude list

           Args:
              Mandatory:
                ops_learn (`obj`) : Ops object.
                ops_compare (`obj`) : Ops object.
              Optional:
                exclude (`list`) : Keys/attributs to ignore in the diff.
                mock (`list`) : List of items, which contain a list of keys
                               strucure of dict, and the value
                               needs to be mocked.

           Returns:
               None

           Raises:
               AssertionError: When diff is found

           Example:
               >>> ops_diff(ops_learn = <bgp_ops_obj>,
                            ops_compare = <bgp_ops_obj>,
                            exclude = ['up_time', 'keepalive', 'maker'],
                            mock = [['info', 'instance', '{}', 'vrf', '{}',
                                    'neighbor', '{}', 'remote_as', '900']])
        '''
        if ops_modified and conf_argument:
            # Some section of ops_learn needs to be
            # modified as its value was modified.

            # First verify the R requirement to make sure they are valid.
            for r in ops_modified:
                # Modify r to only verify that one which were modified.
                for argument, value in conf_argument.items():
                    if argument in r.args[0]:
                        loc = r.args[0].index(argument)
                        r.args[0][loc + 1] = value

                ret = find([ops_learn], r, filter_=False)
                if not ret:
                    raise Exception("'{r} does not exists in new "
                                    "snapshot".format(r=r))

                # Good this exists, but it will fail the comparaison with the
                # next snapshot, even though its valid. So let's take the value
                # of the snap and use it for this snapshot comparaison as we've
                # already verified it was valid
                osnap = ops_compare
                learn = ops_learn
                for item in r.args[0][:-2]:
                    # item could be either attr or dit
                    try:
                        osnap = osnap[item]
                        learn = learn[item]
                    except (KeyError, TypeError) as e:
                        try:
                            osnap = getattr(osnap, item)
                            learn = getattr(learn, item)
                        except AttributeError:
                            raise KeyError("'{item}' does not exists in the "
                                           "snapshots".format(item=item))
                else:
                    learn[r.args[0][-2]] = osnap[r.args[0][-2]]
                    pass

        diff = Diff(ops_compare, ops_learn, exclude=exclude)
        diff.findDiff()

        if str(diff):
            log.info("The output is not same with diff\n{}".format(str(diff)))
            raise AssertionError("The output is not same with diff\n{}".format(
                str(diff)))
示例#4
0
文件: common.py 项目: atifs/genielibs
def get_ops_diff(new, original, exclude=None, modified_path=None, keys=None):
    '''Diff two ops object with ignoring the keys from the exclude list

       Args:
          Mandatory:
            new (`obj`) : Ops object.
            original (`obj`) : Ops object.
          Optional:
            exclude (`list`) : Keys/attributs to ignore in the diff.
            modified_path (`list`) : List of items that needs to be checked.
                                     The item is following the ops attributes path
                                     in a list.
            keys (`list`) : List of items that contains the key values for the
                            'modified_path' regexp items.

       Returns:
           None

       Raises:
           AssertionError: When diff is found
           ValueError: When required attributes are not in the ops

       Example:
           >>> ops_diff(new = <bgp_ops_obj>,
                        original = <bgp_ops_obj>,
                        exclude = ['up_time', 'keepalive', 'maker'],
                        modified_path = [['info', 'instance', '(?P<instance>.*)',
                                          'vrf', '(?P<vrf>.*)', 'neighbor', '(?P<neighbor>.*)',
                                          'remote_as', '900']],
                        keys = [{'instance': '1', 'vrf': 'default', 'neighbor': '1.1.1.1'},
                                {'instance': '1', 'vrf': 'VRF1', 'neighbor': '2.2.2.2'},])
    '''
    def _modify_ops_snapshot(original, current, path):
        # First does path exists in original, except the value
        r = R(path[:-1] + ['(.*)'])
        ret = find([original], r, filter_=False)
        if not ret:
            raise ValueError(
                "'{p}' does not exist on original snapshot "
                "as per the original trigger requirement".format(p=path))
        _modify_value(current, path[:-1], ret[0][0])

    def _modify_value(snapshot, path, value):
        for p in path[:-1]:
            try:
                snapshot = snapshot[p]
            except (TypeError):
                snapshot = getattr(snapshot, p)
        if isinstance(snapshot, dict):
            snapshot[path[-1]] = value
        else:
            setattr(snapshot, path[-1], value)

    if modified_path and keys:
        mapping = Mapping()
        for req in modified_path:

            # use mapping internal function to populate the path with learned values
            req = mapping._populate_path([req], new.device, keys=keys)
            rs = [R(requirement) for requirement in req]

            # want to print one by one
            for rs_item in rs:
                ret = find([new], rs_item, filter_=False, all_keys=True)
                if not ret:
                    # return rs_item.args
                    raise ValueError("'{req}' does not exists in "
                                     "'{o}'".format(req=rs_item.args, o=new))

            # Let's modify the ops value to be equal to the original
            # snapshot. This will allow for comparing the other keys
            for require in req:
                try:
                    _modify_ops_snapshot(original=original,
                                         current=new,
                                         path=require)
                except Exception as e:
                    return

    diff = Diff(original,
                new,
                exclude=(exclude or []) +
                ['maker', 'callables', 'device', 'diff_ignore'])
    diff.findDiff()
    if diff.diffs:
        log.error("Current ops is not equal to the initial Snapshot "
                  "taken on device {d}.\n{e}".format(e=str(diff),
                                                     d=getattr(
                                                         new, 'device',
                                                         'name')))
        raise AssertionError(
            "Current ops is not equal to the initial Snapshot "
            "taken on device {d}.\n{e}".format(e=str(diff),
                                               d=getattr(
                                                   new, 'device', 'name')))
示例#5
0
    def compare_profile(self, pts, pts_compare, devices):
        '''Compare system profiles taken as snapshots during the run'''

        if os.path.isfile(pts):
            compare1 = unpickle(pts)
        else:
            compare1 = self.testscript.parameters[pts]

        if os.path.isfile(pts_compare):
            compare2 = unpickle(pts_compare)
        else:
            compare2 = self.testscript.parameters[pts_compare]

        exclude_list = [
            'device', 'maker', 'diff_ignore', 'callables',
            '(Current configuration.*)', 'ops_schema'
        ]

        try:
            if 'exclude' in self.pts_datafile:
                exclude_list.extend(self.pts_datafile['exclude'])
        except AttributeError:
            pass

        msg = []
        for fet in compare1:
            failed = []
            feature_exclude_list = exclude_list.copy()

            # Get the information too from the pts_data
            try:
                feature_exclude_list.extend(self.pts_datafile[fet]['exclude'])
            except (KeyError, AttributeError):
                pass

            for dev in compare1[fet]:
                # Only compare for the specified devices
                if dev not in devices:
                    continue
                dev_exclude = feature_exclude_list.copy()
                try:
                    dev_exclude.extend(compare1[fet][dev].exclude)
                    # TODO - better fix,
                    dev_exclude.remove(None)
                except (AttributeError, ValueError):
                    pass

                diff = Diff(compare1[fet][dev],
                            compare2[fet][dev],
                            exclude=dev_exclude)

                diff.findDiff()

                if len(diff.diffs):
                    failed.append((dev, diff))

            if failed:
                msg.append('\n' + '*' * 10)
                msg.append("Comparison between {pts} and "
                           "{OPS} is different for feature '{f}' "
                           "for device:\n".format(pts=pts,
                                                  OPS=pts_compare,
                                                  f=fet))
                for device, diff in failed:
                    msg.append("'{d}'\n{diff}".format(d=device, diff=diff))

            else:
                message = "Comparison between {pts} and "\
                          "{OPS} is identical\n".format(pts=pts,
                          OPS=pts_compare)
                # print out message
                log.info(message)

        if msg:
            self.builtin.fail('\n'.join(msg))

        message = 'All Feature were identical on all devices'
        self.builtin.pass_execution(message)
示例#6
0
    def test04(self, steps):
        with steps.start(f"Compare pre-state to post interface states",
                         continue_=True) as step:
            # Verification
            diff = Diff(self.pre_dic, self.post_dic)
            diff.findDiff()
            diff = str(diff)
            print(diff)

            print("pre and post....")
            print(self.pre_dic)
            print(self.post_dic)

            if not diff:
                log.info(f'No HSRP changes detected - Test Passed')
            else:
                log.info(f'HSRP changes detected - Test Failed')

                for device in self.devices.keys():

                    missing_hsrp_int = [
                        x for x in self.pre_dic[device].keys()
                        if x not in self.post_dic[device].keys()
                    ]

                    if missing_hsrp_int:
                        for interface in missing_hsrp_int:
                            log.info(
                                f"{device} -- Interface {interface} no longer has HSRP enabled "
                            )

                    added_hsrp_int = [
                        x for x in self.post_dic[device].keys()
                        if x not in self.pre_dic[device].keys()
                    ]

                    if added_hsrp_int:
                        for interface in added_hsrp_int:
                            log.info(
                                f"{device} -- HSRP enabled on new interface - {interface}"
                            )

                    common_hsrp_ints = [
                        x for x in self.post_dic[device].keys()
                        if x in self.pre_dic[device].keys()
                    ]

                    for pre_int in common_hsrp_ints:
                        for post_int in common_hsrp_ints:

                            if 'ipv4' in self.pre_dic[device][pre_int].keys(
                            ) and 'ipv4' not in self.post_dic[device][
                                    pre_int].keys():
                                log.info(
                                    f"{device} -- IPv4 no longer enabled on interface {pre_int}"
                                )

                            if 'ipv6' in self.pre_dic[device][pre_int].keys(
                            ) and 'ipv6' not in self.post_dic[device][
                                    pre_int].keys():
                                log.info(
                                    f"{device} -- IPv6 no longer enabled on interface {pre_int}"
                                )

                            if 'ipv4' in self.post_dic[device][pre_int].keys(
                            ) and 'ipv4' not in self.pre_dic[device][
                                    pre_int].keys():
                                log.info(
                                    f"{device} -- IPv4 now enabled on interface {pre_int}"
                                )

                            if 'ipv6' in self.post_dic[device][pre_int].keys(
                            ) and 'ipv6' not in self.pre_dic[device][
                                    pre_int].keys():
                                log.info(
                                    f"{device} -- IPv6 now enabled on interface {pre_int}"
                                )

                            #execute ipv4 flow
                            if 'ipv4' in self.post_dic[device][pre_int].keys(
                            ) and 'ipv4' in self.pre_dic[device][pre_int].keys(
                            ):

                                missing_hsrp_group = [
                                    x for x in self.pre_dic[device][pre_int]
                                    ['ipv4'].keys()
                                    if x not in self.post_dic[device][pre_int]
                                    ['ipv4'].keys()
                                ]

                                #identify missing HSRP groups
                                if missing_hsrp_group:
                                    for group in missing_hsrp_group:
                                        log.info(
                                            f"{device} -- IPv4 HSRP group {group} has been removed"
                                        )

                                #identify added HSRP groups
                                added_hsrp_group = [
                                    x for x in self.post_dic[device][pre_int]
                                    ['ipv4'].keys()
                                    if x not in self.pre_dic[device][pre_int]
                                    ['ipv4'].keys()
                                ]

                                if added_hsrp_group:
                                    for group in missing_hsrp_group:
                                        log.info(
                                            f"{device} -- IPv4 HSRP group {group} has been added"
                                        )

                                common_hsrp_groups = [
                                    x for x in self.post_dic[device][pre_int]
                                    ['ipv4'].keys()
                                    if x in self.pre_dic[device][pre_int]
                                    ['ipv4'].keys()
                                ]

                                for indi_group in common_hsrp_groups:
                                    pre_priority = self.pre_dic[device][
                                        pre_int]['ipv4'][indi_group][
                                            'priority']
                                    pre_vmac = self.pre_dic[device][pre_int][
                                        'ipv4'][indi_group]['v_mac']
                                    pre_state = self.pre_dic[device][pre_int][
                                        'ipv4'][indi_group]['state']
                                    pre_pri_router = self.pre_dic[device][
                                        pre_int]['ipv4'][indi_group][
                                            'active_router']
                                    post_priority = self.post_dic[device][
                                        pre_int]['ipv4'][indi_group][
                                            'priority']
                                    post_vmac = self.post_dic[device][pre_int][
                                        'ipv4'][indi_group]['v_mac']
                                    post_state = self.post_dic[device][
                                        pre_int]['ipv4'][indi_group]['state']
                                    post_pri_router = self.post_dic[device][
                                        pre_int]['ipv4'][indi_group][
                                            'active_router']

                                    if pre_priority != post_priority:
                                        log.info(
                                            f"{device} -- IPv4 HSRP group {indi_group} priority changed from {pre_priority} to {post_priority}"
                                        )

                                    if pre_vmac != post_vmac:
                                        log.info(
                                            f"{device} -- IPv4 HSRP group {indi_group} virtual mac changed from {pre_vmac} to {post_vmac}"
                                        )

                                    if pre_state != post_state:
                                        log.info(
                                            f"{device} -- IPv4 HSRP group {indi_group} HSRP state changed from {pre_state} to {post_state}"
                                        )

                                    if pre_pri_router != post_pri_router:
                                        log.info(
                                            f"{device} -- IPv4 HSRP group {indi_group} primary router changed from {pre_pri_router} to {post_pri_router}"
                                        )

                            # execute ipv6 flow
                            if 'ipv6' in self.post_dic[device][pre_int].keys() and 'ipv6' in \
                                    self.pre_dic[device][pre_int].keys():

                                missing_hsrp_group = [
                                    x for x in self.pre_dic[device][pre_int]
                                    ['ipv6'].keys()
                                    if x not in self.post_dic[device][pre_int]
                                    ['ipv6'].keys()
                                ]

                                # identify missing HSRP groups
                                if missing_hsrp_group:
                                    for group in missing_hsrp_group:
                                        log.info(
                                            f"{device} -- IPv6 HSRP group {group} has been removed"
                                        )

                                # identify added HSRP groups
                                added_hsrp_group = [
                                    x for x in self.post_dic[device][pre_int]
                                    ['ipv6'].keys()
                                    if x not in self.pre_dic[device][pre_int]
                                    ['ipv6'].keys()
                                ]

                                if added_hsrp_group:
                                    for group in missing_hsrp_group:
                                        log.info(
                                            f"{device} -- IPv6 HSRP group {group} has been added"
                                        )

                                common_hsrp_groups = [
                                    x for x in self.post_dic[device][pre_int]
                                    ['ipv6'].keys()
                                    if x in self.pre_dic[device][pre_int]
                                    ['ipv6'].keys()
                                ]

                                for indi_group in common_hsrp_groups:
                                    pre_priority = self.pre_dic[device][
                                        pre_int]['ipv6'][indi_group][
                                            'priority']
                                    pre_vmac = self.pre_dic[device][pre_int][
                                        'ipv6'][indi_group]['v_mac']
                                    pre_state = self.pre_dic[device][pre_int][
                                        'ipv6'][indi_group]['state']
                                    pre_pri_router = self.pre_dic[device][
                                        pre_int]['ipv6'][indi_group][
                                            'active_router']
                                    post_priority = self.post_dic[device][
                                        pre_int]['ipv6'][indi_group][
                                            'priority']
                                    post_vmac = self.post_dic[device][pre_int][
                                        'ipv6'][indi_group]['v_mac']
                                    post_state = self.post_dic[device][
                                        pre_int]['ipv6'][indi_group]['state']
                                    post_pri_router = self.post_dic[device][
                                        pre_int]['ipv6'][indi_group][
                                            'active_router']

                                    if pre_priority != post_priority:
                                        log.info(
                                            f"{device} -- IPv6 HSRP group {indi_group} priority changed from {pre_priority} to {post_priority}"
                                        )

                                    if pre_vmac != post_vmac:
                                        log.info(
                                            f"{device} -- IPv6 HSRP group {indi_group} virtual mac changed from {pre_vmac} to {post_vmac}"
                                        )

                                    if pre_state != post_state:
                                        log.info(
                                            f"{device} -- IPv6 HSRP group {indi_group} HSRP state changed from {pre_state} to {post_state}"
                                        )

                                    if pre_pri_router != post_pri_router:
                                        log.info(
                                            f"{device} -- IPv6 HSRP group {indi_group} primary router changed from {pre_pri_router} to {post_pri_router}"
                                        )

                step.failed()
示例#7
0
    def verify_process(self, repeat_restart, steps, timeout):
        '''Verify the process has been restarted

           Args:
               uut (`obj`): Device object.
               steps (`step obj`): aetest step object

           Returns:
               None
        '''

        if self.helper:
            device = self.helper
        else:
            device = self.device

        with steps.start('Verify process has restarted correctly') as step:
            temp = TempResult(container=step)

            while timeout.iterate():
                output = self.abstract.parser.show_system.\
                             ShowSystemInternalSysmgrServiceName(device=\
                                        device).parse(process=self.process)

                if 'instance' not in output:
                    temp.failed("No output for 'show system internal sysmgr "
                                "service name {p}'".format(p=self.process))
                    timeout.sleep()
                    continue

                # Check the if the process has changed pid
                try:
                    pid = output['instance'][self.instance]['tag'][
                        self.tag]['pid']
                    sap = output['instance'][self.instance]['tag'][
                        self.tag]['sap']
                    restart_count = output['instance'][self.instance]['tag'][
                        self.tag]['restart_count']
                    last_restart = output['instance'][self.instance]['tag'][
                        self.tag]['last_restart_date']
                    last_restart_time = datetime.datetime.strptime(
                        last_restart, '%a %b %d %H:%M:%S %Y')
                except Exception as e:
                    temp.failed("Issue retrieving information about "
                                "'{p}' process".format(p=self.process),
                                from_exception=e)

                # Make sure time has changed
                if not self.last_restart_time < last_restart_time:
                    temp.failed("The restart time has not changed for "
                                "process '{p}'".format(p=self.process))
                    timeout.sleep()
                    continue

                # Make sure the pid has changed
                if self.process not in self.reconnect and pid == self.previous_pid:
                    temp.failed("The pid has not changed for process '{p}'"
                                "\nprevious pid: {pp}"
                                "\ncurrent pid: "
                                "{cp}".format(p=self.process,
                                              pp=self.previous_pid,
                                              cp=pid))
                    timeout.sleep()
                    continue

                # Verify the restart_count has increased
                if self.process not in self.reconnect and\
                   self.previous_restart_count + repeat_restart != restart_count:
                    temp.failed('Restart count has not increased by {rr}'
                                '\nprevious count: {pc}'
                                '\ncurrent count: {cc}'.format(
                                    rr=repeat_restart,
                                    pc=self.previous_restart_count,
                                    cc=restart_count))
                    timeout.sleep()
                    continue

                # exclude sap when the value is not in range [0, 1023]
                if sap > 1023:
                    self.verify_exclude.append('sap')

                # Modify the original output so it does not fail the compare
                self.previous_output['instance'][self.instance]['tag'][self.tag]['restart_count'] =\
                              restart_count
                self.previous_output['instance'][self.instance]['tag'][self.tag]['pid'] =\
                              pid

                diff = Diff(self.previous_output,
                            output,
                            exclude=self.verify_exclude)
                diff.findDiff()

                if diff.diffs:
                    temp.failed(
                        "The device output has changed in an unexpected "
                        "way\n{d}".format(d=str(diff.diffs)))
                    timeout.sleep()
                    continue
                break
            else:
                temp.result()
示例#8
0
# Learn feature 'BGP' for XE device after config change
# -----------------------------------------------------
log.info(banner("Learn feature 'BGP' for XE device '{}' after config change".\
                format(dev_xe.name)))

post_bgp_ops1 = dev_xe.learn("bgp")
log.info("\nPASS: Successfully learnt feature 'bgp' for XE device '{}' after config change\n".\
         format(dev_xe.name))

# --------------------------------
# Use Genie Diff to compare states
# --------------------------------
log.info(banner("Use Genie Diff to verify BGP neighbor is shutdown on XE device '{}'".\
                format(dev_xe.name)))

bgp_diff = Diff(pre_bgp_ops.info, post_bgp_ops1.info)
bgp_diff.findDiff()
log.info("Genie Diffs observed, BGP neighbor is shutdown/missing:\n\n" +
         str(bgp_diff) + "\n")

# -----------------------------------
# Restore 'BGP' neighbor on XE device
# -----------------------------------
log.info(banner("Restore 'BGP' neighbor on XE device '{}'".format(
    dev_xe.name)))

dev_xe.configure("router bgp 65000\n" " no neighbor 10.2.2.2 shutdown")
log.info("\nPASS: Successfully restored BGP neighbor on XE device '{}'\n".\
         format(dev_xe.name))

# -----
示例#9
0
文件: compare.py 项目: dschulz6/pyATS
from genie.utils.diff import Diff

diff output1 output2 --output diff

with open ('output1') as a:
    output = a.read()

with open ('output2') as b:
    output1 = b.read()

diff = Diff(output.info, output1.info)
diff.findDiff()
print(diff)
示例#10
0
    def test04(self, steps):
        with steps.start(f"Compare pre-state to post interface states",
                         continue_=True) as step:
            # Verification
            diff = Diff(self.pre_dic, self.post_dic)
            diff.findDiff()
            diff = str(diff)
            print(diff)

            print("pre and post....")
            print(self.pre_dic)
            print(self.post_dic)

            if not diff:
                log.info(f'No BGP neighbor changes detected - Test Passed')
            else:
                log.info(f'BGP neighbor changes detected - Test Failed')

                for device in self.devices.keys():

                    #identify missing peers
                    missing_peer = [
                        x for x in self.pre_dic[device].keys()
                        if x not in self.post_dic[device].keys()
                    ]

                    if missing_peer:
                        for x in missing_peer:
                            log.info(
                                f"{device} -- BGP neighbor {x} is missing")

                    #identify new peers
                    new_peer = [
                        x for x in self.post_dic[device].keys()
                        if x not in self.pre_dic[device].keys()
                    ]

                    if new_peer:
                        for x in new_peer:
                            log.info(
                                f"{device} -- BGP neighbor {x} has been added")

                    #Identify existing peers with changes in state

                    common_neighbors = [
                        x for x in self.pre_dic[device].keys()
                        if x in self.post_dic[device].keys()
                    ]

                    for neighbor in common_neighbors:

                        #ccheck for changes in peering state
                        if self.pre_dic[device][neighbor][
                                'state'] != self.post_dic[device][neighbor][
                                    'state']:
                            log.info(
                                f"{device} -- Change in BGP peering state detected. State change from {self.pre_dic[device][neighbor]['state']} to {self.post_dic[device][neighbor]['state']}"
                            )

                        #check for local AS change
                        if self.pre_dic[device][neighbor][
                                'local_as'] != self.post_dic[device][neighbor][
                                    'local_as']:
                            log.info(
                                f"{device} -- Local BGP AS changed. Changed from {self.pre_dic[device][neighbor]['state']} to {self.post_dic[device][neighbor]['state']}"
                            )

                            #Check for neighbor AS change

                            if self.pre_dic[device][neighbor][
                                    'peer_as'] != self.post_dic[device][
                                        neighbor]['peer_as']:
                                log.info(
                                    f"{device} -- The AS number of neighbor {neighbor} has changed.  change from {self.pre_dic[device][neighbor]['state']} to {self.post_dic[device][neighbor]['state']}"
                                )

                step.failed()
    def parse(self, testbed, section, steps):
        """ Testcase Setup section """
        # ---------------------------------------
        # Loop over devices
        # ---------------------------------------
        for device in testbed:

            # ---------------------------------------
            # Load Data Model
            # ---------------------------------------
            with open("data_models/%s.yaml" % device.alias) as stream:
                data_model = yaml.safe_load(stream)
            # ---------------------------------------
            # Genie learn('config').info for pre-change state
            # ---------------------------------------
            print(Panel.fit(Text.from_markup(LEARN)))

            self.learned_config = ParseConfigFunction.parse_learn(
                steps, device, "config")
            original_learned_config = self.learned_config

            #----------------------------------------
            # Write Pre-Change File
            #----------------------------------------
            with steps.start('Store Original Golden Image',
                             continue_=True) as step:
                print(Panel.fit(Text.from_markup(WRITING)))

                original_config_filename = "%s_Original_Golden_Image_%s.json" % (
                    timestr, device.alias)
                # Write Original Learned Config as JSON
                if hasattr(self, 'learned_config'):
                    with open(
                            "Camelot/Cisco/DevNet_Sandbox/Lancelot/Golden_Image/%s"
                            % original_config_filename, "w") as fid:
                        json.dump(self.learned_config,
                                  fid,
                                  indent=4,
                                  sort_keys=True)
                        fid.close()

            # ---------------------------------------
            # Create Intent from Template and Data Models
            # ---------------------------------------
            with steps.start('Generating Intent From Data Model and Template',
                             continue_=True) as step:
                print(Panel.fit(Text.from_markup(RUNNING)))
                intended_config_template = env.get_template(
                    'intended_config.j2')
                rendered_intended_config = intended_config_template.render(
                    host_data_model=data_model)

                with open(
                        "Camelot/Cisco/DevNet_Sandbox/Lancelot/Intended_Config/%s_Intended_Config.txt"
                        % timestr, "w") as fid:
                    fid.write(rendered_intended_config)
                    fid.close()

            # ---------------------------------------
            # Push Intent to Device
            # ---------------------------------------
            with steps.start('Push Intent', continue_=True) as step:
                print(Panel.fit(Text.from_markup(PUSH_INTENT)))
                device.configure(rendered_intended_config)

            # ---------------------------------------
            # Re-capture state
            # ---------------------------------------
            print(Panel.fit(Text.from_markup(LEARN)))

            self.learned_config = ParseConfigFunction.parse_learn(
                steps, device, "config")
            new_learned_config = self.learned_config

            # ---------------------------------------
            # Write post-change state
            # ---------------------------------------
            with steps.start('Store New Golden Image', continue_=True) as step:
                print(Panel.fit(Text.from_markup(WRITING)))

                new_config_filename = "%s_Golden_Image_%s.json" % (
                    timestr, device.alias)

                # Write Original Learned Config as JSON
                if hasattr(self, 'learned_config'):
                    with open(
                            "Camelot/Cisco/DevNet_Sandbox/Lancelot/Golden_Image/%s"
                            % new_config_filename, "w") as fid:
                        json.dump(self.learned_config,
                                  fid,
                                  indent=4,
                                  sort_keys=True)
                        fid.close()

            # ---------------------------------------
            # Show the differential
            # ---------------------------------------
            with steps.start('Show Differential', continue_=True) as step:
                config_diff = Diff(original_learned_config, new_learned_config)
                config_diff.findDiff()

                if config_diff.diffs:
                    print(Panel.fit(Text.from_markup(DIFF)))
                    print(config_diff)

                    with open(
                            'Camelot/Cisco/DevNet_Sandbox/Lancelot/Changes/%s_Changes.txt'
                            % timestr, 'w') as f:
                        with redirect_stdout(f):
                            print(config_diff)
                            f.close()

                else:
                    print(Panel.fit(Text.from_markup(NO_DIFF)))

                    with open(
                            'Camelot/Cisco/DevNet_Sandbox/Lancelot/Changes/%s_Changes.txt'
                            % timestr, 'w') as f:
                        f.write("IDEMPOTENT - NO CHANGES")
                        f.close()
示例#12
0
# Importing a new library!
from genie.utils.diff import Diff
import json
import os

# Step 0: Opening the two files
with open('{cwd}/config_modified.json'.format(cwd=os.path.dirname(__file__)), 'r') as config_file:
    config_modified = json.load(config_file)

with open('{cwd}/config_original.json'.format(cwd=os.path.dirname(__file__)), 'r') as config_file:
    config_original = json.load(config_file)

# Step 1: Printting the differences
dd = Diff(config_original, config_modified)
dd.findDiff()

print(dd)
def main():
    argument_spec = dict(
        command=dict(type='str', required=True),
        parse_command=dict(type='str', required=False),
        prompt=dict(type='list', required=False),
        answer=dict(type='list', required=False),
        compare=dict(type='dict', required=False),
        sendonly=dict(type='bool', default=False, required=False),
        # newline=dict(type='bool', default=True, required=False),
        # check_all=dict(type='bool', default=False, required=False),
    )
    required_together = [['prompt', 'answer']]
    module = AnsibleModule(argument_spec=argument_spec,
                           required_together=required_together,
                           supports_check_mode=True)

    if not PY3:
        module.fail_json(msg="pyATS/Genie requires Python 3")

    if not HAS_GENIE:
        module.fail_json(msg="Genie not found. Run 'pip install genie'")

    if not HAS_PYATS:
        module.fail_json(msg="pyATS not found. Run 'pip install pyats'")

    if module.check_mode and not module.params['command'].startswith('show'):
        module.fail_json(
            msg='Only show commands are supported when using check_mode, not '
            'executing %s' % module.params['command'])

    warnings = list()
    result = {'changed': False, 'warnings': warnings}

    connection = Connection(module._socket_path)

    capabilities = json.loads(connection.get_capabilities())

    if capabilities['device_info']['network_os'] == 'ios':
        genie_os = 'iosxe'
    else:
        genie_os = capabilities['device_info']['network_os']

    compare = module.params.pop('compare')

    parse_command = module.params.pop('parse_command')
    if not parse_command:
        parse_command = module.params['command']

    response = ''
    try:
        response = connection.get(**module.params)
    except ConnectionError as exc:
        module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))

    device = Device("uut", os=genie_os)

    device.custom.setdefault("abstraction", {})["order"] = ["os"]
    device.cli = AttrDict({"execute": None})

    try:
        get_parser(parse_command, device)
    except Exception as e:
        module.fail_json(
            msg="Unable to find parser for command '{0}' ({1})".format(
                parse_command, e))

    try:
        parsed_output = device.parse(parse_command, output=response)
    except Exception as e:
        module.fail_json(
            msg="Unable to parse output for command '{0}' ({1})".format(
                parse_command, e))

    # import sys;
    # sys.stdin = open('/dev/tty')
    # import pdb;
    # pdb.set_trace()

    if compare:
        diff = Diff(parsed_output,
                    compare,
                    exclude=get_parser_exclude(parse_command, device))
        diff.findDiff()
    else:
        diff = None

    if not module.params['sendonly']:
        try:
            result['json'] = module.from_json(response)
        except ValueError:
            pass

        result.update({
            'stdout': response,
            'structured': parsed_output,
            'diff': "{0}".format(diff),
            'exclude': get_parser_exclude(parse_command, device),
        })

    module.exit_json(**result)
示例#14
0
    def restore_configuration(self,
                              device,
                              method,
                              abstract,
                              iteration=10,
                              interval=60,
                              compare=False,
                              compare_exclude=[],
                              reload_timeout=None,
                              delete_after_restore=True):
        if method == 'checkpoint':
            # Enable the feature
            dialog = Dialog([
                Statement(pattern=r'\[no\]',
                          action='sendline(y)',
                          loop_continue=True,
                          continue_timer=False)
            ])

            for i in range(1, iteration):
                # replace config with checkpoint
                cfg = 'load disk0:{name}\n'\
                    'commit replace'.format(name=self.ckname)
                output = device.configure(cfg, reply=dialog)
                if 'fail' not in output:
                    break
                elif i == iteration - 1:
                    raise Exception('Failed to rollback config to checkpoint')
                else:
                    log.info('Rollback checkpoint failed: sleeping {} seconds '
                             'and retrying...'.format(interval))
                    time.sleep(interval)

            if delete_after_restore:
                # need to delete the config file on the device
                dialog = Dialog([
                    Statement(pattern=r'\[confirm\]',
                              action='sendline(y)',
                              loop_continue=True,
                              continue_timer=False)
                ])
                device.execute('delete disk0:{name}'.format(name=self.ckname),
                               reply=dialog)

        # Keeping them for later enhancement
        elif method == 'local':
            pass
        elif method == 'config_replace':
            for i in range(1, iteration):
                # Execute commit replace
                cmd = "load {}\n"\
                      "commit replace".format(self.to_url)
                output = device.configure(cmd)
                if 'Failed to commit' not in output:
                    break
                elif i == iteration - 1:
                    raise Exception('Unable to execute commit replace')
                else:
                    log.info(
                        'Commit replace failed: sleeping {} seconds before'
                        ' retrying.'.format(interval))
                    device.execute('show configuration failed')
                    time.sleep(interval)

            # Compare restored configuration to details in file
            if compare:
                log.info(
                    "Comparing current running-config with config-replace file"
                )

                # Default
                exclude = [
                    'device', 'maker', 'diff_ignore', 'callables',
                    '(Current configuration.*)'
                ]
                if compare_exclude:
                    if isinstance(compare_exclude, str):
                        exclude.extend([compare_exclude])
                    else:
                        exclude.extend(compare_exclude)

                # show run
                show_run_output = device.execute('show running-config')
                show_run_config = Config(show_run_output)
                show_run_config.tree()

                # location:<filename> contents
                more_file = device.execute('more {}'.format(self.to_url))
                more_file_config = Config(more_file)
                more_file_config.tree()

                # Diff 'show run' and config replace file contents
                diff = Diff(show_run_config.config,
                            more_file_config.config,
                            exclude=exclude)
                diff.findDiff()

                # Check for differences
                if len(diff.diffs):
                    log.error(
                        "Differences observed betweenrunning-config and "
                        "config-replce file:'{f}' for device {d}:".format(
                            f=self.to_url, d=device.name))
                    log.error(str(diff.diffs))
                    raise Exception(
                        "Comparison between running-config and "
                        "config-replace file '{f}' failed for device"
                        " {d}".format(f=self.to_url, d=device.name))
                else:
                    log.info(
                        "Comparison between running-config and config-replace"
                        "file '{f}' passed for device {d}".format(
                            f=self.to_url, d=device.name))

            if delete_after_restore:
                # Delete location:<filename>
                self.filetransfer = FileUtils.from_device(device)
                self.filename = self.to_url
                self.filetransfer.deletefile(target=self.to_url, device=device)

                # Verify location:<filename> deleted
                dir_output = self.filetransfer.dir(target=self.to_url,
                                                   device=device)
                for file in dir_output:
                    if self.filename in file:
                        break
                else:
                    log.info("Successfully deleted '{}'".format(self.to_url))
                    return
                raise Exception("Unable to delete '{}'".format(self.to_url))
        else:
            pass
示例#15
0
def run_module():
    # define available arguments/parameters a user can pass to the module
    module_args = dict(
        host=dict(type="str", required=True),
        port=dict(type="int", required=False),
        protocol=dict(type="str", required=False),
        username=dict(type="str", required=True),
        password=dict(type="str", required=True, no_log=True),
        os=dict(type="str", required=True),
        feature=dict(type="str", required=True),
        compare_to=dict(type="raw", required=False),
        exclude=dict(type="list", required=False),
        no_default_exclusion=dict(type="bool", required=False),
        colors=dict(type="bool", required=False)
    )
    # TODO: Add protocol so Unicon can use anything
    # print(type(module_args['compare_to']))
    # seed the result dict in the object
    # we primarily care about changed and state
    # change is if this module effectively modified the target
    # state will include any data that you want your module to pass back
    # for consumption, for example, in a subsequent task
    result = dict(changed=False)

    # the AnsibleModule object will be our abstraction working with Ansible
    # this includes instantiation, a couple of common attr would be the
    # args/params passed to the execution, as well as if the module
    # supports check mode
    module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)

    host = module.params["host"]
    if module.params.get("port") is not None:
        port = module.params["port"]
    else:
        port = 22
    username = module.params["username"]
    password = module.params["password"]
    os = module.params["os"]
    feature = module.params["feature"]
    if module.params.get("compare_to"):
        # compare_to = json.loads(module.params.get("compare_to"))
        compare_to = module.params.get("compare_to")
    if module.params.get("exclude"):
        excluded_keys = module.params.get("exclude")
    if module.params.get("no_default_exclusion") is False:
        no_default_exclusion = False
    elif module.params.get("no_default_exclusion") is None:
        no_default_exclusion = False
    else:
        no_default_exclusion = True
    if module.params.get("colors") is False:
        colors = False
    elif module.params.get("colors") is not None:
        colors = True
    else:
        colors = True
    if module.params.get("protocol") == "telnet":
        protocol = "telnet"
    else:
        protocol = "ssh"

    # if the user is working with this module in only check mode we do not
    # want to make any changes to the environment, just return the current
    # state with no modifications
    if module.check_mode:
        module.exit_json(**result)

    # Check user input
    for k, v in module.params.items():
        if k == "port" and v is not None:
            if not isinstance(v, int) and v not in range(1-65535):
                raise AnsibleError(
                    "The {} parameter must be an integer between 0-65535".format(k)
                )
        elif k == "compare_to":
            pass
        elif k == "port":
            pass
        elif k == "exclude":
            pass
        elif k == "colors":
            pass
        elif k == "no_default_exclusion":
            pass
        elif k == "protocol":
            pass
        else:
            if not isinstance(v, string_types):
                raise AnsibleError(
                    "The {} parameter must be a string such as a hostname or IP address.".format(
                        k
                    )
                )

    # Did the user pass in a feature that is supported on a given platform
    genie_ops = importlib.util.find_spec("genie.libs.ops")
    ops_file_obj = Path(genie_ops.origin).parent.joinpath("ops.json")
    with open(ops_file_obj, "r") as f:
        ops_json = json.load(f)
    supported_features = [k for k, _ in ops_json.items()]

    # Load in default exclusions for diffs for all features for genie learn
    # genie_yamls = importlib.util.find_spec("genie.libs.sdk.genie_yamls")
    # genie_excludes = Path(genie_yamls.origin).parent.joinpath("pts_datafile.yaml")
    # with open(genie_excludes, "r") as f:
    #     diff_excludes = json.load(f)
    # supported_features = [k for k, _ in ops_json.items()]

    default_excludes = {}
    from importlib import import_module
    for i in supported_features:
        modulename = "genie.libs.ops.{}.{}".format(i, i)
        package_name = i.capitalize()
        try:
            this_module = import_module(modulename, package_name)
            this_class = getattr(this_module, package_name)
            this_excludes = this_class.exclude
            default_excludes.update({i: this_excludes})
        except AttributeError:
            default_excludes.update({i: []})

        # this_module = __import__(modulename)
        # default_excludes.append({i: this_module.i)
        # from genie.libs.ops.i.i import Interface

    # Is the feature even supported?
    if feature not in supported_features:
        raise AnsibleError(
            "The feature entered is not supported on the current version of Genie.\nCurrently supported features: {0}\n{1}".format(
                to_native(supported_features),
                "https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/models",
            )
        )

    # Is the feature supported on the OS that was provided from the user?
    for f in ops_json.items():
        if feature == f[0]:
            if os not in [k for k, _ in f[1].items()]:
                raise AnsibleError(
                    "The {0} feature entered is not supported on {1}.\nCurrently supported features & platforms:\n{2}".format(
                        feature, os,
                        "https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/models",
                    )
                )

    testbed = {
        "devices": {
            host: {
                "ip": host,
                "port": port,
                "protocol": protocol,
                "username": username,
                "password": password,
                "os": os,
            }
        }
    }

    tb = load(testbed)
    dev = tb.devices[host]
    dev.connect(log_stdout=False, learn_hostname=True)
    output = dev.learn(feature)

    # Do diff if compare_to was provided
    if module.params.get("compare_to"):
        # do genie diff
        # print(type(compare_to['genie'][feature]))
        # print(type(output.info))
        # dd = Diff({"a": "yes"}, {"a": "no"})
        # with open('/tmp/compare_to.txt', 'w') as f:
        #     f.write(json.dumps(compare_to['genie'][feature]))
        # with open('/tmp/output.txt', 'w') as f:
        #     f.write(json.dumps(output.info))

        before = compare_to['genie'][feature]
        current = json.dumps(output.info)
        current = json.loads(current)
        # current = eval(str(output.info))
        try:
            excluded_keys
            if no_default_exclusion:
                merged_exclusions = excluded_keys
            else:
                merged_exclusions = list(set().union(excluded_keys, default_excludes[feature]))
            dd = Diff(before, current, exclude=merged_exclusions)
        except NameError:
            if len(default_excludes[feature]) > 0:
                if no_default_exclusion:
                    dd = Diff(before, current)
                else:
                    dd = Diff(before, current, exclude=default_excludes[feature])
            else:
                dd = Diff(before, current)
        dd.findDiff()
        if colors:
            result.update({"diff": {"prepared": '\n'.join(color_diff(str(dd)))}})
        else:
            result.update({"diff": {"prepared": str(dd)}})
        module._diff = True
        if len(str(dd)) > 0:
            result['changed'] = True

    feature_data = {
        feature: output.info
    }

    result.update({"genie": feature_data})

    # use whatever logic you need to determine whether or not this module
    # made any modifications to your target
    # if module.params['new']:
    #     result['changed'] = True

    # during the execution of the module, if there is an exception or a
    # conditional state that effectively causes a failure, run
    # AnsibleModule.fail_json() to pass in the message and the result
    # if module.params['name'] == 'fail me':
    #     module.fail_json(msg='You requested this to fail', **result)

    # in the event of a successful module execution, you will want to
    # simple AnsibleModule.exit_json(), passing the key/value results
    module.exit_json(**result)