예제 #1
0
 def get_binding_file(self, remote_filename, local_filename):
     try:
         localfd = open(local_filename, 'w+')
         ftp = FTP(self._dev._host)
         ftp.login(user=self._dev._auth_user, passwd=self._dev._auth_pwd)
         LOG.info("FTP get the file %s" % remote_filename)
         ftp.retrbinary('RETR %s' % remote_filename, localfd.write)
     except Exception as e:
         LOG.critical(
             'Failed to connect to the device: exception: %s' % e.message)
         return False
     LOG.info('Successfully copied the file %s' % remote_filename)
     return True
예제 #2
0
    def __call__(self, message):
        global dispQ
        LOG.info("Inside Handle commit notifications")
        LOG.info("Received message: %s" % str(message))
        config_dict = message['commit-patch']
        print config_dict
        try:
            sw_present = False
            for keys in config_dict:
                if keys.endswith('softwire-config'):
                    sw_present = True
                    break
            if sw_present:
                purge_request = True
                # Push all the configuration into a new file
                stub = openconfig_service_pb2.beta_create_OpenconfigRpcApi_stub(
                    self._dev.getChannel())

                get_request = openconfig_service_pb2.GetRequestList(
                    operation_id="1001",
                    operation=1,
                    path="/configuration/ietf-softwire:softwire-config")
                request = openconfig_service_pb2.GetRequest(
                    request_id=1002, encoding=1, get_request=[get_request])
                response = stub.Get(request,
                                    common.app_globals.RPC_TIMEOUT_SECONDS)
                for rsp in response.response:
                    print rsp.value
                    if rsp.response_code == openconfig_service_pb2.OK and rsp.value != "":
                        print rsp

                        LOG.info(
                            "Invoked the getRequest for snabb configuration")
                        config_dict = json.loads(
                            rsp.value)["ietf-softwire:softwire-config"]
                        LOG.debug(
                            "Notification message contains the config %s" %
                            (str(config_dict)))
                        purge_request = False
                        dispQ.put(config_dict)
                if purge_request:
                    LOG.info("Softwire-config deleted in the config")
                    config_dict['purge'] = True
                    dispQ.put(config_dict)
            else:
                LOG.info("Softwire-config not present in the config")
        except Exception as e:
            LOG.critical("Exception: %s" % e.message)
            LOG.info('Exiting the JET app')
            os._exit(1)
        return
예제 #3
0
 def write_file(self, filename, templatename, dictitems):
     PATH = os.path.dirname(os.path.abspath(__file__))
     TEMPLATE_ENVIRONMENT = Environment(
         autoescape=True,
         loader=FileSystemLoader(os.path.join(PATH, 'template')),
         trim_blocks=True,
         lstrip_blocks=False)
     try:
         with open(filename, 'w') as f:
             btext = TEMPLATE_ENVIRONMENT.get_template(
                 templatename).render(context=dictitems)
             f.write(btext)
             LOG.info("Successfully written %s" % str(filename))
         return True
     except Exception as e:
         LOG.critical("Failed to write the file %s, exception: %s" %
                      (str(filename), e.message))
         return False
예제 #4
0
def Main():
    parser = argparse.ArgumentParser(
        prog=os.path.basename(__file__),
        description='Snabb VMX integration JET app')
    parser.add_argument("--host",
                        help="Host address of the JSD server",
                        type=str,
                        default=DEFAULT_RPC_HOST)
    parser.add_argument(
        "--user",
        help="Username for authentication by JET server (default:%(default)s)",
        type=str,
        default=DEFAULT_USER_NAME)
    parser.add_argument(
        "--password",
        help="Password for authentication by JET server (default:%(default)s",
        type=str,
        default=DEFAULT_PASSWORD)
    parser.add_argument(
        "--rpc_port",
        nargs='?',
        help="Port number of the JSD gRPC server. default: %(default)s",
        type=int,
        default=DEFAULT_RPC_PORT)
    parser.add_argument(
        "--notification_port",
        nargs='?',
        help="Port number of the JSD notification server. default: %(default)s",
        type=int,
        default=DEFAULT_NOTIFICATION_PORT)
    parser.add_argument("--config",
                        nargs='?',
                        help="JSON config file",
                        type=str,
                        default=None)

    args = parser.parse_args()
    if args.config is not None:
        try:
            json_cfg = {}
            with open(args.config) as json_cfg_file:
                json_cfg = json.load(json_cfg_file)
            device = Device(json_cfg['host'], json_cfg['user'],
                            json_cfg['password'], json_cfg['rpc_port'],
                            json_cfg['notification_port'])
        except Exception as e:
            LOG.error("exception :%s" % str(e.message))
            sys.exit(0)
    else:
        try:
            device = Device(args.host, args.user, args.password, args.rpc_port,
                            args.notification_port)
        except Exception as e:
            LOG.error("Exception:%s" % e.message)
            sys.exit(0)
    dispatchFunction = ParseNotification(device)
    dispatchThread = Thread(target=dispatchFunction)
    dispatchThread.setDaemon(True)
    dispatchThread.start()
    try:
        device.initialize()
        # log device initialized successfully
        print "Device initialized for the configuration updates"
        opw = OpServer()
        reactor.listenTCP(9191, server.Site(opw))
        LOG.info("Starting the reactor")
        reactor.run()

    except Exception as e:
        # log device initialization failed
        LOG.critical("JET app exiting due to exception: %s" % str(e.message))
        sys.exit(0)
    return
예제 #5
0
    def parse_for_binding(self, config_dict):
        br_addresses = OrderedDict()
        br_address_idx = -1
        softwires = []
        addresses = {}
        remote_binding_table_filename = self.myget(config_dict['binding']['br'],'binding-table-file')
        # This section will take care of the binding files and binding table entries
        # Fetch this binding file from the device
        if remote_binding_table_filename is not None:
            # New binding table is provided by the user
            new_binding_file = r'/tmp/snabbvmx.binding.new'
            # touch the new file
            open(new_binding_file, 'w+').close()
            if self.get_binding_file(remote_binding_table_filename, new_binding_file):
                # Copy the remote file to new_binding_file and create the softwires, psid and br_address tables
                # Then add to these tables the new entries from the configuration
                # At the end compare the new binding file with the old one to see if binding entries have changed
                with open(new_binding_file, 'r') as f:
                    # Walk over the file to create the binding entries
                    regex_br_address = r"softwires_([\w:]+)"
                    regex_br_entries = r"([\w:]+)+\s+(\d+.\d+.\d+.\d+),(\d+),(\d+),(\d+)"
                    for lines in f.readlines():
                        if re.search(regex_br_address, lines):
                            match = re.search(regex_br_address, lines)
                            br_addresses[match.group(0).split(
                                '_')[1]] = br_address_idx + 1
                            br_address_idx += 1
                        elif re.search(regex_br_entries, lines):
                            match = re.search(regex_br_entries, lines)
                            shift = 16 - int(match.group(4)) - \
                                int(match.group(5))
                            softwires.append('{ ipv4=%s, psid=%s, b4=%s, aftr=%s }' %
                                             (match.group(2),match.group(3),
                                              match.group(1),br_address_idx))
                            if shift > 0:
                                addresses[str(match.group(2))] = "{psid_length=%s, shift=%d}" % (match.group(4),
                                                                                                 shift)
                            else:
                                addresses[
                                    str(match.group(2))] = "{psid_length=%s}" % match.group(4)

                        else:
                            LOG.info("Ignoring this line: %s" % lines)
                os.remove(new_binding_file)
            else:
                LOG.critical(
                    'Failed to copy remote binding file onto the local disk')
        else:
            LOG.info("No binding table info found in the config")

        # Now parse the snabb config to see if there is any binding table entry
        new_instance_list = config_dict['binding']['br']['br-instances']['br-instance']
        for instances in new_instance_list:
            bt = self.myget(instances,'binding-table')
            if bt is not None:
                bte = self.myget(bt,'binding-entry')
                if bte is not None:
                    for items in bte:
                        binding_ipv6_info = self.myget(items,"binding-ipv6info")
                        ipv4 = self.myget(items,"binding-ipv4-addr")
                        b4_address = self.myget(items,"br-ipv6-addr")
                        portset = self.myget(items,'port-set')
                        psid = self.myget(portset,"psid")
                        psid_len = self.myget(portset,"psid-len")
                        offset = self.myget(portset,"psid-offset")
                        shift = 16-psid_len-offset
                        if binding_ipv6_info is not None and ipv4 is not None and b4_address is not None:
                            if shift > 0:
                                addresses[ipv4] = "{psid_length=%s, shift=%d}" % (psid_len, shift)
                            else:
                                addresses[ipv4] = "{psid_length=%s}" %psid_len
                            if b4_address in br_addresses.keys():
                                aftr = br_addresses[b4_address]
                            else:
                                aftr = len(br_addresses)
                                br_addresses[b4_address] = aftr

                            softwires.append('{ ipv4=%s, psid=%s, b4=%s, aftr=%s }' % (
                                ipv4,psid,binding_ipv6_info,aftr))
                        else:
                            LOG.info("Incomplete binding table entry in the configuration %s" %str(items))
                else:
                    LOG.info("Empty binding table entry in the configuration")
            else:
                LOG.info("No binding table configuration present")

        # Write it into a file
        with open(SNABB_BINDING_FILENAME_TMP, 'w+') as nf:
            nf.write('psid_map {\n')
            for items in sorted(addresses.iterkeys()):
                nf.write("\t" + items + " " + addresses[items] + '\n')
            nf.write("}\nbr_addresses {\n")
            for items in br_addresses.keys():
                nf.write("\t" + items + ",\n")
            nf.write("}\nsoftwires {\n")
            for items in softwires:
                nf.write("\t" + items + "\n")
            nf.write("}")

        # Determine if this binding file is different from existing
        # file
        if self.old_binding_filename is None:
            self.old_binding_filename = SNABB_BINDING_FILENAME
            os.rename(SNABB_BINDING_FILENAME_TMP, self.old_binding_filename)
            self.binding_changed = True
            LOG.info("Old binding file not present, so creating a new binding file")
        elif not filecmp.cmp(self.old_binding_filename, SNABB_BINDING_FILENAME_TMP):
            os.rename(SNABB_BINDING_FILENAME_TMP, self.old_binding_filename)
            self.binding_changed = True
            LOG.info("Binding Table has changed")
        else:
            LOG.info("Binding Table has not changed")
        if (self.binding_changed):
	    cmd = r"/usr/local/bin/snabb lwaftr compile-binding-table " + \
		  str(self.old_binding_filename)
	    try:
		output = subprocess.check_output(cmd, shell=True)
		LOG.info("Compiled the binding file, returned %s" % str(output))
	    except Exception as e:
		LOG.critical("Failed to compile the binding file returned %s" % str(e.message))
		self.binding_changed = False
        return
예제 #6
0
    def parse_snabb_config(self, config_dict):
        if config_dict.get('purge', None) is not None:
            # Call the confAction to kill all the Snabb applications after
            # deleting the cfg/conf/binding files
            self.old_cfg = []
            self.old_conf = []
            self.old_binding_filename = None
            self.instances = {}
	    LOG.info("Purging the snabb configuration files and terminating instances")
            ca = ConfAction()
            ca.deleteAction()
            return
        LOG.info("entered parse_snabb_config")
        # At first lets clear the present flag in all the instances
        for keys in self.instances:
            self.instances[keys] = 0

        # Action handler to commit actions for conf/cfg/binding changes
        action_handler = ConfAction()

        # First the binding entry changes
        self.binding_changed = self.parse_for_binding(config_dict)

        # description is same for all the instances in the YANG schema
        cfg_dict = {}
        conf_dict = {}
        # Parse the config and cfg changes
        descr = self.myget(config_dict,'description')
        new_instance_list = config_dict['binding']['br']['br-instances']['br-instance']
        for instances in new_instance_list:
            # TODO try except loop has to be implemented
            instance_id = self.myget(instances,'id')
            # Verify that the old config contains this instance, if not then we
            # need to delete this instance
            self.instances[instance_id] = 1
            cfg_dict['id'] = instance_id
            cfg_dict['cnf_file_name'] = SNABB_FILENAME + str(instance_id) + '.conf'

            cfg_dict['ipv4_address'] = self.myget(instances,'ipv4_address')
            cfg_dict['ipv4_desc'] = descr
            cfg_dict['ipv4_cache_rate'] = self.myget(instances,'cache_refresh_interval')
            cfg_dict['ipv4_ingress_filter'] = self.myget(instances, 'ipv4_ingress_filter')
            cfg_dict['ipv4_egress_filter'] = self.myget(instances, 'ipv4_egress_filter')
            cfg_dict['fragmentation'] = self.myget(instances, 'fragmentation')


            cfg_dict['ipv6_address'] = self.myget(instances,'ipv6_address')
            cfg_dict['ipv6_desc'] = descr
            cfg_dict['ipv6_cache_rate'] = self.myget(instances,'cache_refresh_interval')
            cfg_dict['ipv6_ingress_filter'] = self.myget(instances, 'ipv6_ingress_filter')
            cfg_dict['ipv6_egress_filter'] = self.myget(instances, 'ipv6_egress_filter')
            cfg_dict['fragmentation'] = self.myget(instances, 'fragmentation')

            cfg_dict['ingress_drop_interval'] = self.myget(instances,'ingress_drop_interval')
            cfg_dict['ingress_drop_monitor'] = self.myget(instances,'ingress_drop_monitor')
            cfg_dict['ingress_drop_threshold'] = self.myget(instances, 'ingress_drop_threshold')
            cfg_dict['ingress_drop_wait'] = self.myget(instances,'ingress_drop_wait')
            cfg_dict['ring_buffer_size'] = None

            # Parse the conf file attributes
            conf_dict['id'] = instance_id
            if self.old_binding_filename is not None:
                conf_dict['binding_table'] = str(self.old_binding_filename)
            else:
                conf_dict['binding_table'] = None

            mac_path = SNABB_MAC_PATH + str(instance_id)
            # Read the files
            mac_id = "00:00:00:00:00:00"
            try:
                with open(mac_path) as f:
                    mac_id = f.read().strip()
            except Exception as e:
                LOG.info('Failed to read the file %s due to exception: %s' %
                         (mac_path, e.message))

            conf_dict['v4_vlan_tag'] = self.myget(instances,'v4_vlan_tag')
            conf_dict['v6_vlan_tag'] = self.myget(instances, 'v6_vlan_tag')
            if self.myget(instances,'v4_vlan_tag') is not None or self.myget(instances, 'v6_vlan_tag') is not None:
                conf_dict['vlan_tagging'] = 'true'
            else:
                conf_dict['vlan_tagging'] = 'false'

            conf_dict['policy_icmpv4_incoming'] = self.myget(instances, 'policy_icmpv4_incoming')
            conf_dict['policy_icmpv4_outgoing'] = self.myget(instances, 'policy_icmpv4_outgoing')
            conf_dict['policy_icmpv6_incoming'] = self.myget(instances,'policy_icmpv6_incoming')
            conf_dict['policy_icmpv6_outgoing'] = self.myget(instances, 'policy_icmpv6_outgoing')
            conf_dict['max_fragments_per_reassembly_packet'] = self.myget(instances, 'max_fragments_per_reassembly_packet')
            conf_dict['max_ipv4_reassembly_packets'] = self.myget(instances, 'max_ipv4_reassembly_packets')
            conf_dict['max_ipv6_reassembly_packets'] = self.myget(instances, 'max_ipv6_reassembly_packets')
            conf_dict['icmpv6_rate_limiter_n_packets'] = self.myget(instances, 'icmpv6_rate_limiter_n_packets')
            conf_dict['icmpv6_rate_limiter_n_seconds'] = self.myget(instances, 'icmpv6_rate_limiter_n_seconds')
            conf_dict['ipv4_address'] = self.myget(instances, 'ipv4_address')
            conf_dict['ipv6_mtu'] = self.myget(instances,"tunnel-path-mru")
            conf_dict['ipv4_mtu'] = self.myget(instances,"tunnel-payload-mtu")
            conf_dict['hairpinning'] = self.myget(instances,"hairpinning")

            #TODO Why are the following values hardcoded
            conf_dict['ipv6_address'] = '2001:db8::1'
            conf_dict['inet_mac'] = '02:02:02:02:02:02'
            conf_dict['next_hop6_mac'] = '02:02:02:02:02:02'

            # TODO these parameters are not defined
            conf_dict['aftr_mac_inet_side'] = mac_id
            conf_dict['aftr_mac_b4_side'] = mac_id

            # Take action based on whether the cfg or conf files have changed
            # or not
            ret_cfg, ret_conf = False, False
            new_cfg_id_present = False
            new_conf_id_present = False
            cnt = 0
            cfg_changed = False
            LOG.debug('New cfg dict = %s' % str(cfg_dict))
            if self.old_cfg is None:
                ret_cfg = self.write_snabb_cfg_file(cfg_dict, instance_id)
                self.old_cfg.append(cfg_dict)
                if not ret_cfg:
                    LOG.critical("Failed to write the cfg file")
                    return
            else:
                # Add the new cfg to the old_cfg dictionary
                cnt = 0
                for cfg_instance in self.old_cfg:
                    if cfg_instance['id'] == cfg_dict['id']:
                        # Check if the configuration has changed for this new instance
                        if cmp(cfg_instance, cfg_dict) != 0:
                            cfg_changed = True
                            self.old_cfg[cnt] = cfg_dict
                            new_cfg_id_present = True
                            LOG.info("Cfg dictionary has changed")
                            break
                        else:
                            new_cfg_id_present = True
                            cfg_changed = False
                            LOG.info("Cfg dictionary has not changed")
                            break
                    cnt += 1
                # New cfg is not in the existing dict, add it
                if not new_cfg_id_present:
                    self.old_cfg.append(cfg_dict)
                    cfg_changed = True
                if (cfg_changed):
                    ret_cfg = self.write_snabb_cfg_file(cfg_dict, instance_id)
                    if not ret_cfg:
                        LOG.critical("Failed to write the cfg file")
                        return

            if self.old_conf is None:
                ret_conf = self.write_snabb_conf_file(conf_dict, instance_id)
                self.old_conf.append(conf_dict)
                if not ret_conf:
                    LOG.critical("Failed to write the conf file")
                    return
            else:
                cnt = 0
                conf_changed = False
                new_conf_id_present = False
                for conf_instance in self.old_conf:
                    if (conf_instance['id'] == conf_dict['id']):
                        if cmp(conf_instance, conf_dict) != 0:
                            conf_changed = True
                            self.old_conf[cnt] = conf_dict
                            new_conf_id_present = True
                            LOG.info("Conf dictionary has changed")
                            break
                        else:
                            new_conf_id_present = True
                            conf_changed = False
                            LOG.info("Conf dictionary has not changed")
                    cnt = + 1
                if not new_conf_id_present:
                    self.old_conf.append(conf_dict)
                    conf_changed = True
                if (conf_changed):
                    ret_conf = self.write_snabb_conf_file(
                        conf_dict, instance_id)
                    if not ret_conf:
                        LOG.critical("Failed to write the conf file")
                        return

            if ret_conf or ret_cfg:
                # Assume that the instances list is populated here
                if not new_cfg_id_present and not new_conf_id_present:
                    # It is a new instance, so start it
                    LOG.info("New id added, starting new instance")
                    ret = action_handler.start_snabb_instance(instance_id)
                else:
                    LOG.info("The configuration changed, restarting the snabb instance id %s" %instance_id)
                    ret = action_handler.cfgAction(instance_id)
                    if not ret:
                        LOG.critical("Failed to restart the Snabb instance")
	    elif self.binding_changed:
		# Only the binding table has changed.
		# Send SIGHUP to the instances
		LOG.info("Only binding table has changed, sending SIGHUP")
		ret = action_handler.bindAction(self.old_binding_filename)
		if not ret:
		    LOG.critical("Failed to reload the binding tables")
		self.binding_changed = False
            else:
                LOG.info("No config change hence did not restart Snabb instance %s id" %instance_id)

        # Few of the instances might have been deleted, we need to kill those
        # instances
        for keys in self.instances:
            if self.instances[keys] == 0:
                # Remove the old_cfg item which is for this id
                cnt = 0
                for cfg_instance in self.old_cfg:
                    if cfg_instance['id'] == keys:
                        del self.old_cfg[cnt]
                        break
                    cnt += 1

                cnt = 0
                for conf_instance in self.old_conf:
                    if conf_instance['id'] == keys:
                        del self.old_conf[cnt]
                        break
                    cnt += 1
		# Purge the files needed for this instance
                LOG.info(
                    "Instance id %d is not present, need to kill it" % int(keys))
                ret = action_handler.cfgAction(keys, False)
                if not ret:
                    LOG.critical(
                        "Failed to kill the Snabb instance %d" % int(keys))
        return