diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index e43b0f9..0000000
--- a/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.DS_Store
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index d427698..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "devdocs-theme"]
- path = devdocs-theme
- url = https://github.com/citrix/devdocs-theme.git
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..bed3e2c
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,7 @@
+### CITRIX TOOL LICENSE AGREEMENT
+
+Use of this Citrix software is subject to the Citrix license covering the specific edition of the Citrix product with which you will be using this software. Citrix’s standard end-user license agreement (EULA) for its on-premises software and hardware offerings and its standard end-user service agreement (EUSA) for its Citrix Cloud and other SaaS offerings are available at https://www.citrix.com/buy/licensing/agreements.html. Your use of this software is limited to use in connection with the Citrix product(s) to which you are licensed.
+Certain third-party software may be provided with this software that is subject to separate license conditions. The licenses are located in the third-party licenses file accompanying this component or in the corresponding license files available at www.citrix.com .
+
+Citrix and other marks are trademarks and/or registered trademarks of Citrix Systems, Inc. in the U.S. and other countries.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..11e36c1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,27 @@
+![Citrix Logo](media/Citrix_Logo_Trademark.png)
+
+# Citrix ADC scripts for migrating and converting Citrix ADC configuration with deprecated features
+
+## Description
+
+When you migrate from a Citrix ADC version with deprecated features, you may lose some of the configuration. Citrix provides you scripts to avoid such configuration loss when you are migrating from an old version with deprecated features to the newer version.
+
+This repository contains the following scripts:
+
+- [`tdToPartition.pl`](td-to-ap/tdToPartition.pl): The script for migrating the traffic domain configuration on a Citrix ADC to the admin partition configuration. For more information on how to use the script, see [Migrating traffic domain configuration on a Citrix ADC to admin partition configuration](td-to-ap/migration-script-td.md).
+
+- [`check_invalid_config`](nspepi/check_invalid_config): Pre-validation script to check if any deprecated functionality that is removed from Citrix ADC release version 13.1 is still used in the configuration. For more information on how to use the script, see [Scripts for pre-validating and converting deprecated features](nspepi/validation-conversion-script.md).
+
+- [`NSPEPI`](nspepi/nspepi): The script that converts deprecated commands or features to non-deprecated commands or features. For more information on how to use the script, see [Scripts for pre-validating and converting deprecated features](nspepi/validation-conversion-script.md).
+
+
+## Questions
+
+For questions and support, the following channels are available:
+
+- [Citrix Discussion Forum](https://discussions.citrix.com/)
+
+
+## Licensing
+
+The Citrix ADC scripts are licensed with [CITRIX TOOL LICENSE](LICENSE.md).
\ No newline at end of file
diff --git a/devdocs-theme b/devdocs-theme
deleted file mode 160000
index aa7990f..0000000
--- a/devdocs-theme
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit aa7990fc4ab6ce924327f6a9c65eedf0226bf4ae
diff --git a/docs/index.md b/docs/index.md
deleted file mode 100644
index 3934c42..0000000
--- a/docs/index.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Citrix ADC Ansible Module Documentation
-
-This project implements a set of Ansible modules for configuring Citrix ADC appliances. Users of these modules can create, edit, update, and delete configuration objects on a Citrix ADC appliance.
-
-The code is licensed under **GPL**. The authoritative repository, which includes the code and the detailed documentation, is on GitHub at [Citrix ADC Ansible Module](https://github.com/citrix/citrix-adc-ansible-modules).
diff --git a/media/Citrix_Logo_Trademark.png b/media/Citrix_Logo_Trademark.png
new file mode 100644
index 0000000..dad3bcc
Binary files /dev/null and b/media/Citrix_Logo_Trademark.png differ
diff --git a/mkdocs.yml b/mkdocs.yml
deleted file mode 100644
index c0196b1..0000000
--- a/mkdocs.yml
+++ /dev/null
@@ -1,72 +0,0 @@
-# Project information
-site_name: Citrix NetScaler Ansible Module Documentation
-site_title: Developer Docs
-site_description: Documentation for Citrix NetScaler Ansible Modules 0.1
-site_url: https://developer-docs.citrix.com
-
-# Feedback
-
-issues_uri: https://github.com/citrix/devdocs-issue-collector/issues/new
-assignees_uri:
- - ajitchelat
-labels_uri:
- - documentation
-
-# Copyright
-copyright: '© 1999-2020 Citrix Systems, Inc. All rights reserved.'
-
-extra_css:
- - 'assets/stylesheets/extra.css'
- - https://use.fontawesome.com/releases/v5.12.1/css/all.css
-
-
-# Configuration
-theme:
- name: null
- custom_dir: devdocs-theme
-
- # 404 page
- static_templates:
- - 404.html
-
- # Don't include MkDocs' JavaScript
- include_search_page: false
- search_index_only: true
-
- # Default values, taken from mkdocs_theme.yml
- language: en
- features:
- - tabs: false
- palette:
- primary: teal
- accent: teal
- font: false
- favicon: https://docs.citrix.com/assets/images/favicon.ico
- logo: https://developer-docs.citrix.com/_static/logo.svg
- icon: fontawesome
-
-extra:
- social:
- - icon: fontawesome/brands/youtube
- link: https://www.youtube.com/channel/UCiOupk9QF6jdk3EDKTHDykA
- - icon: fontawesome/brands/github-alt
- link: https://github.com/citrix
- - icon: fontawesome/brands/twitter
- link: https://twitter.com/citrixdeveloper
- - icon: fontawesome/brands/linkedin
- link: https://www.linkedin.com/company/citrix/
-
-markdown_extensions:
- - admonition
- - codehilite
- - toc:
- permalink: true
-
-plugins:
- - search:
- separator: '[\s\-\.]+'
-
-# Page tree
-nav:
- - Home: index.md
- - Questions?: https://discussions.citrix.com/forum/1385-citrix-developer-exchange/
diff --git a/nspepi/check_invalid_config b/nspepi/check_invalid_config
new file mode 100755
index 0000000..57b2034
--- /dev/null
+++ b/nspepi/check_invalid_config
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+$ENV{PATH} = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin'.$ENV{PATH};
+
+use File::Basename;
+
+my $number_args = $#ARGV + 1;
+if ($number_args != 1) {
+ print "Usage: check_invalid_config \n";
+ exit;
+}
+
+my $config_file = $ARGV[0];
+if (not -e $config_file) {
+ print "No such file: $config_file\n";
+ exit;
+}
+
+my($filename, $dir_path) = fileparse($config_file);
+
+my $exit_status = system("python2 /netscaler/nspepi2/config_check_main.py -f $config_file");
+if ($exit_status != 0) {
+ print "Error in checking config file: $exit_status";
+ exit;
+}
+my $invalid_config_file = $dir_path."/issues_".$filename;
+
+# Checks whether any command is present in the file
+if (!(-z $invalid_config_file)) {
+ print "\nThe following configuration lines will get errors in 13.1 and both they and dependent configuration will be removed from the configuration:\n";
+ system("cat $invalid_config_file");
+ print "\nThe nspepi upgrade tool can be useful in converting your configuration - see the documentation at https://docs.citrix.com/en-us/citrix-adc/current-release/appexpert/policies-and-expressions/introduction-to-policies-and-exp/converting-policy-expressions-nspepi-tool.html. Use the latest tool version for the most complete and up-to-date version.\n";
+} else {
+ print "\nNo issue detected with the configuration.\n";
+}
+### End check_invalid_config script
diff --git a/nspepi/nspepi b/nspepi/nspepi
new file mode 100755
index 0000000..c867d5d
--- /dev/null
+++ b/nspepi/nspepi
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+exec $DIR/nspepi2/nspepi_main.py "$@"
diff --git a/nspepi/nspepi2/check_classic_configs.py b/nspepi/nspepi2/check_classic_configs.py
new file mode 100644
index 0000000..ca3bb79
--- /dev/null
+++ b/nspepi/nspepi2/check_classic_configs.py
@@ -0,0 +1,624 @@
+#!/usr/bin/env python2
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+import copy
+
+import cli_lex
+import nspepi_common as common
+import check_classic_expr
+from nspepi_parse_tree import *
+
+
+def check_configs_init():
+ """Initialize global variables uses by this module"""
+ global policy_entities_names
+ global classic_entities_names
+ global named_expr
+ named_expr = {}
+ policy_entities_names = set()
+ classic_entities_names = set()
+ # Register built-in named expressions.
+ NamedExpression.register_built_in_named_exprs()
+
+
+def remove_quotes(val):
+ """
+ Helper function to remove the surrounding
+ quotes from a CLI parameter.
+ val - CLI parameter that needs quotes removed.
+ Returns the dequoted CLI parameter
+ """
+ result = val
+ if val.startswith('"') or val.startswith("'"):
+ lexer = cli_lex.Lexer()
+ lexer.input(val)
+ token = lexer.token()
+ assert token.type == "NON_KEY"
+ result = token.value
+ return result
+
+
+def is_classic_named_expr_present(expr):
+ """
+ Helper function to check that
+ classic expression names present in the
+ given expression or not.
+ expr - Expression in which classic
+ expression names need to be found.
+ Returns True if classic named expression is present,
+ otherwise returns False.
+ """
+ lexer = cli_lex.Lexer()
+ lexer.input(expr)
+ classic_expr_info_list = []
+ while True:
+ next_token = lexer.adv_expr_token()
+ if not next_token:
+ break
+ token_value = str(next_token)
+ if token_value in NamedExpression.built_in_named_expr:
+ return True
+ elif token_value in classic_entities_names:
+ return True
+ return False
+
+class CheckConfig(object):
+ """Base class to check the config"""
+
+ @staticmethod
+ def check_pos_expr(commandParseTree, pos):
+ """
+ Check the expression present at a given position
+ commandParseTree - the parse tree to modify
+ pos - the position of the parameter to modify
+ If the expression is classic, then invalid
+ flag would be set.
+ """
+ rule_node = commandParseTree.positional_value(pos)
+ rule_expr = rule_node.value
+ converted_expr = check_classic_expr.check_classic_expr(rule_expr)
+ if converted_expr is None:
+ logging.error('Error in checking command : ' +
+ str(commandParseTree))
+ else:
+ # converted_expr will have quotes and rule_expr will not have
+ # quotes. Since we are comparing these 2 expressions, removing
+ # quotes from converted_expr.
+ converted_expr = remove_quotes(converted_expr)
+ if converted_expr != rule_expr:
+ # expression is converted, this is classic.
+ commandParseTree.set_invalid()
+ if is_classic_named_expr_present(converted_expr):
+ commandParseTree.set_invalid()
+ return commandParseTree
+
+ @staticmethod
+ def check_keyword_expr(commandParseTree, keywordName):
+ """
+ Check the expression present as a value of
+ the given keyword name.
+ commandParseTree - the parse tree to modify
+ keywordName - the name of the keyword parameter to modify
+ If the expression is classic, then invalid
+ flag would be set.
+ """
+ if not commandParseTree.keyword_exists(keywordName):
+ return commandParseTree
+ rule_node = commandParseTree.keyword_value(keywordName)
+ rule_expr = rule_node[0].value
+ converted_expr = check_classic_expr.check_classic_expr(rule_expr)
+ if converted_expr is None:
+ logging.error('Error in checking command : ' +
+ str(commandParseTree))
+ else:
+ # converted_expr will have quotes and rule_expr will not have
+ # quotes. Since we are comparing these 2 expressions, removing
+ # quotes from converted_expr.
+ converted_expr = remove_quotes(converted_expr)
+ if converted_expr != rule_expr:
+ # expression is converted, this is classic.
+ commandParseTree.set_invalid()
+ if is_classic_named_expr_present(converted_expr):
+ commandParseTree.set_invalid()
+ return commandParseTree
+
+ @staticmethod
+ def register_policy_entity_name(commandParseTree):
+ """ Add the entity name in the global list."""
+ name = commandParseTree.positional_value(0).value.lower()
+ policy_entities_names.add(name)
+
+ @staticmethod
+ def register_classic_entity_name(commandParseTree):
+ """ Add the classic entity name in the classic global list."""
+ name = commandParseTree.positional_value(0).value.lower()
+ classic_entities_names.add(name)
+
+
+@common.register_class_methods
+class CacheRedirection(CheckConfig):
+ """ Handle CR feature """
+
+ # Classic built-in policy names
+ built_in_policies = [
+ "bypass-non-get",
+ "bypass-cache-control",
+ "bypass-dynamic-url",
+ "bypass-urltokens",
+ "bypass-cookie"
+ ]
+
+
+ @common.register_for_cmd("add", "cr", "policy")
+ def check_policy(self, commandParseTree):
+ """
+ Checks classic CR policy.
+ """
+ policy_name = commandParseTree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ """
+ If action field is not set, then it is classic policy,
+ else it is an advanced policy.
+ """
+ if commandParseTree.keyword_exists('action'):
+ return []
+ else:
+ return [commandParseTree]
+
+ @common.register_for_cmd("bind", "cr", "vserver")
+ def check_cr_vserver_bind(self, bind_parse_tree):
+ """
+ Handles CR vserver bind command.
+ bind cr vserver -policyName
+ -priority -gotoPriorityExpression
+ """
+ if not bind_parse_tree.keyword_exists('policyName'):
+ return []
+
+ policy_name = bind_parse_tree.keyword_value("policyName")[0].value
+ class_name = self.__class__.__name__
+ policy_type = common.pols_binds.get_policy(policy_name).module
+ # When policy is CR policy.
+ if policy_type == class_name:
+ # check for classic built-in policy.
+ if policy_name in self.built_in_policies:
+ return [bind_parse_tree]
+
+ return []
+
+
+@common.register_class_methods
+class SSL(CheckConfig):
+ """ Handle SSL feature """
+
+ @common.register_for_cmd("add", "ssl", "policy")
+ def check_policy(self, commandParseTree):
+ """
+ Check classic SSL policy.
+ """
+
+ commandParseTree = SSL.check_keyword_expr(commandParseTree, 'rule')
+ if commandParseTree.invalid:
+ return [commandParseTree]
+ return []
+
+
+@common.register_class_methods
+class APPFw(CheckConfig):
+ """ Handle APPFw feature """
+
+ @common.register_for_cmd("add", "appfw", "policy")
+ def check_policy(self, commandParseTree):
+ """
+ Check classic AppFw policy
+ """
+ commandParseTree = APPFw.check_pos_expr(commandParseTree, 1)
+ if commandParseTree.invalid:
+ return [commandParseTree]
+ return []
+
+
+@common.register_class_methods
+class Patset(CheckConfig):
+ """ Patset entity """
+
+ @common.register_for_cmd("add", "policy", "patset")
+ def register_name(self, commandParseTree):
+ Patset.register_policy_entity_name(commandParseTree)
+ if commandParseTree.keyword_exists('indexType'):
+ return [commandParseTree]
+ return []
+
+
+@common.register_class_methods
+class Dataset(CheckConfig):
+ """ Dataset entity """
+
+ @common.register_for_cmd("add", "policy", "dataset")
+ def register_name(self, commandParseTree):
+ Dataset.register_policy_entity_name(commandParseTree)
+ if commandParseTree.keyword_exists('indexType'):
+ return [commandParseTree]
+ return []
+
+
+@common.register_class_methods
+class HTTP_CALLOUT(CheckConfig):
+ """ HTTP callout entity """
+
+ @common.register_for_cmd("add", "policy", "httpCallout")
+ def register_name(self, commandParseTree):
+ HTTP_CALLOUT.register_policy_entity_name(commandParseTree)
+ return []
+
+
+@common.register_class_methods
+class StringMap(CheckConfig):
+ """ String map entity """
+
+ @common.register_for_cmd("add", "policy", "stringmap")
+ def register_name(self, commandParseTree):
+ StringMap.register_policy_entity_name(commandParseTree)
+ return []
+
+
+@common.register_class_methods
+class NSVariable(CheckConfig):
+ """ NS Variable entity """
+
+ @common.register_for_cmd("add", "ns", "variable")
+ def register_name(self, commandParseTree):
+ NSVariable.register_policy_entity_name(commandParseTree)
+ return []
+
+
+@common.register_class_methods
+class EncryptionKey(CheckConfig):
+ """ Encryption key entity """
+
+ @common.register_for_cmd("add", "ns", "encryptionKey")
+ def register_name(self, commandParseTree):
+ EncryptionKey.register_policy_entity_name(commandParseTree)
+ return []
+
+
+@common.register_class_methods
+class HMACKey(CheckConfig):
+ """ HMAC key entity """
+
+ @common.register_for_cmd("add", "ns", "hmacKey")
+ def register_name(self, commandParseTree):
+ HMACKey.register_policy_entity_name(commandParseTree)
+ return []
+
+
+@common.register_class_methods
+class NamedExpression(CheckConfig):
+ """ Handle Named expression feature """
+
+ # Built-in classic named expression names
+ built_in_named_expr = {
+ "ns_true",
+ "ns_false",
+ "ns_non_get",
+ "ns_cachecontrol_nostore",
+ "ns_cachecontrol_nocache",
+ "ns_header_pragma",
+ "ns_header_cookie",
+ "ns_ext_cgi",
+ "ns_ext_asp",
+ "ns_ext_exe",
+ "ns_ext_cfm",
+ "ns_ext_ex",
+ "ns_ext_shtml",
+ "ns_ext_htx",
+ "ns_url_path_cgibin",
+ "ns_url_path_exec",
+ "ns_url_path_bin",
+ "ns_url_tokens",
+ "ns_ext_not_gif",
+ "ns_ext_not_jpeg",
+ "ns_cmpclient",
+ "ns_slowclient",
+ "ns_content_type",
+ "ns_msword",
+ "ns_msexcel",
+ "ns_msppt",
+ "ns_css",
+ "ns_xmldata",
+ "ns_mozilla_47",
+ "ns_msie"
+ }
+
+ @staticmethod
+ def register_built_in_named_exprs():
+ """
+ Register built-in classic Named expression names in
+ classic_entities_names.
+ """
+ for classic_exp_name in NamedExpression.built_in_named_expr:
+ classic_entities_names.add(classic_exp_name)
+
+ @common.register_for_cmd("add", "policy", "expression")
+ def check_policy_expr(self, commandParseTree):
+ """
+ Classic named expression name is not
+ valid for advanced expression if:
+ 1. It the name is same as one of the Policy
+ entity (patset/dataset/stringmap/
+ variable/hmacKey/EncriptionKey/callout) name.
+ 2. it doesn't start with ASCII alphabetic character or underscore.
+ 3. it has characters other than ASCII alphanumerics
+ or underscore characters.
+ 4. it is equal to a advanced policy expression reserved word (prefix identifier or
+ enum value)
+ """
+ reserved_word_list = set(
+ [ # Advanced policy expression prefix list
+ "subscriber",
+ "connection",
+ "analytics",
+ "diameter",
+ "target",
+ "server",
+ "radius",
+ "oracle",
+ "extend",
+ "client",
+ "mysql",
+ "mssql",
+ "false",
+ "true",
+ "text",
+ "smpp",
+ "icap",
+ "http",
+ "url",
+ "sys",
+ "sip",
+ "ica",
+ "dns",
+ "aaa",
+ "re",
+ "xp",
+ "ce"
+ ])
+
+ expr_name = commandParseTree.positional_value(0).value
+ expr_rule = commandParseTree.positional_value(1).value
+ named_expr[expr_name] = expr_rule
+ lower_expr_name = expr_name.lower()
+ if (((lower_expr_name in reserved_word_list) or
+ (re.match('^[a-z_][a-z0-9_]*$', lower_expr_name) is None) or
+ (lower_expr_name in policy_entities_names))):
+ logging.error(("Expression name {} is invalid for advanced "
+ "expression: names must begin with an ASCII "
+ "alphabetic character or underscore and must "
+ "contain only ASCII alphanumerics or underscores"
+ " and shouldn't be name of another policy entity"
+ "; words reserved for policy use may not be used;"
+ " underscores will be substituted for any invalid"
+ " characters in corresponding advanced name")
+ .format(expr_name))
+
+ if commandParseTree.keyword_exists('clientSecurityMessage'):
+ NamedExpression.register_classic_entity_name(commandParseTree)
+ return []
+
+ original_tree = copy.deepcopy(commandParseTree)
+ commandParseTree = NamedExpression \
+ .check_pos_expr(commandParseTree, 1)
+
+ if commandParseTree.invalid:
+ """
+ Add the commands in the global list which will be used to
+ check whether any other expression is using these named
+ expressions.
+ """
+ NamedExpression.register_policy_entity_name(commandParseTree)
+ NamedExpression.register_classic_entity_name(original_tree)
+ else:
+ NamedExpression.register_policy_entity_name(original_tree)
+ return []
+
+
+@common.register_class_methods
+class HTTPProfile(CheckConfig):
+ """ Handle HTTP Profile """
+
+ @common.register_for_cmd("add", "ns", "httpProfile")
+ @common.register_for_cmd("set", "ns", "httpProfile")
+ def check_spdy(self, commandParseTree):
+ """
+ Check if spdy parameter present in HTTP profile.
+ Syntax:
+ """
+ if commandParseTree.keyword_exists('spdy'):
+ return [commandParseTree]
+ return []
+
+
+@common.register_class_methods
+class ContentSwitching(CheckConfig):
+ """ Check Content Switching feature """
+
+ @common.register_for_cmd("add", "cs", "policy")
+ def check_cs_policy(self, commandParseTree):
+ if commandParseTree.keyword_exists('action'):
+ return []
+ if commandParseTree.keyword_exists('rule'):
+ if commandParseTree.keyword_exists('domain'):
+ return [commandParseTree]
+ else:
+ original_cmd = copy.deepcopy(commandParseTree)
+ commandParseTree = ContentSwitching \
+ .check_keyword_expr(commandParseTree, 'rule')
+ if commandParseTree.invalid:
+ return [original_cmd]
+ elif commandParseTree.keyword_exists('url'):
+ return [commandParseTree]
+ elif commandParseTree.keyword_exists('domain'):
+ return [commandParseTree]
+
+ return []
+
+
+@common.register_class_methods
+class CMP(CheckConfig):
+ """
+ Checks CMP feature commands.
+ """
+
+ # Classic built-in policy names.
+ built_in_policies = [
+ "ns_cmp_content_type",
+ "ns_cmp_msapp",
+ "ns_cmp_mscss",
+ "ns_nocmp_mozilla_47",
+ "ns_nocmp_xml_ie"
+ ]
+
+ @common.register_for_cmd("set", "cmp", "parameter")
+ def set_cmp_parameter(self, cmp_param_tree):
+ if cmp_param_tree.keyword_exists("policyType"):
+ self._initial_cmp_parameter = \
+ cmp_param_tree.keyword_value("policyType")[0].value.lower()
+ if self._initial_cmp_parameter == "classic":
+ return [cmp_param_tree]
+ return []
+
+ @common.register_for_cmd("set", "cmp", "policy")
+ def set_cmp_policy(self, cmp_policy_tree):
+ policy_name = cmp_policy_tree.positional_value(0).value
+ if policy_name in self.built_in_policies:
+ return [cmp_policy_tree]
+ return []
+
+ @common.register_for_cmd("add", "cmp", "policy")
+ def check_cmp_policy(self, cmp_policy_tree):
+ original_cmd = copy.deepcopy(cmp_policy_tree)
+ CheckConfig.check_keyword_expr(cmp_policy_tree, 'rule')
+ if cmp_policy_tree.invalid:
+ return [original_cmd]
+ return []
+
+ @common.register_for_cmd("bind", "cmp", "global")
+ def check_cmp_global_bind(self, bind_cmd_tree):
+ """
+ Checks CMP policy bindings to cmp global.
+ """
+ # If state keyword is present then it is a
+ # classic binding.
+ if bind_cmd_tree.keyword_exists("state"):
+ return [bind_cmd_tree]
+
+ policy_name = bind_cmd_tree.positional_value(0).value
+ if policy_name in self.built_in_policies:
+ return [bind_cmd_tree]
+ return []
+
+
+@common.register_class_methods
+class CLITransformFilter(CheckConfig):
+ """
+ Checks Filter feature
+ """
+
+ @common.register_for_cmd("add", "filter", "action")
+ def check_filter_action(self, action_parse_tree):
+ """
+ Check Filter action
+ """
+ return [action_parse_tree]
+
+ @common.register_for_cmd("add", "filter", "policy")
+ def check_filter_policy(self, policy_parse_tree):
+ """
+ Check Filter policy
+ """
+ return [policy_parse_tree]
+
+ @common.register_for_cmd("bind", "filter", "global")
+ def check_filter_global_bindings(self, bind_parse_tree):
+ """
+ Check Filter global binding
+ """
+ return [bind_parse_tree]
+
+ @common.register_for_cmd("add", "filter", "htmlinjectionvariable")
+ @common.register_for_cmd("set", "filter", "htmlinjectionvariable")
+ @common.register_for_cmd("set", "filter", "htmlinjectionparameter")
+ @common.register_for_cmd("set", "filter", "prebodyInjection")
+ @common.register_for_cmd("set", "filter", "postbodyInjection")
+ def check_filter_htmlinjection_command(self, cmd_parse_tree):
+ """
+ Check Filter HTMLInjection command
+ """
+ return [cmd_parse_tree]
+
+
+@common.register_class_methods
+class Rewrite(CheckConfig):
+ """
+ Check rewrite action
+ """
+
+ @common.register_for_cmd("add", "rewrite", "action")
+ def check_rewrite_action(self, tree):
+ if tree.keyword_exists('pattern'):
+ return [tree]
+ if tree.keyword_exists('bypassSafetyCheck'):
+ return [tree]
+ return []
+
+
+@common.register_class_methods
+class LB(CheckConfig):
+ """
+ Check LB persistence rule
+ """
+
+ @common.register_for_cmd("add", "lb", "vserver")
+ def check_rewrite_action(self, commandParseTree):
+ commandParseTree = LB.check_keyword_expr(commandParseTree, 'rule')
+ if commandParseTree.invalid:
+ return [commandParseTree]
+ return []
+
+
+@common.register_class_methods
+class SureConnect(CheckConfig):
+ """
+ Check SureConnect commands
+ """
+
+ @common.register_for_cmd("add", "sc", "policy")
+ @common.register_for_cmd("set", "sc", "parameter")
+ def check_sc_policy(self, tree):
+ return [tree]
+
+
+@common.register_class_methods
+class PriorityQueuing(CheckConfig):
+ """
+ Check PriorityQueuing commands
+ """
+
+ @common.register_for_cmd("add", "pq", "policy")
+ def check_sc_policy(self, tree):
+ return [tree]
+
+
+@common.register_class_methods
+class HDoSP(CheckConfig):
+ """
+ Check HTTP Denial of Service Protection commands
+ """
+
+ @common.register_for_cmd("add", "dos", "policy")
+ def check_sc_policy(self, tree):
+ return [tree]
diff --git a/nspepi/nspepi2/check_classic_expr.py b/nspepi/nspepi2/check_classic_expr.py
new file mode 100644
index 0000000..c6902b4
--- /dev/null
+++ b/nspepi/nspepi2/check_classic_expr.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python2
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+import logging
+import subprocess
+from nspepi_parse_tree import CLIParseTreeNode
+import nspepi_common as common
+
+
+def check_classic_expr(classic_expr):
+ tree_obj = CLIParseTreeNode()
+ info_msg = 'INFO: Expression is not converted' + \
+ ' - most likely it is a valid advanced expression'
+ warn_msg = 'WARNING: Line numbers which has ' + \
+ 'more than 8191 characters length: 0'
+ try:
+ nspepi_tool_path = common.get_nspepi_tool_path()
+ """Error message will be in the staring of
+ output, whereas warning and info messages
+ will be present in the last."""
+ nspepi_tool_output = subprocess.check_output(
+ ['perl', nspepi_tool_path, '-e', classic_expr],
+ shell=False, stderr=subprocess.STDOUT)
+ """ old nspepi tool adds newline character at the end
+ of the converted string, so remove that character."""
+ nspepi_tool_output = nspepi_tool_output.rstrip()
+ except subprocess.CalledProcessError as exc:
+ # Log the command which is failing
+ logging.error(exc)
+ # Log the error message
+ logging.error(exc.output)
+ return None
+ if nspepi_tool_output.startswith('ERROR:'):
+ """old nspepi tool throws "ERROR: Expression is in blocked list
+ of conversion" error for vpn client security expression.
+ We are not removing client security expressions, so these
+ are valid expressions."""
+ nspepi_tool_output = tree_obj.normalize(classic_expr, True)
+ elif nspepi_tool_output.endswith(info_msg):
+ """old nspepi tool didn't convert the expression,
+ so return input expression"""
+ nspepi_tool_output = tree_obj.normalize(classic_expr, True)
+ elif nspepi_tool_output.endswith(warn_msg):
+ logging.warning(nspepi_tool_output)
+ """ If expression has more than 8191 characters, old nspepi
+ tool gives warning message at the end of the output.
+
+ old nspepi tool output:
+ WARNING: Total number of warnings due to
+ expressions length greater than 8191 characters: 1
+ WARNING: Line numbers which has more than 8191 characters length: 0
+
+ Removing warning message from the output
+ """
+ expr_end_pos = nspepi_tool_output.find("WARNING")
+ nspepi_tool_output = nspepi_tool_output[0:expr_end_pos]
+ nspepi_tool_output = nspepi_tool_output.rstrip()
+
+ return nspepi_tool_output
diff --git a/nspepi/nspepi2/cli_lex.py b/nspepi/nspepi2/cli_lex.py
new file mode 100644
index 0000000..73d79f6
--- /dev/null
+++ b/nspepi/nspepi2/cli_lex.py
@@ -0,0 +1,369 @@
+#!/usr/bin/env python2
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+"""
+ CLI lexical analyzer.
+"""
+
+import logging
+
+
+class LexToken(object):
+ """
+ Class to represent the token.
+ Instance variables:
+ type - Token type
+ value - Token value
+ lineno - line number where the
+ token value is in the data parsed
+ lexpos - Points to the token end position
+ """
+
+ def __init__(self, token_type, token_value, lineno, lex_pos):
+ self.type = token_type
+ self.value = token_value
+ self.lineno = lineno
+ self.lexpos = lex_pos
+
+ def __str__(self):
+ return self.value
+
+ def __repr__(self):
+ return "LexToken({},{},{},{})".format(
+ self.type,
+ self.value,
+ self.lineno,
+ self.lexpos
+ )
+
+
+class Lexer(object):
+ """
+ CLI lexical analyzer.
+ Instance variables:
+ data - data to be parsed
+ lex_pos - Points to the current position during parsing
+ length - length of the remaining data that
+ has to be parsed
+ token_value - value of the current token during tokenization
+ """
+
+ def __init__(self):
+ self._data = None
+ self._lex_pos = 0
+ self._length = 0
+ self._token_value = ""
+
+ def input(self, command):
+ """
+ Sets the lexical analyzer with the data that has to be parsed.
+ command - command that has to be parsed
+ """
+ self._data = command
+ self._lex_pos = 0
+ self._length = len(self._data)
+ self._token_value = ""
+
+ def token(self):
+ """
+ Returns next token in a CLI command as a LexToken object.
+ If there is no more data to be parsed, returns None.
+ """
+ start_pos = self._lex_pos
+
+ # Handling spaces that comes around the tokens
+ while self._length > 0 and (self._data[self._lex_pos] in " \t\n"):
+ self.advance_token()
+
+ # Ignoring comments
+ if self._length > 0 and self._data[self._lex_pos] == '#':
+ self.advance_token(self._length)
+
+ # When there is no more input to be parsed, returns None
+ if self._length <= 0:
+ return None
+
+ # Identifies Token type
+ token_type = None
+ if self._data[self._lex_pos] == '-':
+ token_type = "KEY_ARG"
+ # Removing '-' from the starting of the keyword
+ self.advance_token()
+
+ # quotes are not allowed in keywords
+ self._token_value = ""
+ while self._length > 0:
+ if self._data[self._lex_pos] in " \t\n":
+ return LexToken(token_type, self._token_value, 1,
+ self._lex_pos - 1)
+ else:
+ self.advance_and_append_token(self._data[self._lex_pos])
+ return LexToken(token_type, self._token_value, 1,
+ self._lex_pos - 1)
+ else:
+ token_type = "NON_KEY"
+
+ start_pos = self._lex_pos
+
+ # Handling q quote
+ qquote_start_delims = "/{<|~$^+=&%@`?"
+ qquote_end_delims = {"{": "}", "<": ">"}
+
+ if (self._length > 2 and self._data[start_pos] == 'q' and
+ self._data[start_pos + 1] in qquote_start_delims):
+ qquote_end_char = qquote_end_delims.get(self._data[start_pos + 1],
+ self._data[start_pos + 1])
+ qquote_end_index = self._data.find(qquote_end_char, start_pos + 2)
+ if (qquote_end_index != -1 and
+ (qquote_end_index == len(self._data) - 1 or
+ self._data[qquote_end_index + 1] in " \t\n")):
+ next_token = LexToken("NON_KEY",
+ self._data[start_pos + 2:
+ qquote_end_index],
+ 1, qquote_end_index)
+ self.advance_token(len(next_token.value) + 3)
+ return next_token
+
+ state = " "
+ parenthesis_counter = 0
+ self._token_value = ""
+ while self._length > 0:
+ if self._data[self._lex_pos] in "\"'":
+ if self._data[self._lex_pos] == state:
+ # end of quotes
+ state = " "
+ """
+ If token starts with quotes and the corresponding ending
+ quotes appear, then this will be the end of the token.
+ If token doesn't start with the quote,
+ then this will be the end of the quote but not the
+ end of the token.
+ """
+ if self._data[self._lex_pos] == self._data[start_pos]:
+ # Removing end quotes by not appending the character
+ self.advance_token()
+ break
+ self.advance_and_append_token(self._data[self._lex_pos])
+ elif state in "\"'":
+ # single quote within double quote or vice versa
+ self.advance_and_append_token(self._data[self._lex_pos])
+ else:
+ # now inside quotes
+ state = self._data[self._lex_pos]
+ # Removing starting quotes
+ if self._lex_pos != start_pos:
+ self.advance_and_append_token(
+ self._data[self._lex_pos])
+ else:
+ self.advance_token()
+ elif self._data[self._lex_pos] in " \t\n":
+ if state == " " and parenthesis_counter == 0:
+ break
+ # This case occurs when whitespace appears inside quotes
+ self.advance_and_append_token(self._data[self._lex_pos])
+ elif self._data[self._lex_pos] == "(":
+ self.advance_and_append_token(self._data[self._lex_pos])
+ if state not in "\"'":
+ parenthesis_counter += 1
+ elif self._data[self._lex_pos] == ")":
+ self.advance_and_append_token(self._data[self._lex_pos])
+ if state not in "\"'":
+ if parenthesis_counter > 0:
+ parenthesis_counter -= 1
+ else:
+ self.advance_token(self._length)
+ token_type = "ERROR"
+ logging.error("Data: {}".format(self._data))
+ logging.error("Unbalanced closed parenthesis")
+ break
+ elif self._data[self._lex_pos] == "\\":
+ # backslashes are escapes inside quotes
+ if state in "\"'":
+ if self._length == 1:
+ # \\ followed by end of the command
+ self.advance_and_append_token(
+ self._data[self._lex_pos])
+ token_type = "ERROR"
+ logging.error("Data: {}".format(self._data))
+ logging.error("Blackslashes inside quotes are "
+ "followed by end of the command")
+ break
+ if self._data[self._lex_pos + 1] == 't':
+ self.advance_and_append_token('\t', 2)
+ elif self._data[self._lex_pos + 1] == 'n':
+ self.advance_and_append_token('\n', 2)
+ elif self._data[self._lex_pos + 1] == 'r':
+ self.advance_and_append_token('\r', 2)
+ elif self._data[self._lex_pos + 1] in "'\"\\":
+ self.advance_and_append_token(
+ self._data[self._lex_pos + 1], 2)
+ else:
+ self.advance_and_append_token(
+ self._data[self._lex_pos])
+ else:
+ self.advance_and_append_token(self._data[self._lex_pos])
+ else:
+ self.advance_and_append_token(self._data[self._lex_pos])
+
+ if state in "\"'" or parenthesis_counter > 0:
+ # error token for not matching with any rule
+ token_type = "ERROR"
+ logging.error("Data: {}".format(self._data))
+ logging.error("Unbalanced parenthesis or quotes")
+ next_token = LexToken(token_type, self._token_value, 1,
+ self._lex_pos - 1)
+ return next_token
+
+ def advance_token(self, number=1):
+ """
+ This function increments the class instance variable lex_pos and
+ decrements the length variable by the number that is passed as
+ argument.
+ number - number by which the increment has to be done
+ """
+ self._lex_pos += number
+ self._length -= number
+
+ def advance_and_append_token(self, token_char, number=1):
+ """
+ This function advances current position by the number that is
+ passed as argument and appends the token value based on token_char.
+ number - number by which current position is incremented,
+ by default it is 1
+ token_char - character that has to be appened to the token value
+ """
+ self.advance_token(number)
+ self._token_value += token_char
+
+ @staticmethod
+ def adv_ident_char(ch):
+ """ Helper function to check whether
+ character is an Advanced identifier character:
+ letter, underscore, or digit.
+ ch - character to check
+ """
+ return (ch == "_") or ch.isdigit() or ch.isalpha()
+
+ def adv_expr_token(self):
+ """
+ This function is used to tokenize an Advanced
+ expression.
+ Note that currently this only recognizes a subset of the token types.
+ Returns next token as LexToken object.
+ If there is no more data to be parsed, returns None.
+ """
+ # Handling spaces that comes around the tokens
+ while self._length > 0 and (self._data[self._lex_pos] in " \t\r\n"):
+ self.advance_token()
+
+ # When there is no more input to be parsed, returns None
+ if self._length <= 0:
+ return None
+
+ # Identifies Token type
+ token_type = "OTHER"
+
+ start_pos = self._lex_pos
+
+ state = " "
+ self._token_value = ""
+ while self._length > 0:
+ if state == "REGEX" and self._data[self._lex_pos] == regex_end:
+ # End of regex
+ state = " "
+ self.advance_token()
+ break
+ elif self._data[self._lex_pos] in "\"'":
+ if self._data[self._lex_pos] == state:
+ # end of quotes
+ state = " "
+ # Removing end quotes by not appending the character
+ self.advance_token()
+ break
+ elif state in "\"'":
+ # single quote within double quote or vice versa
+ self.advance_and_append_token(self._data[self._lex_pos])
+ elif state == "REGEX":
+ self.advance_and_append_token(self._data[self._lex_pos])
+ else:
+ # now inside quotes
+ state = self._data[self._lex_pos]
+ # Removing starting quotes
+ self.advance_token()
+ token_type = "STRING"
+ elif state == " " and self._data[self._lex_pos] in " \t\r\n":
+ # Whitespace ends most tokens
+ break
+ elif (state == " " and self._length >= 5 and
+ self._data[self._lex_pos:self._lex_pos+2].lower() == 're'
+ and not Lexer.adv_ident_char(self._data[self._lex_pos+2])):
+ # Start of regex
+ state = "REGEX"
+ token_type = "REGEX"
+ regex_end = self._data[self._lex_pos+2]
+ self.advance_token(3)
+ elif ((self._data[self._lex_pos] == "_") or
+ (self._data[self._lex_pos].isalpha())):
+ if ((state not in "\"'" and state != "IDENTIFIER" and
+ state != "REGEX" and self._lex_pos != start_pos)):
+ # End of "other"
+ break
+ elif state not in "\"'" and state != "REGEX":
+ # start of an identifier
+ state = "IDENTIFIER"
+ token_type = "IDENTIFIER"
+ self.advance_and_append_token(self._data[self._lex_pos])
+ else:
+ self.advance_and_append_token(self._data[self._lex_pos])
+ elif (not Lexer.adv_ident_char(self._data[self._lex_pos])):
+ if state == "IDENTIFIER":
+ state = " "
+ break
+ else:
+ # More of identifier
+ self.advance_and_append_token(self._data[self._lex_pos])
+ elif self._data[self._lex_pos] == "\\":
+ # backslashes are escapes inside quotes
+ if state in "\"'":
+ if self._length == 1:
+ # \\ followed by end of the expression
+ self.advance_and_append_token(
+ self._data[self._lex_pos])
+ token_type = "ERROR"
+ logging.error("Data: {}".format(self._data))
+ logging.error("Blackslashes inside quotes are "
+ "followed by end of the expression")
+ break
+ if self._data[self._lex_pos + 1] == 't':
+ self.advance_and_append_token('\t', 2)
+ elif self._data[self._lex_pos + 1] == 'n':
+ self.advance_and_append_token('\n', 2)
+ elif self._data[self._lex_pos + 1] == 'r':
+ self.advance_and_append_token('\r', 2)
+ elif self._data[self._lex_pos + 1] in "'\"\\":
+ self.advance_and_append_token(
+ self._data[self._lex_pos + 1], 2)
+ else:
+ self.advance_and_append_token(
+ self._data[self._lex_pos])
+ else:
+ self.advance_and_append_token(self._data[self._lex_pos])
+ else:
+ self.advance_and_append_token(self._data[self._lex_pos])
+
+ if state in "\"'":
+ # error token for not matching with any rule
+ token_type = "ERROR"
+ logging.error("Data: {}".format(self._data))
+ logging.error("Unbalanced quotes")
+ elif state == "REGEX":
+ # error token for not matching with any rule
+ token_type = "ERROR"
+ logging.error("Data: {}".format(self._data))
+ logging.error("Unterminated regex")
+ next_token = LexToken(token_type, self._token_value, 1,
+ self._lex_pos - 1)
+ return next_token
diff --git a/nspepi/nspepi2/cli_yacc.py b/nspepi/nspepi2/cli_yacc.py
new file mode 100644
index 0000000..a336fce
--- /dev/null
+++ b/nspepi/nspepi2/cli_yacc.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python2
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+import ply.yacc as yacc
+import cli_lex
+from nspepi_parse_tree import *
+import logging
+
+tokens = ('NON_KEY', 'KEY_ARG')
+
+
+def p_command_empty(p):
+ 'command : empty'
+ p[0] = None
+
+
+def p_command(p):
+ 'command : command_name positional_parameters keyword_parameters'
+ p[0] = p[1]
+ p[0].add_positional_list(p[2])
+ p[0].add_keyword_list(p[3])
+
+
+def p_command_name(p):
+ 'command_name : op group ot'
+ p[0] = CLICommand(p[1], p[2], p[3])
+
+
+def p_command_name_no_ot(p):
+ 'command_name : op group'
+ p[0] = CLICommand(p[1], p[2], "")
+
+
+def p_op(p):
+ 'op : NON_KEY'
+ logging.debug("CLI lex op: " + p[1])
+ p[0] = p[1]
+
+
+def p_group(p):
+ 'group : NON_KEY'
+ logging.debug("CLI lex group: " + p[1])
+ p[0] = p[1]
+
+
+def p_ot(p):
+ 'ot : NON_KEY'
+ logging.debug("CLI lex ot: " + p[1])
+ p[0] = p[1]
+
+
+def p_empty(p):
+ 'empty :'
+ pass
+
+
+def p_pos_params(p):
+ 'positional_parameters : positional_parameters NON_KEY'
+ logging.debug("CLI lex pos: " + p[2])
+ p[0] = p[1] + [CLIPositionalParameter(p[2])]
+
+
+def p_pos_empty_param(p):
+ 'positional_parameters : empty'
+ p[0] = []
+
+
+def p_keyword_params(p):
+ 'keyword_parameters : keyword_parameters keyword_parameter'
+ p[0] = p[1] + [p[2]]
+
+
+def p_keyword_empty_param(p):
+ 'keyword_parameters : empty'
+ p[0] = []
+
+
+def p_key_param(p):
+ 'keyword_parameter : keyword keyword_value'
+ p[0] = CLIKeywordParameter(p[1])
+ p[0].add_value_list(p[2])
+
+
+def p_keyword(p):
+ 'keyword : KEY_ARG'
+ logging.debug("CLI lex key: " + p[1])
+ p[0] = CLIKeywordName(p[1])
+
+
+def p_key_val(p):
+ 'keyword_value : keyword_value NON_KEY'
+ logging.debug("CLI lex key val: " + p[2])
+ p[0] = p[1] + [p[2]]
+
+
+def p_key_empty_val(p):
+ 'keyword_value : empty'
+ p[0] = []
+
+
+# This is for syntax errors
+def p_error(p):
+ if p is None:
+ p = "EOL"
+ logging.error("CLI syntax error at " + str(p))
+
+
+_lexer = None
+_parser = None
+
+
+def cli_yacc_init():
+ """ Initialize CLI command parser
+ """
+ global _lexer
+ global _parser
+ _lexer = cli_lex.Lexer()
+ _parser = yacc.yacc(debug=False, write_tables=False)
+
+
+def cli_yacc_parse(cmd, lineno):
+ """ Parse a CLI command.
+ cmd - the CLI command
+ lineno - the line number of the command
+ returns the parse tree or None if either "empty" line or syntax error
+ """
+ tree = _parser.parse(cmd, lexer=_lexer)
+ if tree is not None:
+ tree.original_line = cmd
+ tree.lineno = lineno
+ return tree
diff --git a/nspepi/nspepi2/config_check_main.py b/nspepi/nspepi2/config_check_main.py
new file mode 100755
index 0000000..97e9278
--- /dev/null
+++ b/nspepi/nspepi2/config_check_main.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python2
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+"""
+Checks whether config file contains invalid
+epressions and features.
+
+Dependency packages: PLY, pytest
+"""
+
+# Ensure that the version string conforms to PEP 440:
+# https://www.python.org/dev/peps/pep-0440/
+__version__ = "1.0"
+
+import re
+import argparse
+import glob
+import importlib
+import logging
+import logging.handlers
+import os
+import os.path
+import sys
+from inspect import cleandoc
+import inspect
+
+import cli_yacc
+import nspepi_common as common
+
+import check_classic_configs
+
+# Log handlers that need to be saved from call to call
+file_log_handler = None
+console_log_handler = None
+
+
+def setup_logging(log_file_name, file_log_level):
+ """
+ Sets up logging for the program.
+
+ Args:
+ log_file_name: The name of the log file
+ file_log_level: The level of logs to put in the file log
+ """
+ global file_log_handler
+ global console_log_handler
+ # create logger
+ logger = logging.getLogger()
+ logger.setLevel(logging.DEBUG)
+ # if called multiple times, remove existing handlers
+ logger.removeHandler(file_log_handler)
+ logger.removeHandler(console_log_handler)
+ # create file handler and roll logs if needed
+ exists = os.path.isfile(log_file_name)
+ file_log_handler = logging.handlers.RotatingFileHandler(log_file_name,
+ mode='a',
+ backupCount=9)
+ if exists:
+ file_log_handler.doRollover()
+ # set the file log handler level
+ file_log_handler.setLevel(file_log_level)
+ # create console handler that sees even info messages
+ console_log_handler = logging.StreamHandler()
+ console_log_handler.setLevel(logging.INFO)
+ # create formatters and add them to the handlers
+ fh_format = logging.Formatter('%(asctime)s: %(levelname)s - %(message)s')
+ ch_format = logging.Formatter('%(levelname)s - %(message)s')
+ file_log_handler.setFormatter(fh_format)
+ console_log_handler.setFormatter(ch_format)
+ # add the handlers to the logger
+ logger.addHandler(file_log_handler)
+ logger.addHandler(console_log_handler)
+
+
+def output_line(line, outfile, verbose):
+ """
+ Output a (potentially) converted line.
+
+ Args:
+ line: the line to output
+ outfile: Output file to write converted commands
+ verbose: True iff converted commands should also be output to console
+ """
+ outfile.write(line)
+ if verbose:
+ logging.info(line.rstrip())
+
+
+def check_for_removed_expression(cmd, outfile, verbose):
+ """
+ Checks for the expressions which are removed.
+ Args:
+ cmd: NS config command to be checked
+ outfile: Output file to write commands using removed config
+ verbose: True iff converted commands should also be output to console
+ """
+ if re.search(r'\bSYS\s*\.\s*EVAL_CLASSIC_EXPR\s*\(',
+ cmd, re.IGNORECASE):
+ output_line(cmd, outfile, verbose)
+ return
+
+ body_expr = re.compile(r'\bHTTP\s*\.\s*REQ\s*\.\s*BODY\b\s*', re.IGNORECASE)
+ cmd_len = len(cmd)
+ for match in re.finditer(body_expr, cmd):
+ start_index = match.start()
+ length = match.end() - match.start()
+ if (((start_index + length) >= cmd_len) or
+ (cmd[start_index + length] != '(')):
+ output_line(cmd, outfile, verbose)
+ return
+
+ if re.search(r'\b((Q\.HOSTNAME)|(Q\.TRACKING)|'
+ '(Q\.METHOD)|(Q\.URL)|(Q\.VERSION)|'
+ '(Q\.CONTENT_LENGTH)|(Q\.HEADER)|'
+ '(Q\.IS_VALID)|(Q\.DATE)|'
+ '(Q\.COOKIE)|(Q\.BODY)|(Q\.TXID)|'
+ '(Q\.CACHE_CONTROL)|(Q\.USER)|'
+ '(Q\.IS_NTLM_OR_NEGOTIATE)|'
+ '(Q\.FULL_HEADER)|'
+ '(Q\.LB_VSERVER)|(Q\.CS_VSERVER))',
+ cmd, re.IGNORECASE):
+ output_line(cmd, outfile, verbose)
+ return
+
+ if re.search(r'\b((S\.VERSION)|(S\.STATUS)|'
+ '(S\.STATUS_MSG)|(S\.IS_REDIRECT)|'
+ '(S\.IS_INFORMATIONAL)|(S\.IS_SUCCESSFUL)|'
+ '(S\.IS_CLIENT_ERROR)|(S\.IS_SERVER_ERROR)|'
+ '(S\.TRACKING)|(S\.HEADER)|(S\.FULL_HEADER)|'
+ '(S\.IS_VALID)|(S\.DATE)|(S\.BODY)|'
+ '(S\.SET_COOKIE)|(S\.SET_COOKIE2)|'
+ '(S\.CONTENT_LENGTH)|'
+ '(S\.CACHE_CONTROL)|(S\.TXID)|(S\.MEDIA))',
+ cmd, re.IGNORECASE):
+ output_line(cmd, outfile, verbose)
+ return
+
+
+def check_config_file(infile, outfile, verbose):
+ """
+ Process ns config file passed in argument and report the classic and
+ removed commands.
+
+ Args:
+ infile: NS config file to be converted
+ outfile: Output file to write commands using removed config
+ verbose: True iff converted commands should also be output to console
+ """
+ cli_yacc.cli_yacc_init()
+ # Register handler methods for various commands
+ currentfile = os.path.abspath(inspect.getfile(inspect.currentframe()))
+ currentdir = os.path.dirname(currentfile)
+ for module in glob.glob(os.path.join(currentdir, 'check_classic_configs.py')):
+ importlib.import_module(os.path.splitext(os.path.basename(module))[0])
+ # call methods registered to be called before the start of processing
+ # config file.
+ for m in common.init_methods:
+ m.method(m.obj)
+ lineno = 0
+ for cmd in infile:
+ lineno += 1
+ parsed_tree = cli_yacc.cli_yacc_parse(cmd, lineno)
+ if parsed_tree is not None:
+ # construct dictionary key to look up registered method to call to
+ # parse and transform the command to be emitted
+ # Registered method can return either string or tree.
+ key = " ".join(parsed_tree.get_command_type()).lower()
+ if key in common.dispatchtable:
+ for m in common.dispatchtable[key]:
+ # Since, we are only checking the config and not
+ # converting to advanced, so list will contains
+ # at most only one command.
+ output = m.method(m.obj, parsed_tree)
+ if len(output) == 0:
+ check_for_removed_expression(cmd, outfile, verbose)
+ else:
+ output_line(cmd, outfile, verbose)
+ else:
+ check_for_removed_expression(cmd, outfile, verbose)
+ else:
+ check_for_removed_expression(cmd, outfile, verbose)
+
+
+def main():
+ desc = cleandoc(
+ """
+ Checks whether invalid config is present in input file
+ """)
+ arg_parser = argparse.ArgumentParser(
+ prog="configCheck",
+ description=desc,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ arg_parser.add_argument(
+ "-f", "--infile", metavar="",
+ help="Checks whether invalid config is present in the input file",
+ required=True)
+ arg_parser.add_argument(
+ "-v", "--verbose", action="store_true", help="show verbose output")
+ arg_parser.add_argument(
+ '-V', '--version', action='version',
+ version='%(prog)s {}'.format(__version__))
+ try:
+ args = arg_parser.parse_args()
+ except IOError as e:
+ exit(str(e))
+ # obtain logging parameters and setup logging
+ conf_file_path = os.path.dirname(args.infile)
+ conf_file_name = os.path.basename(args.infile)
+ check_classic_configs.check_configs_init()
+ new_path = os.path.join(conf_file_path, "issues_" + conf_file_name)
+ with open(args.infile, 'r') as infile:
+ with open(new_path, 'w') as outfile:
+ check_config_file(infile, outfile, args.verbose)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/nspepi/nspepi2/convert_auth_cmd.py b/nspepi/nspepi2/convert_auth_cmd.py
new file mode 100644
index 0000000..10ddaef
--- /dev/null
+++ b/nspepi/nspepi2/convert_auth_cmd.py
@@ -0,0 +1,466 @@
+#!/usr/bin/env python2
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+import copy
+import logging
+from collections import OrderedDict
+
+import nspepi_common as common
+from nspepi_parse_tree import *
+from convert_classic_expr import *
+import convert_cli_commands as cli_cmds
+
+# TODO Some of the Client Security Expressions do not have equivalent Advanced
+# expressions. This may lead to some policies being converted and some not,
+# which in overall will lead to invalid config. To avoid this issue,
+# disabling the Classic Authentication policy and its bindings conversion for now.
+
+# All module names starting with "convert_" are parsed to detect and register
+# class methods
+@common.register_class_methods
+class Authentication(cli_cmds.ConvertConfig):
+ """
+ Converts classic Authentication policies and
+ authentication vserver bind commands of classic policies.
+ """
+
+ # override
+ bind_default_goto = "NEXT"
+ flow_type_direction_default = None
+
+ def __init__(self):
+ """
+ Information about authentication commands.
+ _converted_bind_cmd_trees - Dictionary to store converted
+ bind commands.
+ {: }
+ _policy_label_priority - Needs to add priority in policy label
+ bind commands. This variable contains
+ last priority that is used in each
+ policy label.
+ """
+ self._converted_bind_cmd_trees = OrderedDict()
+ self._policy_label_priority = OrderedDict()
+
+ @property
+ def converted_bind_cmd_trees(self):
+ return self._converted_bind_cmd_trees
+
+ #@common.register_for_cmd("add", "authentication", "webAuthPolicy")
+ #@common.register_for_cmd("add", "authentication", "dfaPolicy")
+ def convert_webAuth_dfa_policy(self, auth_policy_parse_tree):
+ """
+ Converting classic webAuth/dfa policy
+ to advanced authentication policy.
+ Syntax:
+ add authentication webAuthPolicy
+ -rule -action
+ or
+ add authentication dfaPolicy
+ -rule -action
+ converts to
+ add authentication Policy
+ -rule -action
+ """
+ # Because we are currently converting authentication policies and its
+ # bindings to authentication vserver only and not VPN vserver, VPN
+ # global and system global bindings, we will get error for VPN
+ # vserver, VPN global and system global bindings. To aviod this issue,
+ # we create advanced policy which is equivalent to old classic policy,
+ # give it a name, and replace all the references to the old classic
+ # policy in the converted bind commands to the corresponding advanced
+ # policy.
+ original_tree = copy.deepcopy(auth_policy_parse_tree)
+ tree_list = [original_tree]
+ policy_name = auth_policy_parse_tree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ # Changing ot of the command.
+ auth_policy_parse_tree.ot = "Policy"
+ # Changing classic rule to advanced rule.
+ cli_cmds.ConvertConfig.convert_keyword_expr(auth_policy_parse_tree,
+ 'rule')
+ if auth_policy_parse_tree.upgraded:
+ self.replace_advanced_name(auth_policy_parse_tree)
+ tree_list.append(auth_policy_parse_tree)
+ pol_obj.policy_type = "classic"
+ else:
+ pol_obj.policy_type = "advanced"
+ return tree_list
+
+ #@common.register_for_cmd("add", "authentication", "certPolicy")
+ #@common.register_for_cmd("add", "authentication", "negotiatePolicy")
+ #@common.register_for_cmd("add", "authentication", "tacacsPolicy")
+ #@common.register_for_cmd("add", "authentication", "samlPolicy")
+ #@common.register_for_cmd("add", "authentication", "radiusPolicy")
+ #@common.register_for_cmd("add", "authentication", "ldapPolicy")
+ #@common.register_for_cmd("add", "authentication", "localPolicy")
+ def convert_other_auth_policy(self, auth_policy_parse_tree):
+ """
+ Converting local/ldap/radius/saml/tacacs/negotiate/cert policy to
+ advanced authentication policy.
+ Syntax for localPolicy:
+ add authentication localPolicy
+ converts to
+ add authentication Policy -rule -action local
+
+ syntax for other policies:
+ add authentication
+ converts to
+ add authentication Policy -rule
+ -action
+ """
+ # Because we are currently converting authentication policies and its
+ # bindings to authentication vserver only and not VPN vserver, VPN
+ # global and system global bindings, we will get error for VPN
+ # vserver, VPN global and system global bindings. To aviod this issue,
+ # we create advanced policy which is equivalent to old classic policy,
+ # give it a name, and replace all the references to the old classic
+ # policy in the converted bind commands to the corresponding advanced
+ # policy.
+ original_tree = copy.deepcopy(auth_policy_parse_tree)
+ tree_list = [original_tree]
+ policy_name = auth_policy_parse_tree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ is_local_policy = (auth_policy_parse_tree.ot.lower() == "localpolicy")
+
+ # Changing ot of the command.
+ auth_policy_parse_tree.ot = "Policy"
+
+ # Changing classic rule to advanced rule
+ # positional to keyword.
+ cli_cmds.ConvertConfig.convert_pos_expr(auth_policy_parse_tree, 1)
+ if not auth_policy_parse_tree.upgraded:
+ pol_obj.policy_type = "advanced"
+ return tree_list
+ advanced_expr = auth_policy_parse_tree.positional_value(1).value
+ rule_keyword = CLIKeywordParameter(CLIKeywordName("rule"))
+ rule_keyword.add_value(advanced_expr)
+ auth_policy_parse_tree.add_keyword(rule_keyword)
+ auth_policy_parse_tree.remove_positional(1)
+
+ # Changing action from positional to keyword
+ action = None
+ if is_local_policy:
+ action_name = "LOCAL"
+ else:
+ action_name = auth_policy_parse_tree.positional_value(1).value
+ auth_policy_parse_tree.remove_positional(1)
+ action_keyword = CLIKeywordParameter(CLIKeywordName("action"))
+ action_keyword.add_value(action_name)
+ auth_policy_parse_tree.add_keyword(action_keyword)
+
+ auth_policy_parse_tree.set_upgraded()
+ pol_obj.policy_type = "classic"
+ self.replace_advanced_name(auth_policy_parse_tree)
+ tree_list.append(auth_policy_parse_tree)
+ return tree_list
+
+ def replace_advanced_name(self, auth_policy_parse_tree):
+ """
+ Replace policy name with the corresponding advanced policy name and
+ store the policy name as classic as this is converted command.
+ auth_policy_parse_tree - bind command parse tree
+ """
+ policy_name = auth_policy_parse_tree.positional_value(0).value
+ advanced_policy_name = "nspepi_adv_" + policy_name
+ auth_policy_parse_tree.positional_value(0) \
+ .set_value(advanced_policy_name)
+ pol_obj = common.Policy(advanced_policy_name,
+ self.__class__.__name__, "classic")
+ common.pols_binds.store_policy(pol_obj)
+
+ def convert_auth_policy_auth_vserver_bind(self, bind_cmd_parse_tree):
+ """
+ This is a helper function which converts bind command
+ of authentication policy to authentication vserver.
+ If advanced policy is bound, returns the original tree.
+ If classic policy which is converted is bound
+ then convert the command as below:
+ classic policies can be bound as primary, secondary, groupExtraction.
+ 1. For primary policy, add -priority or update if already present.
+ since same priority can be given multiple times in classic policies.
+ Example:
+ bind authentication vserver -policy
+ converts to
+ bind authentication vserver -policy
+ -priority -gotoPriorityExpression NEXT
+ 2. For secondary policy, follow the below steps
+ 1) create authentication policylabel
+ _secondary_auth_label
+ 2) Bind all policies which are bound as
+ secondary to this policy label
+ 3) Add -nextfactor _seconday_auth_label
+ to all policies which are bound as primary to that bind point.
+ 3. For groupExtraction policy, follow the below steps
+ 1) create authentication policylabel
+ _group_auth_label
+ 2) Bind all policies with group_extraction
+ to this policy label.
+ 3) Add -nextfactor _group_auth_label
+ to all policies which has -secondary
+ Example:
+ bind authentication vserver v1 -policy p1
+ bind authentication vserver v1 -policy p2
+ bind authentication vserver v1 -policy p3 -secondary
+ bind authentication vserver v1 -policy p4 -secondary
+ bind authentication vserver v1 -policy p5 -groupExtraction
+ Converts to
+ add authentication policylabel v1_group_auth_label
+ add authentication policylabel v1_secondary_auth_label
+ bind authentication vserver v1 -policy p1 -priority 10
+ -nextFactor v1_secondary_auth_label
+ -gotoPriorityExpression NEXT
+ bind authentication vserver v1 -policy p2 -priority 20
+ -nextFactor v1_secondary_auth_label
+ -gotoPriorityExpression NEXT
+ bind authentication policylabel v1_secondary_auth_label
+ -policyName p3 -priority 30 -nextFactor v1_group_auth_label
+ -gotoPriorityExpression NEXT
+ bind authentication policylabel v1_secondary_auth_label
+ -policyName p4 -priority 40 -nextFactor v1_group_auth_label
+ -gotoPriorityExpression NEXT
+ bind authentication policylabel v1_group_auth_label
+ -policyName p5 -priority 30 -gotoPriorityExpression NEXT
+ """
+ policy_name = bind_cmd_parse_tree.keyword_value("policy")[0].value
+ policy_type = common.pols_binds.policies[policy_name].policy_type
+ if policy_type == "advanced":
+ return [bind_cmd_parse_tree]
+ # Getting bind point name
+ vserver_name = bind_cmd_parse_tree.positional_value(0).value
+ bind_point = "auth_vserver_" + vserver_name
+ sec_auth_policy_label = vserver_name + "_secondary_auth_label"
+ group_factor_policy_label = vserver_name + "_group_auth_label"
+
+ # Checking for -secondary and -groupExtraction.
+ # When both options are not present, it means
+ # policy bound is primary.
+ if bind_cmd_parse_tree.keyword_exists("secondary"):
+ # If entry for bind_point is not in dictionary,
+ # then add one entry.
+ if bind_point not in self._converted_bind_cmd_trees:
+ self._converted_bind_cmd_trees[bind_point] = []
+ # If secondary policy label is not added already, add it.
+ if not self.is_policy_label_added(
+ bind_point, sec_auth_policy_label):
+ self.add_policy_label(bind_point, sec_auth_policy_label)
+ self.add_nextfactor_to_primary(
+ bind_point, sec_auth_policy_label)
+ # Bind policy to secondary policy label
+ self.bind_policy_label(
+ bind_point, sec_auth_policy_label, policy_name)
+ elif bind_cmd_parse_tree.keyword_exists("groupExtraction"):
+ # If entry for bind_point is not in dictionary,
+ # then add one entry
+ if bind_point not in self._converted_bind_cmd_trees:
+ self._converted_bind_cmd_trees[bind_point] = []
+ # If group policy label is not added already, add it.
+ if not self.is_policy_label_added(
+ bind_point, group_factor_policy_label):
+ self.add_policy_label(bind_point, group_factor_policy_label)
+ self.add_nextfactor_to_secondary(
+ bind_point, sec_auth_policy_label,
+ group_factor_policy_label)
+ # Bind policy to group policy label
+ self.bind_policy_label(
+ bind_point, group_factor_policy_label, policy_name)
+ else:
+ if bind_point not in self._converted_bind_cmd_trees:
+ self._converted_bind_cmd_trees[bind_point] = []
+ # Replace with advanced policy name.
+ advanced_policy_name = "nspepi_adv_" + policy_name
+ self.update_tree_arg(bind_cmd_parse_tree, "policy",
+ advanced_policy_name)
+ self._converted_bind_cmd_trees[bind_point].append(
+ bind_cmd_parse_tree)
+
+ return []
+
+ def is_policy_label_added(self, bind_point, label_name):
+ """
+ Checks whether policy label is added or not.
+ bind_point - bind_point name which is used as key in dictionary
+ label_name - policy label name that has to be checked for.
+ Returns True, if add policylabel tree is added to
+ _converted_bind_cmd_trees[bind_point].
+ command:
+ add authentication policylabel
+ """
+ cmd_list = self._converted_bind_cmd_trees[bind_point]
+ for cmd_parse_tree in cmd_list:
+ if (cmd_parse_tree.ot.lower() == "policylabel" and
+ cmd_parse_tree.positional_value(0).value == label_name):
+ return True
+ return False
+
+ def add_policy_label(self, bind_point, label_name):
+ """
+ Creates parse tree for "add authentication policylabel" command
+ with name label_name.
+ Command:
+ add authentication policylabel
+ Saves the parse tree in _converted_bind_cmd_trees dictionary.
+ bind_point - bind_point name which is used as key in dictionary
+ label_name - Authentication policy label name that has to be added.
+ """
+ # Tree construction
+ pol_label_tree = CLICommand("add", "authentication", "policylabel")
+ pos = CLIPositionalParameter(label_name)
+ pol_label_tree.add_positional(pos)
+ # Save in dictionary
+ self._converted_bind_cmd_trees[bind_point].insert(0, pol_label_tree)
+
+ def bind_policy_label(self, bind_point, policy_label, policy_name):
+ """
+ Creates parse tree for "bind authentication policylabel" command with
+ given policy label and policy.
+ command:
+ bind authentication policylabel -policyName
+ -priority -gotoPriorityExpression NEXT
+ Saves the parse tree in _converted_bind_cmd_trees dictionary.
+ bind_point - bind_point name which is used as key in dictionary
+ policy_label - Policy label name to which policy has to be bound
+ policy_name - Policy to be bound to policy label
+ """
+ # Tree construction
+ if policy_label not in self._policy_label_priority:
+ self._policy_label_priority[policy_label] = 0
+ self._policy_label_priority[policy_label] += 100
+ bind_label_tree = CLICommand("bind", "authentication", "policylabel")
+ pos = CLIPositionalParameter(policy_label)
+ bind_label_tree.add_positional(pos)
+ policy_key = CLIKeywordParameter(CLIKeywordName("policyName"))
+ advanced_policy_name = "nspepi_adv_" + policy_name
+ policy_key.add_value(advanced_policy_name)
+ bind_label_tree.add_keyword(policy_key)
+ priority_key = CLIKeywordParameter(CLIKeywordName("priority"))
+ priority_key.add_value(str(self._policy_label_priority[policy_label]))
+ bind_label_tree.add_keyword(priority_key)
+ goto_key = \
+ CLIKeywordParameter(CLIKeywordName("gotoPriorityExpression"))
+ goto_key.add_value("NEXT")
+ bind_label_tree.add_keyword(goto_key)
+ # Save in dictionary
+ self._converted_bind_cmd_trees[bind_point].append(bind_label_tree)
+
+ def add_nextfactor_to_primary(self, bind_point, label_name):
+ """
+ Adds -nextFactor to all the policies which are
+ bound as primary to that bindpoint
+ Primary policies are bound to authentication vserver by the following
+ command:
+ bind authentication vserver -policy
+ bind_point - bind_point name which is used as key in dictioanry
+ label_name - Policy label name which should be added as nextfactor
+ """
+ cmd_list = self._converted_bind_cmd_trees[bind_point]
+ for index in range(len(cmd_list)):
+ cmd_parse_tree = cmd_list[index]
+ if ((' '.join(cmd_parse_tree.get_command_type())).lower() ==
+ "bind authentication vserver"):
+ self.add_nextfactor(cmd_parse_tree, label_name)
+
+ def add_nextfactor_to_secondary(
+ self, bind_point, secondary_label_name, group_label_name):
+ """
+ Adds -nextFactor to all the policies which
+ are bound as secondary to that bind point.
+ Secondary policies are bound to secondary_policy_label by the
+ following command:
+ bind authentication policylabel
+ -policyName
+ -priority
+ bind_point - bind point name which is used as
+ key in dictionary
+ group_label_name - Policy label name which should be
+ added as nextFactor
+ secondary_label_name - Policy label name to which nextfactor
+ has to be added
+ """
+ cmd_list = self._converted_bind_cmd_trees[bind_point]
+ for index in range(len(cmd_list)):
+ cmd_parse_tree = cmd_list[index]
+ if ((' '.join(cmd_parse_tree.get_command_type())).lower() ==
+ "bind authentication policylabel" and
+ cmd_parse_tree.positional_value(0).value ==
+ secondary_label_name):
+ self.add_nextfactor(cmd_parse_tree, group_label_name)
+
+ def add_nextfactor(self, tree, policy_label_name):
+ """
+ Adds -nextfactor to command.
+ tree - command parse tree to which nextfactor
+ has to be added
+ policy_label_name - Policy label name which
+ should be added as nextfactor
+ """
+ nextfactor_key = CLIKeywordParameter(CLIKeywordName("nextFactor"))
+ nextfactor_key.add_value(policy_label_name)
+ tree.add_keyword(nextfactor_key)
+ tree.set_upgraded()
+
+ @common.register_for_cmd("bind", "authentication", "vserver")
+ def convert_auth_vserver_bind(self, bind_parse_tree):
+ """
+ Handles Authentication vserver bind
+ command.
+ bind authentication vserver [-policy
+ [-priority ] [-gotoPriorityExpression
+ ]]
+ """
+ if not bind_parse_tree.keyword_exists('policy'):
+ return [bind_parse_tree]
+
+ policy_name = bind_parse_tree.keyword_value("policy")[0].value
+ priority_arg = "priority"
+ goto_arg = "gotoPriorityExpression"
+
+ class_name = self.__class__.__name__
+ policy_type = common.pols_binds.get_policy(policy_name).module
+ # If policy is Authentication policy.
+ if policy_type == class_name:
+ return self.convert_auth_policy_auth_vserver_bind(
+ bind_parse_tree)
+
+ """
+ Calls the method that is registered for the particular
+ policy type that is bound to vserver. Returns converted_list.
+ If the policy module is not registered for binding,
+ then returns the original parse tree.
+ """
+ key = "Authentication"
+ if key in common.bind_table:
+ if policy_type in common.bind_table[key]:
+ m = common.bind_table[key][policy_type]
+ return m.method(m.obj, bind_parse_tree, policy_name,
+ priority_arg, goto_arg)
+ return [bind_parse_tree]
+
+ @common.register_for_final_call
+ def get_converted_auth_bind_cmds(self):
+ """
+ Returns all command parse trees saved in _converted_bind_cmd_trees.
+ This should be called only at the end of processing
+ of entire ns.conf file.
+ Return value - list of parse trees.
+ """
+ tree_list = []
+ policy_type = self.__class__.__name__
+ priority_arg = "priority"
+ goto_arg = "gotoPriorityExpression"
+ for bind_point in self._converted_bind_cmd_trees:
+ for tree in self._converted_bind_cmd_trees[bind_point]:
+ if ((' '.join(tree.get_command_type())).lower() ==
+ "bind authentication vserver"):
+ policy_name = tree.keyword_value("policy")[0].value
+ tree_list += self.convert_entity_policy_bind(
+ tree, tree, policy_name,
+ policy_type, priority_arg, goto_arg)
+ else:
+ tree_list.append(tree)
+ return tree_list
diff --git a/nspepi/nspepi2/convert_classic_expr.py b/nspepi/nspepi2/convert_classic_expr.py
new file mode 100644
index 0000000..c4814a3
--- /dev/null
+++ b/nspepi/nspepi2/convert_classic_expr.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python2
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+import logging
+import subprocess
+import re
+import convert_cli_commands as cli_commands
+import nspepi_common as common
+
+
+from pi_lex import PILex
+from nspepi_parse_tree import CLIParseTreeNode
+
+eval_classic_expr = re.compile(r'SYS\s*\.\s*EVAL_CLASSIC_EXPR\s*\(\s*"',
+ re.IGNORECASE)
+
+q_s_expr = re.compile(r'\b((Q\.HOSTNAME)|(Q\.TRACKING)|'
+ r'(Q\.METHOD)|(Q\.URL)|(Q\.VERSION)|'
+ r'(Q\.CONTENT_LENGTH)|(Q\.HEADER)|'
+ r'(Q\.IS_VALID)|(Q\.DATE)|'
+ r'(Q\.COOKIE)|(Q\.BODY)|(Q\.TXID)|'
+ r'(Q\.CACHE_CONTROL)|(Q\.USER)|'
+ r'(Q\.IS_NTLM_OR_NEGOTIATE)|'
+ r'(Q\.FULL_HEADER)|'
+ r'(Q\.LB_VSERVER)|(Q\.CS_VSERVER)|'
+ r'(S\.VERSION)|(S\.STATUS)|'
+ r'(S\.STATUS_MSG)|(S\.IS_REDIRECT)|'
+ r'(S\.IS_INFORMATIONAL)|(S\.IS_SUCCESSFUL)|'
+ r'(S\.IS_CLIENT_ERROR)|(S\.IS_SERVER_ERROR)|'
+ r'(S\.TRACKING)|(S\.HEADER)|(S\.FULL_HEADER)|'
+ r'(S\.IS_VALID)|(S\.DATE)|(S\.BODY)|'
+ r'(S\.SET_COOKIE)|(S\.SET_COOKIE2)|'
+ r'(S\.CONTENT_LENGTH)|'
+ r'(S\.CACHE_CONTROL)|(S\.TXID)|(S\.MEDIA))\b',
+ re.IGNORECASE)
+
+def convert_classic_expr(classic_expr):
+ tree_obj = CLIParseTreeNode()
+ info_msg = 'INFO: Expression is not converted' + \
+ ' - most likely it is a valid advanced expression'
+ warn_msg = 'WARNING: Line numbers which has ' + \
+ 'more than 8191 characters length: 0'
+ try:
+ nspepi_tool_path = common.get_nspepi_tool_path()
+ """Error message will be in the staring of
+ output, whereas warning and info messages
+ will be present in the last."""
+ nspepi_tool_output = subprocess.check_output(
+ ['perl', nspepi_tool_path, '-e', classic_expr],
+ shell=False, stderr=subprocess.STDOUT)
+ """ old nspepi tool adds newline character at the end
+ of the converted string, so remove that character."""
+ nspepi_tool_output = nspepi_tool_output.rstrip()
+ except subprocess.CalledProcessError as exc:
+ # Log the command which is failing
+ logging.error(exc)
+ # Log the error message
+ logging.error(exc.output)
+ return None
+ if nspepi_tool_output.startswith('ERROR:'):
+ """old nspepi tool throws "ERROR: Expression is in blocked list
+ of conversion" error for vpn client security expression."""
+ logging.error(nspepi_tool_output)
+ return None
+ elif nspepi_tool_output.endswith(info_msg):
+ """old nspepi tool didn't convert the expression,
+ so return input expression"""
+ nspepi_tool_output = classic_expr
+ # classic_expr is not enclosed in quotes.
+ nspepi_tool_output = tree_obj.normalize(nspepi_tool_output, True)
+ elif nspepi_tool_output.endswith(warn_msg):
+ logging.warning(nspepi_tool_output)
+ """ If expression has more than 8191 characters, old nspepi
+ tool gives warning message at the end of the output.
+
+ old nspepi tool output:
+ WARNING: Total number of warnings due to
+ expressions length greater than 8191 characters: 1
+ WARNING: Line numbers which has more than 8191 characters length: 0
+
+ Removing warning message from the output
+ """
+ expr_end_pos = nspepi_tool_output.find("WARNING")
+ nspepi_tool_output = nspepi_tool_output[0:expr_end_pos]
+ nspepi_tool_output = nspepi_tool_output.rstrip()
+
+ # When NSPEPI tool is used with -e option, this handles classic built-in
+ # Named expressions. When tool is used with -f option, all named
+ # expressions are handled here.
+ nspepi_tool_output = cli_commands.ConvertConfig.replace_named_expr(
+ cli_commands.remove_quotes(nspepi_tool_output))
+ nspepi_tool_output = tree_obj.normalize(nspepi_tool_output, True)
+ return nspepi_tool_output
+
+def convert_adv_expr(advanced_expr):
+ """
+ Converts Q and S prefixes.
+ Converts SYS.EVAL_CLASSIC_EXPR expression in advanced expressions to remove
+ classic expressions.
+ advanced_expr - Expression in which Q and S prefixes and SYS.EVAL_CLASSIC_EXPR
+ expression should be replaced.
+ Returns None in case of any Error. Otherwise returns converted expression.
+ """
+ advanced_expr = convert_q_s_expr(advanced_expr)
+ return convert_sys_eval_classic_expr(advanced_expr)
+
+def convert_q_s_expr(advanced_expr):
+ """
+ Convertes Q and S prefixes to use HTTP.REQ and HTTP.RES
+ advanced_expr - Expression in which Q and S prefixes
+ should be replaced.
+ Returns converted expression.
+ """
+ q_s_expr_list = []
+ # Get all indexes of Q and S expressions.
+ for match in re.finditer(q_s_expr, advanced_expr):
+ q_s_expr_list.append(match.start())
+ for expr_index in reversed(q_s_expr_list):
+ if (advanced_expr[expr_index] == 'Q' or
+ advanced_expr[expr_index] == 'q'):
+ converted_expr = "HTTP.REQ"
+ else:
+ converted_expr = "HTTP.RES"
+ advanced_expr = (advanced_expr[0: expr_index] +
+ converted_expr +
+ advanced_expr[expr_index + 1:])
+ return advanced_expr
+
+def convert_sys_eval_classic_expr(advanced_expr):
+ """
+ Converts SYS.EVAL_CLASSIC_EXPR expression in advanced expressions to remove
+ classic expressions.
+ advanced_expr - Expression in which SYS.EVAL_CLASSIC_EXPR expression
+ should be replaced.
+ Returns None in case of any Error. Otherwise returns converted expression.
+ """
+ original_expr = advanced_expr
+ advanced_expr_length = len(advanced_expr)
+ sys_eval_list = []
+ # Get all indexes where SYS.EVAL_CLASSIC_EXPR starts.
+ for match in re.finditer(eval_classic_expr, advanced_expr):
+ start_index = match.start()
+ length = match.end() - match.start()
+ sys_eval_list.append([start_index, length])
+ for sys_exp_info in reversed(sys_eval_list):
+ # arg_start_index points to opening quote in
+ # SYS.EVAL_CLASSIC_EXPR("<>")
+ sys_start_index = sys_exp_info[0]
+ sys_length = sys_exp_info[1]
+ arg_start_index = sys_start_index + sys_length - 1
+ classic_exp_info = PILex.get_pi_string(
+ advanced_expr[arg_start_index:])
+ if classic_exp_info is None:
+ logging.error("Error in converting expression: {}".format(
+ original_expr))
+ return None
+ classic_expr = classic_exp_info[0]
+ length = classic_exp_info[1]
+ # arg_end_index points to closing quote in SYS.EVAL_CLASSIC_EXPR("<>").
+ arg_end_index = arg_start_index + length - 1
+ # Handle spaces between closing quote and closing brace.
+ sys_end_index = arg_end_index + 1
+ while(sys_end_index < advanced_expr_length and
+ advanced_expr[sys_end_index] != ')' and
+ advanced_expr[sys_end_index] in " \t\r"):
+ sys_end_index += 1
+ if (sys_end_index >= advanced_expr_length or
+ advanced_expr[sys_end_index] != ')'):
+ logging.error("Error in converting expression: {}".format(
+ original_expr))
+ return None
+ converted_expr = convert_classic_expr(classic_expr)
+ if converted_expr is not None:
+ # Result from convert_classic_expr will have enclosing quotes.
+ converted_expr = cli_commands.remove_quotes(converted_expr)
+ if converted_expr is None or converted_expr == classic_expr:
+ logging.error("Error in converting expression: {}".format(
+ original_expr))
+ return None
+ # Converted expression should be enclosed in braces because
+ # SYS.EVAL_CLASSIC_EXPR can have && or ||.
+ advanced_expr = (advanced_expr[0: sys_start_index] + '(' +
+ converted_expr + ')' +
+ advanced_expr[sys_end_index + 1:])
+ tree_obj = CLIParseTreeNode()
+ advanced_expr = tree_obj.normalize(advanced_expr, True)
+ return advanced_expr
diff --git a/nspepi/nspepi2/convert_cli_commands.py b/nspepi/nspepi2/convert_cli_commands.py
new file mode 100644
index 0000000..94d3397
--- /dev/null
+++ b/nspepi/nspepi2/convert_cli_commands.py
@@ -0,0 +1,2291 @@
+#!/usr/bin/env python2
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+import collections
+import copy
+
+import cli_lex
+import nspepi_common as common
+import convert_classic_expr
+from nspepi_parse_tree import *
+
+# All module names starting with "convert_" are parsed to detect and register
+# class methods
+
+
+def convert_cli_init():
+ """Initialize global variables uses by this module"""
+ global vserver_protocol_dict
+ vserver_protocol_dict = OrderedDict()
+ global policy_entities_names
+ global classic_entities_names
+ global named_expr
+ named_expr = {}
+ policy_entities_names = set()
+ classic_entities_names = set()
+ # Register built-in named expressions.
+ NamedExpression.register_built_in_named_exprs()
+ global cli_global_binds
+ global cli_vserver_binds
+ global cli_user_binds
+ global cli_group_binds
+ global cli_service_binds
+ cli_global_binds = OrderedDict()
+ cli_vserver_binds = OrderedDict()
+ cli_user_binds = OrderedDict()
+ cli_group_binds = OrderedDict()
+ cli_service_binds = OrderedDict()
+
+
+def remove_quotes(val):
+ """
+ Helper function to remove the surrounding
+ quotes from a CLI parameter.
+ val - CLI parameter that needs quotes removed.
+ Returns the dequoted CLI parameter
+ """
+ result = val
+ if val.startswith('"') or val.startswith("'"):
+ lexer = cli_lex.Lexer()
+ lexer.input(val)
+ token = lexer.token()
+ assert token.type == "NON_KEY"
+ result = token.value
+ return result
+
+
+def get_advanced_name(classic_name):
+ """
+ Helper function to get a valid Advanced identifier
+ corresponding to a Classic identifier.
+ Note that this does not deal with reserved words.
+ Nor does it check for duplicates.
+ """
+ adv_name = "nspepi_adv_" + re.sub(r'[^a-zA-Z0-9_]', '_', classic_name)
+ return adv_name
+
+
+def get_classic_expr_list(expr):
+ """
+ Helper function to get the list of
+ classic expression names present in the
+ given expression.
+ expr - Expression in which classic
+ expression names need to be found.
+ Returns the list of items found or None if none;
+ the items are each a list with:
+ - classic expression name
+ - advanced expression name
+ - start offset of token to replace
+ - length of token to replace
+ """
+ lexer = cli_lex.Lexer()
+ lexer.input(expr)
+ classic_expr_info_list = []
+ while True:
+ next_token = lexer.adv_expr_token()
+ if not next_token:
+ break
+ token_value = str(next_token)
+ token_value_len = len(token_value)
+ if token_value in NamedExpression.built_in_named_expr:
+ # Checking for built-in classic Named expression.
+ adv_expr_name = NamedExpression.built_in_named_expr[token_value]
+ else:
+ adv_expr_name = get_advanced_name(token_value)
+ if (next_token.type == "IDENTIFIER" and
+ adv_expr_name.lower() in policy_entities_names):
+ start_offset = next_token.lexpos - token_value_len + 1
+ expr_info = [token_value, adv_expr_name,
+ start_offset, token_value_len]
+ classic_expr_info_list.append(expr_info)
+
+ return classic_expr_info_list
+
+
+class ConvertConfig(object):
+ """Base class to convert the config"""
+
+ @staticmethod
+ def replace_named_expr(rule_expr):
+ """
+ Helper function to replace the classic named
+ expression with the advanced named expression
+ in the given expression.
+ rule_expr - the expression to modify
+ Returns the expression with names modified as needed.
+ """
+ converted_expr = rule_expr
+ for expr_info in reversed(get_classic_expr_list(rule_expr)):
+ # Work in reverse order to avoid recomputing offsets
+ offset = expr_info[2]
+ replace_len = expr_info[3]
+ converted_expr = (converted_expr[0: offset] +
+ expr_info[1] + converted_expr[offset +
+ replace_len:])
+
+ return converted_expr
+
+ @staticmethod
+ def convert_pos_expr(commandParseTree, pos):
+ """
+ Convert the expression present at a given position
+ commandParseTree - the parse tree to modify
+ pos - the position of the parameter to modify
+ Returns the modified parse tree.
+ """
+ rule_node = commandParseTree.positional_value(pos)
+ rule_expr = rule_node.value
+ converted_expr = convert_classic_expr.convert_classic_expr(rule_expr)
+ if converted_expr is None:
+ logging.error('Error in converting command : ' +
+ str(commandParseTree))
+ converted_expr = rule_expr
+ else:
+ # converted_expr will have quotes and rule_expr will not have
+ # quotes. Since we are comparing these 2 expressions, removing
+ # quotes from converted_expr.
+ converted_expr = remove_quotes(converted_expr)
+ if converted_expr != rule_expr:
+ # expression is converted, this is classic.
+ rule_node.set_value(converted_expr)
+ commandParseTree.set_upgraded()
+ else:
+ # expression is not converted, then it can be advanced
+ # expression. Advanced expressions can have Q and S prefixes and
+ # SYS.EVAL_CLASSIC_EXPR expression which needs to be converted.
+ commandParseTree = ConvertConfig \
+ .convert_adv_expr_list(commandParseTree, [pos])
+ return commandParseTree
+
+ @staticmethod
+ def convert_keyword_expr(commandParseTree, keywordName):
+ """
+ Convert the expression present as a value of
+ the given keyword name.
+ commandParseTree - the parse tree to modify
+ keywordName - the name of the keyword parameter to modify
+ Returns the modified parse tree.
+ """
+ rule_node = commandParseTree.keyword_value(keywordName)
+ rule_expr = rule_node[0].value
+ converted_expr = convert_classic_expr.convert_classic_expr(rule_expr)
+ if converted_expr is None:
+ logging.error('Error in converting command : ' +
+ str(commandParseTree))
+ converted_expr = rule_expr
+ else:
+ # converted_expr will have quotes and rule_expr will not have
+ # quotes. Since we are comparing these 2 expressions, removing
+ # quotes from converted_expr.
+ converted_expr = remove_quotes(converted_expr)
+ if converted_expr != rule_expr:
+ # expression is converted, this is classic.
+ rule_node[0].set_value(converted_expr)
+ commandParseTree.set_upgraded()
+ else:
+ # expression is not converted, then it can be advanced
+ # expression. Advanced expressions can have Q and S prefixes and
+ # SYS.EVAL_CLASSIC_EXPR expression which needs to be converted.
+ commandParseTree = ConvertConfig \
+ .convert_adv_expr_list(commandParseTree, [keywordName])
+ return commandParseTree
+
+ @staticmethod
+ def convert_adv_expr_list(tree, param_list):
+ """
+ Converts Q and S prefixes and SYS.EVAL_CLASSIC_EXPR expression from the given
+ list of parameters.
+ tree - the parse tree to modify
+ param_list - list of parameters to modify. Each Parameter can be either
+ positional parameter or keyword parameter.
+ If its a keyword parameter, mention the keyword name.
+ If its a positional parameter, mention the position of
+ the parameter.
+ Returns the modified parse tree.
+ """
+ original_tree = copy.deepcopy(tree)
+ for param in param_list:
+ adv_expr = common.get_cmd_arg(param, tree)
+ if adv_expr is None:
+ continue
+ converted_expr = convert_classic_expr.convert_adv_expr(adv_expr)
+ if converted_expr is None:
+ logging.error('Error in converting command : ' +
+ str(original_tree))
+ return original_tree
+ else:
+ converted_expr = remove_quotes(converted_expr)
+ if converted_expr != adv_expr:
+ if isinstance(param, int):
+ # Positional Parameter
+ tree.positional_value(param).set_value(converted_expr)
+ else:
+ # Keyword Parameter
+ tree.keyword_value(param)[0].set_value(converted_expr)
+ tree.set_adv_upgraded()
+ return tree
+
+ def store_builtin_policies(self):
+ """
+ Creates and stores Policy object for built-in policies.
+ """
+ # Since built-in policy add commands are not saved
+ # in ns.conf, function registered for add commands will
+ # not be called for built-in policies where policy object
+ # is stored.
+ for policy_name in self.built_in_policies:
+ pol_obj = common.Policy(policy_name, self.__class__.__name__,
+ "classic")
+ common.pols_binds.store_policy(pol_obj)
+ pol_obj = common.Policy(self.built_in_policies[policy_name],
+ self.__class__.__name__, "advanced")
+ common.pols_binds.store_policy(pol_obj)
+
+ @staticmethod
+ def register_policy_entity_name(commandParseTree):
+ """ Add the entity name in the global list."""
+ name = commandParseTree.positional_value(0).value.lower()
+ policy_entities_names.add(name)
+
+ @staticmethod
+ def register_classic_entity_name(commandParseTree):
+ """ Add the classic entity name in the classic global list."""
+ name = commandParseTree.positional_value(0).value.lower()
+ classic_entities_names.add(name)
+
+ """
+ POLICY BIND CONVERSION INFRASTRUCTURE
+ Converting binds of classic policies to the equivalent advanced
+ policies has a number of issues to address:
+
+ 1. There can be multiple classic policies bound to same priority
+ and classic policies can be bound without priority also.
+ The equivalent advanced policies must be bound at separate
+ policies that maintain the original order.
+
+ 2. Binds for policies for classic modules that are being replaced
+ by an advanced module (e.g. Filter by Rewrite and Responder)
+ may need to be inserted before or after existing policy binds
+ to maintain the same order of evaluation.
+
+ Use of this infrastructure for a module
+
+ 1. Each module class must be a subclass of ConvertConfig to inherit
+ the infrastructure methods and attributes.
+
+ 2. If the bind command does not have gotoPriorityExpression,
+ then it uses the default gotoPriorityExpression of END.
+ If the module class does not want to use the default value, then
+ the module class must override the bind_default_goto attribute.
+ If the module class does not want to add gotoPriorityExpression
+ to bind command, then override the bind_default_goto attribute
+ with None.
+
+ 3. If priority and gotopriorityexpression are positional arguments
+ in the bind command and if the bind command does not have these values
+ initially and the binding infra is used to add a priority and default
+ goto value then, priority and gotopriorityexpression will
+ be added at the end of the positional arguments list.
+ example:
+ bind global
+ converts to
+ bind global
+
+ 4. Each parsed bind command is processed by either:
+
+ .convert_global_bind(parse_tree, module, priority_arg,
+ goto_arg, position)
+ or
+ .convert_entity_policy_bind(parse_tree,
+ policy_module, priority_arg, goto_arg, position)
+
+
+ These methods save bind commands in the appropriate dictionaries.
+ These methods can be used for bindings of policies to
+ global/vserver/user/group/service.
+ """
+
+ class BindInfo(object):
+ """
+ Object to hold the bind command info.
+ """
+ def __init__(self):
+ """
+ Bind command information.
+ orig_cmd - original bind command read from config.
+ parse_tree - bind command parse tree.
+ position - position where the bind command
+ has to be inserted. Possible
+ values - before, inplace, after.
+ priority - priority value.
+ goto - gotopriorityexpression value.
+ bind_arg_priority - Provides the positional index
+ or keyword name for the priority
+ parameter.
+ bind_arg_goto - Provides the positional index or
+ keyword name for the goto parameter.
+ policy_type - type of policy("classic" or "advanced")
+ flow_type_direction - bind type information("REQUEST" or
+ "RESPONSE")
+ """
+ self.orig_cmd = ""
+ self.parse_tree = None
+ self.position = "" # "before", "inplace", or "after" insertion
+ self.priority = 0
+ self.goto = ""
+ self.bind_arg_priority = "priority"
+ self.bind_arg_goto = "gotoPriorityExpression"
+ self.policy_type = None
+ self.flow_type_direction = None
+
+ def set(self, orig_cmd, parse_tree, position, priority, goto,
+ priority_arg, goto_arg, policy_type, flow_type_direction):
+ """
+ Sets the BindInfo class instance variables.
+ orig_cmd - original bind command read from config.
+ parse_tree - bind comman parse tree.
+ position - position of insertion.
+ priority - priority
+ goto - gotopriorityexpression value.
+ priority_arg - Positional index or keyword name
+ for the priority argument.
+ goto_arg - positional index or keyword name
+ for the goto argument.
+ flow_type_direction - bind type information( "REQUEST"
+ or "RESPONSE")
+ """
+ self.orig_cmd = orig_cmd
+ self.parse_tree = parse_tree
+ self.position = position
+ self.priority = priority
+ self.goto = goto
+ self.bind_arg_priority = priority_arg
+ self.bind_arg_goto = goto_arg
+ self.policy_type = policy_type
+ self.flow_type_direction = flow_type_direction
+
+ def get_bind_dict(self, current_dict, key):
+ """
+ Return the dictionary selected by key in current_dict. If
+ this dictionary does not yet exist, it will be created.
+ current_dict - dictionary
+ key - key name
+ """
+ if key not in current_dict:
+ current_dict[key] = OrderedDict()
+ return current_dict[key]
+
+ def save_bind_for_reprioritization_common(self, bind_dict, orig_tree, tree,
+ position, priority, goto,
+ bind_type, priority_arg,
+ goto_arg, policy_type):
+ """
+ Save a bind command in the relevant dictionary for later processing
+ bind_dict - is a dictionary which has list of
+ BindInfo objects for commands for a bindpoint
+ (e.g. GLOBAL REWRITE REQ_DEFAULT). A BindInfo
+ object for the current command will be appended
+ to the list.
+ orig_tree - parsed command tree of bind command read from config.
+ tree - processed and possibly modified command tree of bind command.
+ position - indicates where the bind is to be inserted:
+ "before", "inplace", or "after".
+ priority - is the bind prority as an int; may be 0.
+ goto - is the gotoPriorityExpression for the bind
+ bind_type - is the type arg e.g. "REQ_DEFAULT"
+ priority_arg - Positional index or keyword name
+ for the priority argument.
+ goto_arg - Positional index or keyword name
+ for the goto argument.
+ """
+ # bind_type may be None in some cases.
+ # bind_type is used as key for dictionary,
+ # so making the bind_type equal to empty string
+ # when the bind_type is None.
+ if bind_type is None:
+ bind_type = ""
+ else:
+ bind_type = bind_type.lower()
+ if bind_type not in bind_dict:
+ bind_dict[bind_type] = []
+ bind_info = self.BindInfo()
+ flow_type_direction = self.flow_type_direction_default
+ bind_info.set(orig_tree.original_line, tree, position, int(priority),
+ goto, priority_arg, goto_arg,
+ policy_type, flow_type_direction)
+ bind_dict[bind_type].append(bind_info)
+
+ def update_tree_arg(self, tree, arg, value):
+ """
+ Modifies the parse tree argument. If arg is string it
+ is a keyword. If arg is an int it is a positional index.
+ For positional index:
+ Updates the positional value if the positional value
+ already exists.
+ If positional argument arg is not present, then
+ adds positional argument with value.
+ For keyword argument:
+ Updated the keyword value, if keyword already exists
+ else adds a keyword to tree with keyword name arg and
+ keyword value value
+ If value is None, argument is not added.
+ tree - Command parse tree.
+ arg - Command argument, Can be positional or keyword.
+ value - command argument value.
+ """
+ if value is None:
+ return
+ if isinstance(arg, int):
+ if tree.positional_value(arg) is not None:
+ tree.positional_value(arg).set_value(value)
+ else:
+ pos = CLIPositionalParameter(value)
+ tree.add_positional(pos)
+ else:
+ if tree.keyword_exists(arg):
+ tree.keyword_value(arg)[0].set_value(value)
+ else:
+ keyword_arg = CLIKeywordParameter(CLIKeywordName(arg))
+ keyword_arg.add_value(value)
+ tree.add_keyword(keyword_arg)
+ tree.set_upgraded()
+
+ """
+ The gotoPriorityExpression argument to use for converted bindings.
+ Override in a module class if different.
+ If gotoPriorityExpression argument is not required, then override
+ with None.
+ """
+ bind_default_goto = "END"
+
+ """
+ The bind type side(REQUEST or RESPONSE) information used to
+ add -type keyword in converted bindings.
+ Override in a module class if different.
+ If -type keyword is not required, then override with None.
+ """
+ flow_type_direction_default = "REQUEST"
+
+ def convert_global_bind(self, orig_tree, tree, policy_name, module,
+ priority_arg, goto_arg, position="inplace"):
+ """
+ Process a global bind command represented by
+ the command parse tree and saves the required info:
+ bind global
+ Save the bind command in the cli_global_binds dictionary. Return empty
+ list to delete the command. It will later be emitted after
+ reprioritization.
+ The dictionary path:
+ cli_global_binds[][]
+ Args:
+ orig_tree - bind command parse tree of original command from ns.conf.
+ In case bind command is created newly then this argument
+ should contain the bind command parse tree of original
+ command from which this new bind command is getting
+ created
+ tree - bind command parse tree.
+ policy_name - name of the policy that is bound in this bind command
+ module - Name of the policy module(e.g. appfw, tunnel)
+ priority_arg - identification of the parameter in the bind command
+ for priority
+ goto_arg - identification of the parameter in the bind command for goto
+ position - position to be inserted.
+ """
+ if position not in ("before", "inplace", "after"):
+ logging.critical("unexpected insert position value")
+ sys.exit()
+ priority, goto, bind_type = self.get_common_info(tree,
+ priority_arg,
+ goto_arg)
+ # get policy type
+ policy_type = None
+ if policy_name in common.pols_binds.policies:
+ policy_type = common.pols_binds.policies[policy_name].policy_type
+ bind_dict = self.get_bind_dict(cli_global_binds, module.lower())
+ self.save_bind_for_reprioritization_common(bind_dict, orig_tree, tree,
+ position, priority, goto,
+ bind_type, priority_arg,
+ goto_arg, policy_type)
+ # store original bind command for analysis
+ common.pols_binds.store_original_bind(
+ common.Bind(
+ "global", orig_tree.ot.lower(), None, policy_name,
+ module.lower(), bind_type if bind_type else "", str(priority),
+ orig_tree.original_line, lineno=orig_tree.lineno))
+ return []
+
+ def convert_entity_policy_bind(self, orig_tree, tree, policy_name,
+ policy_module, priority_arg, goto_arg,
+ position="inplace"):
+ """
+ Process a vserver/user/group/service bind command
+ represented by the command parse tree:
+ bind vserver
+ bind user
+ bind group
+ bind service
+ Save the bind command in the cli_vserver_binds/cli_user_binds/
+ cli_group_binds/cli_service_binds dictionary. Return an empty
+ parse tree to delete the command. It will later be emitted after
+ reprioritization.
+ The dictionary path:
+ cli_vserver_binds[][][][]
+ cli_user_binds[][][][]
+ cli_group_binds[][][][]
+ cli_service_binds[][][][]
+ orig_tree - bind command parse tree of original command from ns.conf.
+ In case bind command is created newly then this argument
+ should contain the bind command parse tree of original
+ command from which this new bind command is getting
+ created
+ tree - bind command parse tree.
+ policy_name - name of the policy that is bound in this bind command
+ policy_module - module name of policy which is
+ bound to the bind command.
+ priority_arg - identification of the parameter in the bind command
+ for priority
+ goto_arg - identification of the parameter in the bind command for goto
+ position - position to be inserted.
+ """
+ if position not in ("before", "inplace", "after"):
+ logging.critical("unexpected insert position value")
+ sys.exit()
+ entity = tree.ot.lower()
+ entity_type = tree.group
+ entity_name = common.get_cmd_arg(0, tree)
+ priority, goto, bind_type = self.get_common_info(tree, priority_arg,
+ goto_arg)
+ policy_type = None
+ # get policy type
+ if policy_name in common.pols_binds.policies:
+ policy_type = common.pols_binds.policies[policy_name].policy_type
+ if entity == "vserver":
+ bind_dict = self.get_bind_dict(cli_vserver_binds,
+ entity_type.lower())
+ elif entity == "user":
+ bind_dict = self.get_bind_dict(cli_user_binds, entity_type.lower())
+ elif entity == "group":
+ bind_dict = self.get_bind_dict(cli_group_binds,
+ entity_type.lower())
+ elif entity == "service":
+ bind_dict = self.get_bind_dict(cli_service_binds,
+ entity_type.lower())
+ else:
+ logging.critical("Unexpected command " + str(tree))
+ sys.exit()
+ bind_dict = self.get_bind_dict(bind_dict, entity_name)
+ bind_dict = self.get_bind_dict(bind_dict, policy_module.lower())
+ self.save_bind_for_reprioritization_common(bind_dict, orig_tree, tree,
+ position, priority, goto,
+ bind_type, priority_arg,
+ goto_arg, policy_type)
+ # store original bind command for analysis
+ common.pols_binds.store_original_bind(
+ common.Bind(
+ entity, entity_type.lower(), entity_name, policy_name,
+ policy_module.lower(), (bind_type if bind_type else ""),
+ str(priority), orig_tree.original_line,
+ lineno=orig_tree.lineno))
+ return []
+
+ def get_common_info(self, tree, priority_arg, goto_arg):
+ """
+ Returns priority, gotoPriorityExpression and
+ bind type value from the parse tree.
+ tree - parse tree
+ """
+ priority = common.get_cmd_arg(priority_arg, tree)
+ if priority is None:
+ priority = 0
+ # Add priority argument to tree.
+ self.update_tree_arg(tree, priority_arg, str(priority))
+ else:
+ priority = int(priority)
+
+ goto = common.get_cmd_arg(goto_arg, tree)
+ if goto is None:
+ if self.bind_default_goto is not None:
+ goto = self.bind_default_goto
+ # Add goto argument to tree.
+ self.update_tree_arg(tree, goto_arg, str(goto))
+
+ bind_type = common.get_cmd_arg("type", tree)
+ if bind_type is not None:
+ if bind_type.lower() in ["req_default", "req_override"]:
+ bind_type = "REQUEST"
+ elif bind_type.lower() in ["res_default", "res_override"]:
+ bind_type = "RESPONSE"
+ return priority, goto, bind_type
+
+ """
+ Increment used when renumbering bind priorities.
+ """
+ PRIORITY_INCREMENT = 100
+
+ def reprioritize_binds(self, binds):
+ """
+ Sort the binds for the bindpoint and if necessary renumber their
+ priorities.
+ - binds is a list of BindInfo objects
+ Return a list of reprioritized BindInfo objects.
+ """
+ # Sort the binds by their positions.
+ new_binds = []
+ for position in ("before", "inplace", "after"):
+ for bind_info in binds:
+ if bind_info.position == position:
+ new_binds.append(bind_info)
+
+ # Check if the bind priorities are not in the required order and so
+ # require renumbering. Also 0 priorities require renumbering.
+ need_pri_renum = False
+ for i in range(len(new_binds)):
+ if (new_binds[i].priority == 0) or ((i > 0) and
+ (new_binds[i-1].priority
+ >= new_binds[i].priority)):
+ need_pri_renum = True
+ break
+
+ if not need_pri_renum:
+ return new_binds
+
+ # Keep track of which old priority is mapped to which new priority,
+ # for use in changing gotoPriorityExpressions. This needs to be done
+ # separately for the before, inplace, and after binds, since there
+ # can be duplicates priorities across these groups.
+ old_to_new_pri = {}
+ for position in ("before", "inplace", "after"):
+ old_to_new_pri[position] = {}
+ new_pri = self.PRIORITY_INCREMENT
+
+ # Renumber the priorities in the binds.
+ for bind_info in new_binds:
+ old_to_new_pri[bind_info.position][bind_info.priority] = new_pri
+ # Update priority in parse tree.
+ self.update_tree_arg(bind_info.parse_tree,
+ bind_info.bind_arg_priority, str(new_pri))
+ new_pri += self.PRIORITY_INCREMENT
+
+ # Check if any of the bind gotoPriorityExpressions have a priority
+ # that needs to be modified. Also issue a error if any
+ # gotoPriorityExpressions uses an expression.
+ for bind_info in new_binds:
+ goto = bind_info.goto
+ if goto is None:
+ continue
+ elif goto.isdigit():
+ old_goto = int(goto)
+ max_goto = max(old_to_new_pri[bind_info.position])
+ if old_goto in old_to_new_pri[bind_info.position]:
+ new_goto = str(old_to_new_pri[bind_info.position]
+ [old_goto])
+ elif old_goto > max_goto:
+ new_goto = "END"
+ else:
+ new_goto = "1"
+ # Update goto in parse tree.
+ self.update_tree_arg(bind_info.parse_tree,
+ bind_info.bind_arg_goto, new_goto)
+ elif goto not in ("NEXT", "END", "USE_INVOCATION_RESULT"):
+ logging.error("gotoPriorityExpression in {} uses an"
+ " expression. Since the priorities for this"
+ " bindpoint have been renumbered, this"
+ " expression will need to be modified manually."
+ "".format(str(bind_info.parse_tree)))
+ return new_binds
+
+ def reprioritize_and_emit_global_binds(self):
+ """
+ Renumber the priorities for policy binds to all global bindpoints
+ and return a list of the command strings for those binds.
+ """
+ bind_cmd_trees = []
+ for module in cli_global_binds:
+ module_bind_dict = cli_global_binds[module]
+ for bind_type in module_bind_dict:
+ binds = module_bind_dict[bind_type]
+ new_binds = self.reprioritize_binds(binds)
+ for bind_info in new_binds:
+ if common.pols_binds.is_bind_unsupported(
+ bind_info.orig_cmd):
+ logging.error(
+ "Bind command [{}] is commented out because it"
+ " can't be converted to be under a valid advanced"
+ " bindpoint as priority needs to be changed"
+ " manually. However, the command is partially"
+ " converted as [{}]. If the command is required"
+ " please take a backup because comments are not"
+ " saved in ns.conf after triggering"
+ "'save ns config'.{}"
+ "".format(bind_info.orig_cmd.strip(),
+ str(bind_info.parse_tree).strip(),
+ common.CMD_MOD_ERR_MSG))
+ bind_cmd_trees.append(
+ "# {}".format(str(bind_info.parse_tree)))
+ else:
+ type_key_value = None
+ # Combine bind type side information and global_type
+ # information to determine the type keyword value.
+ # Possible values - REQ_DEFAULT, REQ_OVERRIDE,
+ # RES_DEFAULT, RES_OVERRIDE.
+ global_type = (
+ common.pols_binds.get_global_type_for_bind(
+ bind_info.orig_cmd))
+ if (bind_info.flow_type_direction and global_type and
+ bind_info.flow_type_direction in
+ ("REQUEST", "RESPONSE") and
+ bind_info.policy_type == "classic"):
+ type_key_value = (
+ bind_info.flow_type_direction[0:3]
+ + '_' + global_type)
+ self.update_tree_arg(
+ bind_info.parse_tree, "type",
+ type_key_value.upper())
+ if (module == "rewrite" and
+ bind_info.policy_type == "classic"):
+ type_key_value = (
+ bind_type[0:3] + '_' + global_type)
+ self.update_tree_arg(
+ bind_info.parse_tree, "type",
+ type_key_value.upper())
+ bind_cmd_trees.append(bind_info.parse_tree)
+ return bind_cmd_trees
+
+ def reprioritize_and_emit_4_level_dict(self, bind_dict):
+ """
+ Renumber the priorities for all policy binds in bind_dict
+ and return a list of the command strings for those binds.
+ bind_dict - dictionary in which parse trees are saved.
+ dictionary path:
+ bind_dict[][][][]
+ """
+ bind_cmd_trees = []
+ for entity_type in bind_dict:
+ entity_type_bind_dict = bind_dict[entity_type]
+ for entity_name in entity_type_bind_dict:
+ entity_name_bind_dict = entity_type_bind_dict[entity_name]
+ for module in entity_name_bind_dict:
+ module_bind_dict = entity_name_bind_dict[module]
+ for bind_type in module_bind_dict:
+ binds = module_bind_dict[bind_type]
+ new_binds = self.reprioritize_binds(binds)
+ for bind_info in new_binds:
+ if common.pols_binds.is_bind_unsupported(
+ bind_info.orig_cmd):
+ logging.error(
+ "Bind command [{}] is commented out"
+ " because it can't be converted to be"
+ " under a valid advanced bindpoint as"
+ " priority needs to be changed manually."
+ " However, the command is partially"
+ " converted as [{}]. If the command is"
+ " required please take a backup because"
+ " comments are not saved in ns.conf"
+ " after triggering 'save ns config'."
+ "{}".format(
+ bind_info.orig_cmd.strip(),
+ str(bind_info.parse_tree).strip(),
+ common.CMD_MOD_ERR_MSG))
+ bind_cmd_trees.append(
+ "# {}".format(str(bind_info.parse_tree)))
+ else:
+ if (bind_info.flow_type_direction and
+ bind_info.policy_type == "classic"):
+ self.update_tree_arg(
+ bind_info.parse_tree, "type",
+ bind_info.flow_type_direction.upper())
+ bind_cmd_trees.append(bind_info.parse_tree)
+ return bind_cmd_trees
+
+ def reprioritize_and_emit_binds(self):
+ """
+ Renumber the priorities for all policy binds
+ and return a list of the command strings for those binds.
+ """
+ bind_cmd_trees = []
+ bind_cmd_trees += self.reprioritize_and_emit_global_binds()
+ bind_cmd_trees += \
+ self.reprioritize_and_emit_4_level_dict(cli_vserver_binds)
+ bind_cmd_trees += \
+ self.reprioritize_and_emit_4_level_dict(cli_user_binds)
+ bind_cmd_trees += \
+ self.reprioritize_and_emit_4_level_dict(cli_group_binds)
+ bind_cmd_trees += \
+ self.reprioritize_and_emit_4_level_dict(cli_service_binds)
+ return bind_cmd_trees
+
+
+@common.register_class_methods
+class CacheRedirection(ConvertConfig):
+ """ Handle CR feature """
+
+ # Classic built-in policy names and there corresponding
+ # advanced built-in policy names.
+ built_in_policies = {
+ "bypass-non-get": "bypass-non-get-adv",
+ "bypass-cache-control": "bypass-cache-control-adv",
+ "bypass-dynamic-url": "bypass-dynamic-url-adv",
+ "bypass-urltokens": "bypass-urltokens-adv",
+ "bypass-cookie": "bypass-cookie-adv"
+ }
+
+ @common.register_for_init_call
+ def store_builtin_cr_policies(self):
+ """
+ Creates and stores Policy object for built-in CR policies.
+ """
+ self.store_builtin_policies()
+
+ @common.register_for_cmd("add", "cr", "vserver")
+ def convert_cr_vserver(self, commandParseTree):
+ """
+ Get vserver protocol to help in filter bind conversion
+ cr_protocol - cr vserver protocol
+ crv_name - cr vserver name
+ vserver_protocol_dict - dict to store protocol as value to the
+ vserver name as key
+ """
+ cr_protocol = commandParseTree.positional_value(1).value
+ crv_name = commandParseTree.positional_value(0).value
+ vserver_protocol_dict[crv_name] = cr_protocol.upper()
+ return [commandParseTree]
+
+ @common.register_for_cmd("add", "cr", "policy")
+ def convert_policy(self, commandParseTree):
+ """
+ Converts classic cr policy to advanced.
+ """
+ policy_name = commandParseTree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ """Action can be set only with advance expression,
+ so, only check for Q and S prefixes and sys.eval_classic_expr in
+ the rule. If action field is not set, then convert the rule
+ and set ORIGIN action."""
+ if commandParseTree.keyword_exists('action'):
+ commandParseTree = CacheRedirection \
+ .convert_adv_expr_list(commandParseTree, ["rule"])
+ return [commandParseTree]
+
+ """Convert classic CR policy to advanced.
+ Syntax:
+ add cr policy -rule
+ to
+ add cr policy -rule -action ORIGIN"""
+ commandParseTree = CacheRedirection \
+ .convert_keyword_expr(commandParseTree, 'rule')
+ pol_obj.policy_type = ("classic"
+ if commandParseTree.upgraded else "advanced")
+ """If expression successfully converted into advanced,
+ then only add action."""
+ if commandParseTree.upgraded:
+ action_node = CLIKeywordName('action')
+ action_param = CLIKeywordParameter(action_node)
+ action_param.add_value('ORIGIN')
+ commandParseTree.add_keyword(action_param)
+ return [commandParseTree]
+
+ @common.register_for_cmd("bind", "cr", "vserver")
+ def convert_cr_vserver_bind(self, bind_parse_tree):
+ """
+ Handles CR vserver bind command.
+ bind cr vserver -policyName
+ -priority -gotoPriorityExpression
+ """
+ if not bind_parse_tree.keyword_exists('policyName'):
+ return [bind_parse_tree]
+
+ policy_name = bind_parse_tree.keyword_value("policyName")[0].value
+ priority_arg = "priority"
+ goto_arg = "gotoPriorityExpression"
+
+ class_name = self.__class__.__name__
+ policy_type = common.pols_binds.get_policy(policy_name).module
+ # When policy is CR policy.
+ if policy_type == class_name:
+ # check for classic built-in policy.
+ if policy_name in self.built_in_policies:
+ self.update_tree_arg(bind_parse_tree, "policyName",
+ self.built_in_policies[policy_name])
+ return self.convert_entity_policy_bind(
+ bind_parse_tree, bind_parse_tree, policy_name,
+ policy_type, priority_arg, goto_arg)
+
+ """
+ Calls the method that is registered for the particular
+ policy type that is bound to CR. Returns converted_list.
+ If the policy module is not registered for binding,
+ then returns the original parse tree.
+ """
+ key = "CacheRedirection"
+ if key in common.bind_table:
+ if policy_type in common.bind_table[key]:
+ m = common.bind_table[key][policy_type]
+ return m.method(m.obj, bind_parse_tree, policy_name,
+ priority_arg, goto_arg)
+
+ return [bind_parse_tree]
+
+
+# TODO File based Classic Expressions do not have equivalent Advanced
+# expressions. File based Classic Expressions can be used in Authorization
+# policies. This may lead to some policies being converted and some not,
+# which in overall will lead to invalid config. To avoid this issue,
+# disabling the Classic Authorization policy and its bindings
+# conversion for now.
+
+# TODO The Advanced Authorization policies can have Q and S prefixes and
+# SYS.EVAL_CLASSIC_EXPR expression which needs to be converted.
+# Registering the authorization policy to convert_advanced_expr in AdvExpression
+# class to support Advance expression conversion. While enabling back the Classic
+# Authorization policy conversion, remove the entry from convert_advanced_expr.
+#@common.register_class_methods
+class Authorization(ConvertConfig):
+ """ Handle Authorization feature """
+
+ @common.register_for_cmd("add", "authorization", "policy")
+ def convert_policy(self, commandParseTree):
+ """Convert classic Authorization policy to advanced
+ Syntax:
+ add authorization policy
+ to
+ add authorization policy
+ """
+ policy_name = commandParseTree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ commandParseTree = Authorization.convert_pos_expr(commandParseTree, 1)
+ pol_obj.policy_type = ("classic"
+ if commandParseTree.upgraded else "advanced")
+ return [commandParseTree]
+
+ # TODO need to integrate with priority interleaving.
+ @common.register_for_bind(["User", "Group", "LB", "ContentSwitching"])
+ def convert_authz_entity_bind(self, commandParseTree, policy_name,
+ priority_arg, goto_arg):
+ """
+ Handles binding of authorization policy to AAA user and group.
+ Arguments:
+ commandParseTree - bind command parse tree.
+ priority_arg - Indicates whether priority argument is
+ keyword or positional. It will be either
+ positional index or keyword name.
+ goto_arg - Indicates whether gotoPriorityExpression
+ argument is keyword or positional argument.
+ It will be either positional index or keyword name.
+ Returns converted list of parse trees.
+ """
+ policy_type = self.__class__.__name__
+ return self.convert_entity_policy_bind(
+ commandParseTree, commandParseTree,
+ policy_name, policy_type, priority_arg, goto_arg)
+
+
+@common.register_class_methods
+class TMSession(ConvertConfig):
+ """ Handle TM feature """
+
+ # classic built-in policy and its corresponding advanced built-in policy.
+ built_in_policies = {
+ "SETTMSESSPARAMS_POL": "SETTMSESSPARAMS_ADV_POL"
+ }
+
+ def __init__(self):
+ """
+ Adds the module to the list to skip the global
+ override during the priority analysis.
+ """
+ common.PoliciesAndBinds.add_to_skip_global_override(
+ self.__class__.__name__.lower())
+
+ @common.register_for_init_call
+ def store_builtin_tmsession_policy(self):
+ """
+ Creates and stores Policy object for built-in TM session policy.
+ """
+ self.store_builtin_policies()
+
+ @common.register_for_cmd("add", "tm", "sessionPolicy")
+ def convert_policy(self, commandParseTree):
+ """Convert classic TM session policy to advanced
+ Syntax:
+ add tm sessionPolicy
+ to
+ add tm sessionPolicy
+ """
+ policy_name = commandParseTree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ commandParseTree = TMSession.convert_pos_expr(commandParseTree, 1)
+ pol_obj.policy_type = ("classic"
+ if commandParseTree.upgraded else "advanced")
+ return [commandParseTree]
+
+ # override
+ flow_type_direction_default = None
+ bind_default_goto = "NEXT"
+
+ @common.register_for_bind(["User", "Group", "Authentication"])
+ def convert_tmsession_entity_bind(self, commandParseTree, policy_name,
+ priority_arg, goto_arg):
+ """
+ Handles binding of tmsession policy to AAA user, AAA group
+ and Authentication vserver.
+ Arguments:
+ commandParseTree - bind command parse tree.
+ priority_arg - Indicates whether priority argument is
+ keyword or positional. It will be either
+ positional index or keyword name.
+ goto_arg - Indicates whether gotoPriorityExpression
+ argument is keyword or positional argument.
+ It will be either positional index or keyword name.
+ Returns converted list of parse trees.
+ """
+ policy_type = self.__class__.__name__
+ # check for classic built-in policy.
+ # TM session policy can be bound to aaa user, aaa group and
+ # Authentication vserver.In all these bind commands, keyword used
+ # for policy is "policy"
+ if policy_name in self.built_in_policies:
+ self.update_tree_arg(commandParseTree, "policy",
+ self.built_in_policies[policy_name])
+ return self.convert_entity_policy_bind(
+ commandParseTree, commandParseTree,
+ policy_name, policy_type, priority_arg, goto_arg)
+
+ @common.register_for_cmd("bind", "tm", "global")
+ def convert_tm_global(self, commandParseTree):
+ """
+ Handles TM global bind command.
+ bind tm global [-policyName
+ [-priority ] [-gotoPriorityExpression
+ ]]
+ """
+ policy_name = commandParseTree.keyword_value("policyName")[0].value
+ # TM global can bind TM sesssion policies
+ # and TM traffic policies. Only TM session
+ # policies have to be handled.
+ if (common.pols_binds.get_policy(policy_name).module
+ != self.__class__.__name__):
+ return [commandParseTree]
+ priority_arg = "priority"
+ goto_arg = "gotoPriorityExpression"
+ module = self.__class__.__name__
+ # check for classic built-in policy
+ if policy_name in self.built_in_policies:
+ self.update_tree_arg(commandParseTree, "policyName",
+ self.built_in_policies[policy_name])
+ return self.convert_global_bind(commandParseTree,
+ commandParseTree, policy_name, module,
+ priority_arg, goto_arg)
+
+
+@common.register_class_methods
+class TunnelTraffic(ConvertConfig):
+ """ Handle Tunnel Traffic feature """
+
+ flow_type_direction_default = None
+
+ @common.register_for_cmd("add", "tunnel", "trafficPolicy")
+ def convert_policy(self, commandParseTree):
+ """Convert classic Tunnel traffic policy to advanced
+ Syntax:
+ add tunnel trafficPolicy
+ to
+ add tunnel trafficPolicy
+ """
+ policy_name = commandParseTree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ commandParseTree = TunnelTraffic.convert_pos_expr(commandParseTree, 1)
+ pol_obj.policy_type = ("classic"
+ if commandParseTree.upgraded else "advanced")
+ return [commandParseTree]
+
+ @common.register_for_cmd("bind", "tunnel", "global")
+ def convert_tunnel_global(self, commandParseTree):
+ """
+ Handles tunnel global bind command.
+ Syntax:
+ bind tunnel global ( [-priority ])
+ -state(ENABLED | DISABLED) [-gotoPriorityExpression ]
+ """
+ if (commandParseTree.keyword_exists("state") and
+ commandParseTree.keyword_value("state")[0].value.lower()
+ == "disabled"):
+ logging.warning("Following bind command is commented out because"
+ " state is disabled. If command is required"
+ " please take a backup because comments will"
+ " not be saved in ns.conf after triggering"
+ " 'save ns config': {}"
+ "".format(str(commandParseTree).strip()))
+ return ['#' + str(commandParseTree)]
+
+ # Classic built-in policy bindings should be disabled
+ # and the corresponding advanced built-in policy bindings should
+ # be added.
+ built_in_policies = {
+ "ns_tunnel_nocmp": "ns_adv_tunnel_nocmp",
+ "ns_tunnel_cmpall_gzip": "ns_adv_tunnel_cmpall_gzip",
+ "ns_tunnel_mimetext": "ns_adv_tunnel_mimetext",
+ "ns_tunnel_msdocs": "ns_adv_tunnel_msdocs"
+ }
+ policy_node = commandParseTree.positional_value(0)
+ policy_name = policy_node.value
+ disabled_bind_list = []
+ if policy_name in built_in_policies:
+ disabled_classic_built_in_bind = copy.deepcopy(commandParseTree)
+ # Add -state DISABLED keyword to classic
+ # built-in policy bind command.
+ self.update_tree_arg(disabled_classic_built_in_bind,
+ "state", "DISABLED")
+ disabled_bind_list = [disabled_classic_built_in_bind]
+ disabled_classic_built_in_bind.set_upgraded()
+ # Advanced built-in policy binding.
+ policy_name = built_in_policies[policy_name]
+ policy_node.set_value(policy_name)
+ # Since built-in policy add commands are not saved
+ # in ns.conf, function registered for add commands will
+ # not be called for built-in policies where policy object
+ # is stored.
+ # Policy object should be stored here for built-in policies.
+ pol_obj = common.Policy(policy_name, self.__class__.__name__,
+ "classic")
+ common.pols_binds.store_policy(pol_obj)
+ commandParseTree.set_upgraded()
+
+ priority_arg = "priority"
+ goto_arg = "gotoPriorityExpression"
+ module = self.__class__.__name__
+ return disabled_bind_list + self.convert_global_bind(
+ commandParseTree,
+ commandParseTree, policy_name,
+ module, priority_arg, goto_arg)
+
+# TODO Some of the Client Security Expressions do not have equivalent Advanced
+# expressions. This may lead to some policies being converted and some not,
+# which in overall will lead to invalid config. To avoid this issue,
+# disabling the Classic VPNTraffic policy and its bindings conversion
+# for now.
+
+# TODO The Advanced VPNTraffic policies can have Q and S prefixes and
+# SYS.EVAL_CLASSIC_EXPR expression which needs to be converted.
+# Registering the VPNTraffic policy to convert_advanced_expr in AdvExpression
+# class to support Advance expression conversion. While enabling back the Classic
+# VPNTraffic policy conversion, remove the entry from convert_advanced_expr.
+
+# TODO VPN class handles bindings of VPNTraffic policies. While enabling the
+# VPNTraffic policy conversion, enable VPN class as well.
+#@common.register_class_methods
+class VPNTraffic(ConvertConfig):
+ """ Handle VPN Traffic feature """
+
+ # override
+ flow_type_direction_default = None
+
+ def __init__(self):
+ """
+ Adds the module to the list to skip the global
+ override during the priority analysis.
+ """
+ common.PoliciesAndBinds.add_to_skip_global_override(
+ self.__class__.__name__.lower())
+
+ @common.register_for_cmd("add", "vpn", "trafficPolicy")
+ def convert_policy(self, commandParseTree):
+ """Convert classic VPN traffic policy to advanced
+ Syntax:
+ add vpn trafficPolicy
+ to
+ add vpn trafficPolicy
+ """
+ policy_name = commandParseTree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ commandParseTree = VPNTraffic.convert_pos_expr(commandParseTree, 1)
+ pol_obj.policy_type = ("classic"
+ if commandParseTree.upgraded else "advanced")
+ return [commandParseTree]
+
+ # TODO need to integrate with priority interleaving.
+ @common.register_for_bind(["User", "Group", "VPN"])
+ def convert_vpntraffic_entity_bind(self, commandParseTree, policy_name,
+ priority_arg, goto_arg):
+ """
+ Handles binding of tmsession policy to AAA user, AAA group
+ and VPN vserver.
+ Arguments:
+ commandParseTree - bind command parse tree.
+ priority_arg - Indicates whether priority argument is
+ keyword or positional. It will be either
+ positional index or keyword name.
+ goto_arg - Indicates whether gotoPriorityExpression
+ argument is keyword or positional argument.
+ It will be either positional index or keyword name.
+ Returns converted list of parse trees.
+ """
+ policy_type = self.__class__.__name__
+ return self.convert_entity_policy_bind(
+ commandParseTree, commandParseTree,
+ policy_name, policy_type, priority_arg, goto_arg)
+
+# TODO VPN class is used to handle VPN global and VPN vserver bindings
+# for VPNTraffic policies. Since the VPNTraffic policy conversion is disabled
+# for now, disabling the binding conversion as well. Enable this back while enabling
+# VPNTraffic policy conversion.
+#@common.register_class_methods
+class VPN(ConvertConfig):
+ """
+ Handles VPN global and VPN vserver
+ bind command which can bind the
+ following policies:
+ vpn clientlessAccessPolicy,
+ vpn sessionPolicy,
+ vpn trafficPolicy
+ We have to deal with only vpn sessionPolicies
+ and vpn trafficPolicies.
+ """
+
+ # override
+ flow_type_direction_default = None
+
+ # TODO Create a new class VPNSession, Handle VPN session
+ # policy conversion in VPNSession class and handle
+ # VPN session policy bindings to VPN global in
+ # the below method.
+ @common.register_for_cmd("bind", "vpn", "global")
+ def convert_vpn_global(self, commandParseTree):
+ """
+ Handles VPN global bind command.
+ bind vpn global [-policyName [-priority
+ ][-gotoPriorityExpression ]]
+ """
+ if not commandParseTree.keyword_exists('policyName'):
+ return [commandParseTree]
+ policy_name = commandParseTree.keyword_value("policyName")[0].value
+ priority_arg = "priority"
+ goto_arg = "gotoPriorityExpression"
+ if common.pols_binds.get_policy(policy_name).module == "VPNTraffic":
+ module = "VPNTraffic"
+ return self.convert_global_bind(commandParseTree,
+ commandParseTree, policy_name,
+ module, priority_arg, goto_arg)
+ else:
+ return [commandParseTree]
+
+ @common.register_for_cmd("bind", "vpn", "vserver")
+ def convert_vpn_vserver_bind(self, commandParseTree):
+ """
+ Handles VPN vserver bind command conversion.
+ bind vpn vserver [-policy [-priority
+ ] [-gotoPriorityExpression ]
+ """
+ if not commandParseTree.keyword_exists('policy'):
+ return [commandParseTree]
+
+ policy_name = commandParseTree.keyword_value("policy")[0].value
+ policy_type = common.pols_binds.get_policy(policy_name).module
+ priority_arg = "priority"
+ goto_arg = "gotoPriorityExpression"
+
+ """
+ Calls the method that is registered for the particular
+ policy type that is bound to vserver. Returns converted_list.
+ If the policy module is not registered for binding,
+ then returns the original parse tree.
+ """
+ key = "VPN"
+ if key in common.bind_table:
+ if policy_type in common.bind_table[key]:
+ m = common.bind_table[key][policy_type]
+ return m.method(
+ m.obj, commandParseTree, policy_name, priority_arg,
+ goto_arg)
+ return [commandParseTree]
+
+
+@common.register_class_methods
+class APPFw(ConvertConfig):
+ """ Handle APPFw feature """
+
+ @common.register_for_cmd("add", "appfw", "policy")
+ def convert_policy(self, commandParseTree):
+ """Convert classic AppFw policy to advanced
+ Syntax:
+ add appfw policy
+ to
+ add appfw policy
+ """
+ policy_name = commandParseTree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ commandParseTree = APPFw.convert_pos_expr(commandParseTree, 1)
+ pol_obj.policy_type = ("classic"
+ if commandParseTree.upgraded else "advanced")
+ return [commandParseTree]
+
+ @common.register_for_cmd("bind", "appfw", "global")
+ def convert_appfw_global_bind(self, commandParseTree):
+ """
+ Handles appfw global bindinds.
+ Syntax:
+ bind appfw global
+ []
+ [-state ( ENABLED | DISABLED )]
+ """
+
+ # Remove bind command when state is disabled.
+ if (commandParseTree.keyword_exists("state") and
+ commandParseTree.keyword_value("state")[0].value.lower()
+ == "disabled"):
+ logging.warning("Following bind command is commented out because"
+ " state is disabled. If command is required"
+ " please take a backup because comments will"
+ " not be saved in ns.conf after triggering"
+ " 'save ns config': {}"
+ "".format(str(commandParseTree).strip()))
+ return ['#' + str(commandParseTree)]
+
+ priority_arg = 1
+ goto_arg = 2
+ module = self.__class__.__name__
+ policy_name = commandParseTree.positional_value(0).value
+ return self.convert_global_bind(commandParseTree,
+ commandParseTree, policy_name, module,
+ priority_arg, goto_arg)
+
+ @common.register_for_bind(["LB"])
+ def convert_appfw_vserver_bind(self, commandParseTree, policy_name,
+ priority_arg, goto_arg):
+ """
+ Handles binding of appfw policy to LB vserver.
+ Arguments:
+ commandParseTree - bind command parse tree.
+ priority_arg - Indicates whether priority argument is
+ keyword or positional. It will be either
+ positional index or keyword name.
+ goto_arg - Indicates whether gotoPriorityExpression
+ argument is keyword or positional argument.
+ It will be either positional index or keyword name.
+ Returns converted list of parse trees.
+ """
+ policy_type = self.__class__.__name__
+ return self.convert_entity_policy_bind(commandParseTree,
+ commandParseTree, policy_name,
+ policy_type, priority_arg,
+ goto_arg)
+
+
+@common.register_class_methods
+class Syslog(ConvertConfig):
+ """ Handle Nslog feature """
+
+ # TODO Classic Syslog policy conversion is disabled for now. The Advanced
+ # Syslog policies can have Q and S prefixes and SYS.EVAL_CLASSIC_EXPR expression
+ # which needs to be converted. Registering the Syslog policy to convert_advanced_expr
+ # in AdvExpression class to support Advance expression conversion.
+ # While enabling back the Classic Syslog policy conversion, remove the
+ # entry from convert_advanced_expr.
+
+ # TODO uncomment this when the bind command conversion is supported.
+ # @common.register_for_cmd("add", "audit", "syslogPolicy")
+ def convert_policy(self, commandParseTree):
+ """Convert classic Syslog policy to advanced
+ Syntax:
+ add audit syslogPolicy
+ to
+ add audit syslogPolicy
+ """
+ policy_name = commandParseTree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ commandParseTree = Syslog.convert_pos_expr(commandParseTree, 1)
+ pol_obj.policy_type = ("classic"
+ if commandParseTree.upgraded else "advanced")
+ return [commandParseTree]
+
+
+@common.register_class_methods
+class Nslog(ConvertConfig):
+ """ Handle Nslog feature """
+
+ # TODO Classic Nslog policy conversion is disabled for now. The Advanced
+ # Nslog policies can have Q and S prefixes and SYS.EVAL_CLASSIC_EXPR expression
+ # which needs to be converted. Registering the Nslog policy to convert_advanced_expr
+ # in AdvExpression class to support Advance expression conversion.
+ # While enabling back the Classic Nslog policy conversion, remove the
+ # entry from convert_advanced_expr.
+
+ # TODO uncomment this when the bind command conversion is supported.
+ # @common.register_for_cmd("add", "audit", "nslogPolicy")
+ def convert_policy(self, commandParseTree):
+ """Convert classic Nslog policy to advanced
+ Syntax:
+ add audit nslogPolicy
+ to
+ add audit nslogPolicy
+ """
+ policy_name = commandParseTree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ commandParseTree = Nslog.convert_pos_expr(commandParseTree, 1)
+ pol_obj.policy_type = ("classic"
+ if commandParseTree.upgraded else "advanced")
+ return [commandParseTree]
+
+
+@common.register_class_methods
+class Patset(ConvertConfig):
+ """ Patset entity """
+
+ @common.register_for_cmd("add", "policy", "patset")
+ def register_name(self, commandParseTree):
+ Patset.register_policy_entity_name(commandParseTree)
+ return [commandParseTree]
+
+
+@common.register_class_methods
+class Dataset(ConvertConfig):
+ """ Dataset entity """
+
+ @common.register_for_cmd("add", "policy", "dataset")
+ def register_name(self, commandParseTree):
+ Dataset.register_policy_entity_name(commandParseTree)
+ return [commandParseTree]
+
+
+@common.register_class_methods
+class HTTP_CALLOUT(ConvertConfig):
+ """ HTTP callout entity """
+
+ @common.register_for_cmd("add", "policy", "httpCallout")
+ def register_name(self, commandParseTree):
+ callout_name = commandParseTree.positional_value(0).value
+ lower_callout_name = callout_name.lower()
+ if (lower_callout_name in classic_entities_names):
+ """
+ This will be true only if the classic named expression has
+ the same name as the callout entity name.
+ """
+ logging.error(("HTTP callout name {} is conflicting with"
+ " named expression entity name, please resolve"
+ " the conflict.").format(callout_name))
+ else:
+ HTTP_CALLOUT.register_policy_entity_name(commandParseTree)
+ commandParseTree = HTTP_CALLOUT.convert_adv_expr_list(
+ commandParseTree, ["hostExpr", "urlStemExpr", "headers",
+ "parameters", "bodyExpr", "fullReqExpr", "resultExpr"])
+ return [commandParseTree]
+
+
+@common.register_class_methods
+class StringMap(ConvertConfig):
+ """ String map entity """
+
+ @common.register_for_cmd("add", "policy", "stringmap")
+ def register_name(self, commandParseTree):
+ StringMap.register_policy_entity_name(commandParseTree)
+ return [commandParseTree]
+
+
+@common.register_class_methods
+class NSVariable(ConvertConfig):
+ """ NS Variable entity """
+
+ @common.register_for_cmd("add", "ns", "variable")
+ def register_name(self, commandParseTree):
+ NSVariable.register_policy_entity_name(commandParseTree)
+ return [commandParseTree]
+
+
+@common.register_class_methods
+class EncryptionKey(ConvertConfig):
+ """ Encryption key entity """
+
+ @common.register_for_cmd("add", "ns", "encryptionKey")
+ def register_name(self, commandParseTree):
+ EncryptionKey.register_policy_entity_name(commandParseTree)
+ return [commandParseTree]
+
+
+@common.register_class_methods
+class HMACKey(ConvertConfig):
+ """ HMAC key entity """
+
+ @common.register_for_cmd("add", "ns", "hmacKey")
+ def register_name(self, commandParseTree):
+ HMACKey.register_policy_entity_name(commandParseTree)
+ return [commandParseTree]
+
+
+@common.register_class_methods
+class NamedExpression(ConvertConfig):
+ """ Handle Named expression feature """
+
+ # Built-in classic named expression names and there
+ # corresponding built-in advanced named expression names.
+ built_in_named_expr = {
+ "ns_true": "TRUE",
+ "ns_false": "FALSE",
+ "ns_non_get": "ns_non_get_adv",
+ "ns_cachecontrol_nostore": "ns_cachecontrol_nostore_adv",
+ "ns_cachecontrol_nocache": "ns_cachecontrol_nocache_adv",
+ "ns_header_pragma": "ns_header_pragma_adv",
+ "ns_header_cookie": "ns_header_cookie_adv",
+ "ns_ext_cgi": "ns_ext_cgi_adv",
+ "ns_ext_asp": "ns_ext_asp_adv",
+ "ns_ext_exe": "ns_ext_exe_adv",
+ "ns_ext_cfm": "ns_ext_cfm_adv",
+ "ns_ext_ex": "ns_ext_ex_adv",
+ "ns_ext_shtml": "ns_ext_shtml_adv",
+ "ns_ext_htx": "ns_ext_htx_adv",
+ "ns_url_path_cgibin": "ns_url_path_cgibin_adv",
+ "ns_url_path_exec": "ns_url_path_exec_adv",
+ "ns_url_path_bin": "ns_url_path_bin_adv",
+ "ns_url_tokens": "ns_url_tokens_adv",
+ "ns_ext_not_gif": "ns_ext_not_gif_adv",
+ "ns_ext_not_jpeg": "ns_ext_not_jpeg_adv",
+ "ns_cmpclient": "ns_cmpclient_adv",
+ "ns_slowclient": "ns_slowclient_adv",
+ "ns_content_type": "ns_content_type_advanced",
+ "ns_msword": "ns_msword_advanced",
+ "ns_msexcel": "ns_msexcel_advanced",
+ "ns_msppt": "ns_msppt_advanced",
+ "ns_css": "ns_css_adv",
+ "ns_xmldata": "ns_xmldata_adv",
+ "ns_mozilla_47": "ns_mozilla_47_adv",
+ "ns_msie": "ns_msie_adv"
+ }
+
+ @staticmethod
+ def register_built_in_named_exprs():
+ """
+ Register built-in classic Named expression names in
+ classic_entities_names and built-in advanced Named expression names
+ in policy_entities_names.
+ """
+ for classic_exp_name in NamedExpression.built_in_named_expr:
+ classic_entities_names.add(classic_exp_name)
+ policy_entities_names.add(NamedExpression.built_in_named_expr[
+ classic_exp_name].lower())
+
+ @common.register_for_cmd("add", "policy", "expression")
+ def convert_policy(self, commandParseTree):
+ """
+ Classic named expression name is not
+ valid for advanced expression if:
+ 1. It the name is same as one of the Policy
+ entity (patset/dataset/stringmap/
+ variable/hmacKey/EncriptionKey/callout) name.
+ 2. it doesn't start with ASCII alphabetic character or underscore.
+ 3. it has characters other than ASCII alphanumerics
+ or underscore characters.
+ 4. it is equal to a advanced policy expression reserved word (prefix identifier or
+ enum value)
+ """
+ reserved_word_list = set(
+ [ # Advanced policy expression prefix list
+ "subscriber",
+ "connection",
+ "analytics",
+ "diameter",
+ "target",
+ "server",
+ "radius",
+ "oracle",
+ "extend",
+ "client",
+ "mysql",
+ "mssql",
+ "false",
+ "true",
+ "text",
+ "smpp",
+ "icap",
+ "http",
+ "url",
+ "sys",
+ "sip",
+ "ica",
+ "dns",
+ "aaa",
+ "s",
+ "q",
+ "re",
+ "xp",
+ "ce"
+ ])
+
+ expr_name = commandParseTree.positional_value(0).value
+ expr_rule = commandParseTree.positional_value(1).value
+ named_expr[expr_name] = expr_rule
+ lower_expr_name = expr_name.lower()
+ if (((lower_expr_name in reserved_word_list) or
+ (re.match('^[a-z_][a-z0-9_]*$', lower_expr_name) is None) or
+ (lower_expr_name in policy_entities_names))):
+ logging.error(("Expression name {} is invalid for advanced "
+ "expression: names must begin with an ASCII "
+ "alphabetic character or underscore and must "
+ "contain only ASCII alphanumerics or underscores"
+ " and shouldn't be name of another policy entity"
+ "; words reserved for policy use may not be used;"
+ " underscores will be substituted for any invalid"
+ " characters in corresponding advanced name")
+ .format(expr_name))
+
+ if commandParseTree.keyword_exists('clientSecurityMessage'):
+ logging.error(("Error in converting expression {} : "
+ "conversion of clientSecurityMessage based "
+ "expression is not supported.")
+ .format(expr_name))
+ return [commandParseTree]
+
+ original_tree = copy.deepcopy(commandParseTree)
+ """Convert classic named expression to advanced
+ Syntax:
+ add policy expression
+ to
+ add policy expression
+ """
+ commandParseTree = NamedExpression \
+ .convert_pos_expr(commandParseTree, 1)
+
+ if commandParseTree.adv_upgraded:
+ tree_list = [commandParseTree]
+ else:
+ tree_list = [original_tree]
+ if commandParseTree.upgraded:
+ """
+ Because we are not currently converting all the commands that
+ use named expressions, we can have the situation where a
+ non-converted command uses a Classic named expression but a
+ converted command would have used the same named expression.
+ To deal with this we create an Advanced that is equivalent
+ to the old Classic, give it a new name, and replace all the
+ references to the old Classic in converted expressions to the
+ corresponding Advanced. Because of that we need to return
+ both the old Classic and corresponding Advanced named
+ expressions from this routine.
+ """
+ name_node = commandParseTree.positional_value(0)
+ name_node.set_value(get_advanced_name(name_node.value))
+ tree_list.append(commandParseTree)
+ NamedExpression.register_policy_entity_name(commandParseTree)
+ NamedExpression.register_classic_entity_name(original_tree)
+ else:
+ NamedExpression.register_policy_entity_name(original_tree)
+ return tree_list
+
+
+@common.register_class_methods
+class HTTPProfile(ConvertConfig):
+ """ Handle HTTP Profile """
+
+ @common.register_for_cmd("add", "ns", "httpProfile")
+ @common.register_for_cmd("set", "ns", "httpProfile")
+ def convert_spdy(self, commandParseTree):
+ """Convert spdy feature to HTTP2
+ Syntax:
+ add ns httpProfile -spdy
+ to
+ add ns httpProfile -http2 ENABLED
+ """
+ if commandParseTree.keyword_exists('spdy'):
+ commandParseTree.remove_keyword('spdy')
+ http2_keyword = CLIKeywordParameter(CLIKeywordName("http2"))
+ http2_keyword.add_value('ENABLED')
+ commandParseTree.add_keyword(http2_keyword)
+ commandParseTree = HTTPProfile \
+ .convert_adv_expr_list(commandParseTree, ["clientIpHdrExpr"])
+ return [commandParseTree]
+
+
+@common.register_class_methods
+class ContentSwitching(ConvertConfig):
+ """ Handle Content Switching feature """
+
+ # override
+ bind_default_goto = None
+
+ def __init__(self):
+ """
+ _policy_bind_info - Contains information about classic policies
+ without actions and there bind commands.
+ key - policy name
+ value - dictionary with the following keys:
+ "policy_tree" - Policy tree without action
+ "bind_trees" - List of bind trees where
+ the corresponding policy is
+ bound.
+ """
+ self._policy_bind_info = OrderedDict()
+
+ @common.register_for_cmd("add", "cs", "vserver")
+ def convert_cs_vserver(self, commandParseTree):
+ """
+ Get vserver protocol to help in filter bind conversion
+ cs_protocol - cs vserver protocol
+ csv_name - cs vserver name
+ vserver_protocol_dict - dict to store protocol as value to the
+ vserver name as key
+ """
+ cs_protocol = commandParseTree.positional_value(1).value
+ csv_name = commandParseTree.positional_value(0).value
+ vserver_protocol_dict[csv_name] = cs_protocol.upper()
+ commandParseTree = ContentSwitching.convert_adv_expr_list(
+ commandParseTree, ["Listenpolicy", "pushLabel"])
+ return [commandParseTree]
+
+ @common.register_for_cmd("add", "cs", "policy")
+ def convert_policy(self, commandParseTree):
+ """Convert CS policy to advanced
+ Syntax:
+ Conversion happens as:
+ 1. add cs policy -domain
+ to
+ add cs policy -rule HTTP.REQ.HOSTNAME.EQ("")
+
+ 2. add cs policy -rule
+ to
+ add cs policy -rule
+
+ 3. add cs policy -rule -domain
+ to
+ add cs policy -rule
+ ' && HTTP.REQ.HOSTNAME.EQ("")'
+
+ 4. add cs policy -url
+ to
+ add cs policy -rule
+
+ 5. add cs policy -url -domain
+ to
+ add cs policy -rule ")>
+
+ """
+ policy_name = commandParseTree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ """ Only in advanced policy, action can be present.
+ """
+ if commandParseTree.keyword_exists('action'):
+ pol_obj.policy_type = "advanced"
+ commandParseTree = ContentSwitching.convert_adv_expr_list(
+ commandParseTree, ["rule"])
+ return [commandParseTree]
+ if commandParseTree.keyword_exists('rule'):
+ if commandParseTree.keyword_exists('domain'):
+ rule_node = commandParseTree.keyword_value('rule')
+ rule_expr = rule_node[0].value
+ converted_expr = convert_classic_expr.convert_classic_expr(
+ rule_expr)
+ if converted_expr is None:
+ logging.error('Error in converting command : ' +
+ str(commandParseTree))
+ return [commandParseTree]
+ converted_expr = converted_expr.strip('"')
+ domain_name = commandParseTree.keyword_value('domain')[0] \
+ .value
+ domain_rule = 'HTTP.REQ.HOSTNAME.EQ(\\"' + \
+ domain_name + '\\")'
+ commandParseTree.remove_keyword('domain')
+ complete_expr = '"(' + converted_expr + ') && ' + \
+ domain_rule + '"'
+ rule_node[0].set_value(complete_expr, True)
+ commandParseTree.set_upgraded()
+ else:
+ commandParseTree = ContentSwitching \
+ .convert_keyword_expr(commandParseTree, 'rule')
+ elif commandParseTree.keyword_exists('url'):
+ domain_rule = None
+ converted_url_expr = None
+ prefix = None
+ prefix_val = None
+ suffix = None
+ url_expr = commandParseTree.keyword_value('url')[0].value
+ if url_expr.endswith('.'):
+ converted_url_expr = 'HTTP.REQ.URL.PATH.EQ("' + \
+ url_expr + '.")'
+ else:
+ prefix_suffix = url_expr.rsplit('.', 1)
+ if len(prefix_suffix) is 1:
+ """ No suffix is present in URL."""
+ prefix = prefix_suffix[0]
+ suffix = None
+ if prefix.endswith('*'):
+ prefix_val = prefix[:-1]
+ else:
+ """ Suffix is present in URL."""
+ prefix = prefix_suffix[0]
+ suffix = prefix_suffix[1]
+ """
+ If URL is abc..*.html, then
+ in classic code, we don't check
+ one dot before *, and this happens
+ only if there is some suffix.
+ """
+ if prefix.endswith('*'):
+ prefix_val = prefix[:-1]
+ if prefix_val.endswith('.'):
+ prefix_val = prefix_val[:-1]
+
+ if suffix and (prefix != '/') and (not prefix.endswith('*')):
+ converted_url_expr = 'HTTP.REQ.URL.PATH.EQ("' + \
+ url_expr + '")'
+ elif (suffix is None) and (not prefix.endswith('*')):
+ converted_url_expr = 'HTTP.REQ.URL.PATH.EQ(("' + \
+ prefix + '." + HTTP.REQ.URL.SUFFIX).' + \
+ 'STRIP_END_CHARS("."))'
+ elif (prefix == '/*') and (suffix is not None):
+ converted_url_expr = 'HTTP.REQ.URL.SUFFIX.EQ("' + \
+ suffix + '")'
+ elif (prefix.endswith('*')) and (suffix is not None):
+ converted_url_expr = '(HTTP.REQ.URL.STARTSWITH("' + \
+ prefix_val + '") && HTTP.REQ.URL.SUFFIX.EQ("' + \
+ suffix + '"))'
+ elif (prefix == '/*'):
+ converted_url_expr = 'true'
+ elif (prefix.endswith('*')):
+ converted_url_expr = 'HTTP.REQ.URL.STARTSWITH("' + \
+ prefix_val + '")'
+ elif (suffix is not None) and (prefix == '/'):
+ converted_url_expr = 'HTTP.REQ.URL.SUFFIX.EQ("' + \
+ suffix + '")'
+
+ if commandParseTree.keyword_exists('domain'):
+ domain_name = commandParseTree.keyword_value('domain')[0] \
+ .value
+ domain_rule = 'HTTP.REQ.HOSTNAME.EQ("' + domain_name + '")'
+ commandParseTree.remove_keyword('domain')
+
+ if (domain_rule):
+ converted_url_expr = converted_url_expr + ' && ' + domain_rule
+
+ commandParseTree.remove_keyword('url')
+ rule_keyword = CLIKeywordParameter(CLIKeywordName('rule'))
+ rule_keyword.add_value(converted_url_expr)
+ commandParseTree.add_keyword(rule_keyword)
+ elif commandParseTree.keyword_exists('domain'):
+ domain_name = commandParseTree.keyword_value('domain')[0].value
+ domain_rule = 'HTTP.REQ.HOSTNAME.EQ("' + domain_name + '")'
+ commandParseTree.remove_keyword('domain')
+ rule_keyword = CLIKeywordParameter(CLIKeywordName("rule"))
+ rule_keyword.add_value(domain_rule)
+ commandParseTree.add_keyword(rule_keyword)
+
+ if commandParseTree.upgraded:
+ pol_obj.policy_type = "classic"
+ # Saving policies for resolving multiple bind points for
+ # CS policies without action issue.
+ self._policy_bind_info[policy_name] = {}
+ self._policy_bind_info[policy_name]["policy_tree"] = \
+ commandParseTree
+ return []
+ else:
+ pol_obj.policy_type = "advanced"
+ return [commandParseTree]
+
+ @common.register_for_cmd("bind", "cs", "vserver")
+ def convert_cs_bind_command(self, commandParseTree):
+ """
+ Handles CS vserver bind command.
+ bind cs vserver -policyName
+ -priority [-gotoPriorityExpression ]
+ """
+ if not commandParseTree.keyword_exists('policyName'):
+ return [commandParseTree]
+
+ # Get the policy name
+ policy_name = commandParseTree.keyword_value('policyName')[0].value
+ priority_arg = "priority"
+ goto_arg = "gotoPriorityExpression"
+
+ class_name = self.__class__.__name__
+ policy_type = common.pols_binds.get_policy(policy_name).module
+ if policy_type == class_name:
+ if policy_name in self._policy_bind_info:
+ # Saving bind commands for resolving multiple bind points for
+ # CS policies without action issue.
+ if "bind_trees" not in self._policy_bind_info[policy_name]:
+ self._policy_bind_info[policy_name]["bind_trees"] = []
+ self._policy_bind_info[policy_name]["bind_trees"].append(
+ commandParseTree)
+ return []
+ else:
+ return self.convert_entity_policy_bind(
+ commandParseTree, commandParseTree,
+ policy_name, policy_type, priority_arg, goto_arg)
+
+ """
+ Calls the method that is registered for the particular
+ policy type that is bound to CS. Returns converted_list.
+ If the policy module is not registered for binding,
+ then returns the original parse tree.
+ """
+ key = "ContentSwitching"
+ if key in common.bind_table:
+ if policy_type in common.bind_table[key]:
+ m = common.bind_table[key][policy_type]
+ return m.method(m.obj, commandParseTree, policy_name,
+ priority_arg, goto_arg)
+ return [commandParseTree]
+
+ @common.register_for_bind(["CacheRedirection"])
+ def convert_cs_policy_entity_bind(
+ self, commandParseTree, policy_name, priority_arg, goto_arg):
+ """
+ Handles CS policy binding to CR vserver.
+ Arguments:
+ commandParseTree - bind command parse tree.
+ priority_arg - Indicates whether priority argument is
+ keyword or positional. It will be either
+ positional index or keyword name.
+ goto_arg - Indicates whether gotoPriorityExpression
+ argument is keyword or positional argument.
+ It will be either positional index or keyword name.
+ Returns converted list of parse trees.
+ """
+ policy_type = self.__class__.__name__
+ if policy_name in self._policy_bind_info:
+ # Saving bind commands for resolving multiple bind points for
+ # CS policies without action issue.
+ if "bind_trees" not in self._policy_bind_info[policy_name]:
+ self._policy_bind_info[policy_name]["bind_trees"] = []
+ self._policy_bind_info[policy_name]["bind_trees"].append(
+ commandParseTree)
+ return []
+ else:
+ return self.convert_entity_policy_bind(
+ commandParseTree, commandParseTree,
+ policy_name, policy_type, priority_arg, goto_arg)
+
+ @common.register_for_final_call
+ def get_converted_cmds(self):
+ """
+ Classic CS policies do not support CS action. But in advanced policies
+ there is limitation that multiple bind points are not allowed for
+ CS policies without action. This will lead to issue during the
+ conversion if any classic CS policy is bound to multiple bind points.
+ To avoid this issue following steps are followed for each bind command:
+ 1. Get policy name and vserver name from bind command.
+ 2. New action is created with name "nspepi_adv_cs_act_"
+ 3. New policy is created with name
+ "nspepi_adv__" and
+ set action keyword to "nspepi_adv_cs_act_"
+ 4. Remove targetLBVserver from bind command and update the policy name
+ to newly created policy name.
+ 5. If same policy is bound to different vservers, multiple policies
+ are created.
+ CS policies can be bound to CS vserver and CR vserver.
+ Return list of newly added CS actions and policies.
+ """
+ newly_added_policy_names = []
+ newly_added_action_names = []
+ pol_list = []
+ act_list = []
+ overlength_action_names = {}
+ overlength_policy_names = {}
+ overlength_action_counter = 0
+ overlength_policy_counter = 0
+ for policy_name in self._policy_bind_info:
+ policy_tree = self._policy_bind_info[policy_name]["policy_tree"]
+ if "bind_trees" not in self._policy_bind_info[policy_name]:
+ # when policy is not used in any bind command.
+ pol_list.append(policy_tree)
+ continue
+ for index in range(len(self._policy_bind_info[policy_name][
+ "bind_trees"])):
+ vserver_name = ""
+ bind_tree = self._policy_bind_info[policy_name][
+ "bind_trees"][index]
+ if ((' '.join(bind_tree.get_command_type())).lower() ==
+ "bind cs vserver"):
+ vserver_name = bind_tree.keyword_value(
+ "targetLBVserver")[0].value
+ elif ((' '.join(bind_tree.get_command_type())).lower() ==
+ "bind cr vserver"):
+ vserver_name = bind_tree.keyword_value(
+ "policyName")[1].value
+ new_policy_name = "nspepi_adv_" + policy_name + '_' + \
+ vserver_name
+ truncated_pol_name = new_policy_name
+ action_name = "nspepi_adv_cs_act_" + vserver_name
+ truncated_act_name = action_name
+ if new_policy_name not in newly_added_policy_names:
+ if action_name not in newly_added_action_names:
+ # Create new action
+ action_tree = CLICommand("add", "cs", "action")
+ # Check action name length. Max allowed length is 127
+ if len(action_name) > 127:
+ truncated_act_name, overlength_action_counter = \
+ self.truncate_name(action_name,
+ overlength_action_names,
+ overlength_action_counter)
+ pos = CLIPositionalParameter(truncated_act_name)
+ action_tree.add_positional(pos)
+ vserver_key = CLIKeywordParameter(CLIKeywordName(
+ "targetLBVserver"))
+ vserver_key.add_value(vserver_name)
+ action_tree.add_keyword(vserver_key)
+ act_list.append(action_tree)
+ newly_added_action_names.append(action_name)
+ else:
+ # when action is already added.
+ # Get truncated name if truncated.
+ if action_name in overlength_action_names:
+ truncated_act_name = overlength_action_names[
+ action_name]
+ # Create new policy with [policy_name]_[vserver_name] as
+ # as name and bind to newly created action
+ # cs_act_[vserver_name]
+ new_policy = copy.deepcopy(policy_tree)
+ # Max length of policy name allowed is 127.
+ truncated_pol_name = new_policy_name
+ if len(new_policy_name) > 127:
+ truncated_pol_name, overlength_policy_counter = \
+ self.truncate_name(new_policy_name,
+ overlength_policy_names,
+ overlength_policy_counter)
+ self.update_tree_arg(new_policy, 0, truncated_pol_name)
+ action_key = CLIKeywordParameter(CLIKeywordName("action"))
+ action_key.add_value(truncated_act_name)
+ new_policy.add_keyword(action_key)
+ newly_added_policy_names.append(new_policy_name)
+ pol_list.append(new_policy)
+ else:
+ # When policy is already added.
+ # Get truncated policy name if truncated.
+ if new_policy_name in overlength_policy_names:
+ truncated_pol_name = overlength_policy_names[
+ new_policy_name]
+ # Remove targetLBVserver from bind command and update policy
+ # name to newly added policy name.
+ priority_arg = "priority"
+ goto_arg = "gotoPriorityExpression"
+ policy_type = self.__class__.__name__
+ self.update_tree_arg(bind_tree, "policyName",
+ truncated_pol_name)
+ if ((' '.join(bind_tree.get_command_type())).lower() ==
+ "bind cs vserver"):
+ bind_tree.remove_keyword("targetLBVserver")
+ elif ((' '.join(bind_tree.get_command_type())).lower() ==
+ "bind cr vserver"):
+ # In bind cr vserver command, vserver name exists in
+ # following way:
+ # bind cr vserver -policyName
+ #
+ bind_tree.remove_keyword_value("policyName", 1)
+ self.convert_entity_policy_bind(
+ bind_tree, bind_tree, policy_name,
+ policy_type, priority_arg, goto_arg)
+ return act_list + pol_list
+
+ def truncate_name(self, name, name_mapping, counter):
+ """
+ Truncates name shorter than 127 and adds a counter at the end.
+ name - name that should be truncated.
+ name_mapping - dictionary which saves name and its truncated name.
+ key - name
+ value - truncated name
+ counter - counter to be appended at the end of truncated name.
+ """
+ counter += 1
+ # Reserving 1 for '_' + 6 for counter.
+ truncated_name = name[0: 120]
+ truncated_name += "_" + str(counter)
+ name_mapping[name] = truncated_name
+ return truncated_name, counter
+
+
+@common.register_class_methods
+class AAA(ConvertConfig):
+
+ @common.register_for_cmd("add", "aaa", "group")
+ def convert_add_group(self, tree):
+ """
+ Process: add aaa group [-weight ]
+ and store weight for each group.
+
+ Args:
+ tree: Command parse tree for add aaa group command
+
+ Returns:
+ tree: Processed command parse tree for add aaa group command
+ """
+ groupname = common.get_cmd_arg(0, tree)
+ weight = common.get_cmd_arg("weight", tree)
+ weight = weight if weight else "0"
+ common.pols_binds.store_group(common.Group(groupname, weight))
+ return [tree]
+
+ def user_group_bind_common(self, tree, key):
+ """
+ Common processing for bind aaa user and bind aaa group.
+ bind aaa user [-policy ]
+ [-priority ] [-type ]
+ [-gotoPriorityExpression ] ...
+ bind aaa group [-policy ]
+ [-priority ] [-type ]
+ [-gotoPriorityExpression ] ...
+ """
+ policy_name = common.get_cmd_arg("policy", tree)
+ if not policy_name:
+ return [tree]
+ policy_type = common.pols_binds.get_policy(policy_name).module
+ priority_arg = "priority"
+ goto_arg = "gotoPriorityExpression"
+
+ # Calls the method that is registered for the particular
+ # policy type that is bound to the user or group.
+ # Returns converted_list. If the policy module is not
+ # registered for binding, then returns the original parse
+ # tree.
+ if key in common.bind_table:
+ if policy_type in common.bind_table[key]:
+ m = common.bind_table[key][policy_type]
+ return m.method(m.obj, tree, policy_name, priority_arg,
+ goto_arg)
+ return [tree]
+
+ @common.register_for_cmd("bind", "aaa", "user")
+ def convert_user_bind(self, tree):
+ return self.user_group_bind_common(tree, "User")
+
+ @common.register_for_cmd("bind", "aaa", "group")
+ def convert_group_bind(self, tree):
+ return self.user_group_bind_common(tree, "Group")
+
+@common.register_class_methods
+class AdvExpression(ConvertConfig):
+ """
+ Handles conversion of Q and S prefixes and SYS.EVAL_CLASSIC_EXPR expression in commands
+ which allows only advanced expressions.
+ """
+
+ @common.register_for_cmd("add", "videooptimization", "detectionpolicy")
+ @common.register_for_cmd("add", "videooptimization", "pacingpolicy")
+ @common.register_for_cmd("add", "dns", "policy")
+ @common.register_for_cmd("add", "cache", "selector")
+ @common.register_for_cmd("add", "cs", "action")
+ @common.register_for_cmd("add", "vpn", "clientlessAccessPolicy")
+ @common.register_for_cmd("add", "authentication", "webAuthAction")
+ @common.register_for_cmd("set", "authentication", "webAuthAction")
+ @common.register_for_cmd("add", "tm", "trafficPolicy")
+ @common.register_for_cmd("add", "authentication", "samlIdPPolicy")
+ @common.register_for_cmd("add", "feo", "policy")
+ @common.register_for_cmd("add", "cache", "policy")
+ @common.register_for_cmd("add", "transform", "policy")
+ @common.register_for_cmd("add", "appqoe", "action")
+ @common.register_for_cmd("add", "appqoe", "policy")
+ @common.register_for_cmd("add", "ssl", "policy")
+ @common.register_for_cmd("add", "appflow", "policy")
+ @common.register_for_cmd("add", "autoscale", "policy")
+ @common.register_for_cmd("add", "authentication", "Policy")
+ @common.register_for_cmd("add", "authentication", "loginSchemaPolicy")
+ @common.register_for_cmd("add", "authentication", "loginSchema")
+ @common.register_for_cmd("add", "gslb", "vserver")
+ @common.register_for_cmd("add", "ns", "assignment")
+ @common.register_for_cmd("add", "dns", "action64")
+ @common.register_for_cmd("add", "dns", "policy64")
+ @common.register_for_cmd("add", "authentication", "OAuthIdPPolicy")
+ @common.register_for_cmd("add", "authentication", "samlIdPProfile")
+ @common.register_for_cmd("add", "contentInspection", "policy")
+ @common.register_for_cmd("add", "ica", "policy")
+ @common.register_for_cmd("add", "lb", "group")
+ @common.register_for_cmd("add", "audit", "messageaction")
+ @common.register_for_cmd("add", "aaa", "preauthenticationpolicy")
+ @common.register_for_cmd("add", "spillover", "policy")
+ @common.register_for_cmd("add", "stream", "selector")
+ @common.register_for_cmd("add","tm", "formSSOAction")
+ @common.register_for_cmd("add", "tm", "samlSSOProfile")
+ @common.register_for_cmd("add", "vpn", "sessionPolicy")
+ @common.register_for_cmd("add", "vpn", "trafficAction")
+ @common.register_for_cmd("add", "vpn", "vserver")
+ # TODO: This entry need to be removed when Classic Syslog policy
+ # conversion is enabled in Syslog class.
+ @common.register_for_cmd("add", "audit", "syslogPolicy")
+ # TODO: This entry need to be removed when Classic Nslog policy
+ # conversion is enabled in Nslog class.
+ @common.register_for_cmd("add", "audit", "nslogPolicy")
+ # TODO: This entry need to be removed when Classic authorization policy
+ # conversion is enabled in Authorization class.
+ @common.register_for_cmd("add", "authorization", "policy")
+ # TODO: This entry need to be removed when Classic VPNTraffic policy
+ # conversion is enabled in VPNTraffic class.
+ @common.register_for_cmd("add", "vpn", "trafficPolicy")
+ def convert_advanced_expr(self, tree):
+ """
+ Commands which allows ONLY advanced expressions should be registered for this method.
+ Handles conversion of Q and S prefixes and SYS.EVAL_CLASSIC_EXPR expression.
+ Each command that will be registered to this method, should add an entry in
+ command_parameters_list.
+ """
+
+ # Each command should mention the list of parameters where advanced expression
+ # can be used. Only these parameters will be checked for SYS.EVAL_CLASSIC_EXPR
+ # expression.
+ # If its a keyword parameter, mention the keyword name.
+ # If its a positional parameter, mention the position of the parameter.
+ command_parameters_list = {
+ "add videooptimization detectionpolicy": ["rule"],
+ "add videooptimization pacingpolicy": ["rule"],
+ "add dns policy": [1],
+ "add cache selector": [1, 2, 3, 4, 5, 6, 7, 8],
+ "add cs action": ["targetVserverExpr"],
+ "add vpn clientlessaccesspolicy": [1],
+ "add authentication webauthaction": ["fullReqExpr", "successRule"],
+ "set authentication webauthaction": ["fullReqExpr", "successRule"],
+ "add tm trafficpolicy": [1],
+ "add authentication samlidppolicy": ["rule"],
+ "add feo policy": [1],
+ "add cache policy": ["rule"],
+ "add transform policy": [1],
+ "add appqoe action": ["dosTrigExpression"],
+ "add appqoe policy": ["rule"],
+ "add ssl policy": ["rule"],
+ "add appflow policy": [1],
+ "add autoscale policy": ["rule"],
+ "add authentication policy": ["rule"],
+ "add authentication loginschemapolicy": ["rule"],
+ "add authentication loginschema": ["userExpression", "passwdExpression"],
+ "add gslb vserver": ["rule"],
+ "add ns assignment": ["set", "append", "add", "sub"],
+ "add dns action64": ["mappedRule", "excludeRule"],
+ "add dns policy64": ["rule"],
+ "add authentication oauthidppolicy": ["rule"],
+ "add authentication samlidpprofile": ["NameIDExpr", "acsUrlRule"],
+ "add contentinspection policy": ["rule"],
+ "add ica policy": ["rule"],
+ "add lb group": ["rule"],
+ "add audit messageaction": [2],
+ "add aaa preauthenticationpolicy": [1],
+ "add spillover policy": ["rule"],
+ "add stream selector": [1, 2, 3, 4, 5],
+ "add tm formssoaction": ["ssoSuccessRule"],
+ "add tm samlssoprofile": ["relaystateRule", "NameIDExpr"],
+ "add vpn sessionpolicy": [1],
+ "add vpn trafficaction": ["userExpression", "passwdExpression"],
+ "add vpn vserver": ["Listenpolicy"],
+ # TODO: This entry need to be removed when Classic Syslog policy
+ # conversion is enabled in Syslog class.
+ "add audit syslogpolicy": [1],
+ # TODO: This entry need to be removed when Classic Nslog policy
+ # conversion is enabled in Nslog class.
+ "add audit nslogpolicy": [1],
+ # TODO: This entry need to be removed when Classic authorization policy
+ # conversion is enabled in Authorization class.
+ "add authorization policy": [1],
+ # TODO: This entry need to be removed when Classic VPNTraffic policy
+ # conversion is enabled in VPNTraffic class.
+ "add vpn trafficpolicy": [1],
+ }
+
+ command = " ".join(tree.get_command_type()).lower()
+ if command in command_parameters_list:
+ tree = AdvExpression.convert_adv_expr_list(tree, command_parameters_list[command])
+ return [tree]
diff --git a/nspepi/nspepi2/convert_cmp_cmd.py b/nspepi/nspepi2/convert_cmp_cmd.py
new file mode 100644
index 0000000..3aaac56
--- /dev/null
+++ b/nspepi/nspepi2/convert_cmp_cmd.py
@@ -0,0 +1,540 @@
+#!/usr/bin/env python2
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+import logging
+from collections import OrderedDict
+
+import nspepi_common as common
+import nspepi_parse_tree
+import convert_cli_commands as cli_cmds
+
+
+@common.register_class_methods
+class CMP(cli_cmds.ConvertConfig):
+ """
+ Converts classic CMP policies and
+ bind command.
+ CMP policies can be bound to cmp global,
+ LB, CS, CR vserver.
+ """
+
+ # override
+ flow_type_direction_default = "RESPONSE"
+ # Classic built-in policy names and there corresponding
+ # advanced built-in policy names.
+ built_in_policies = {
+ "ns_cmp_content_type": "ns_adv_cmp_content_type",
+ "ns_cmp_msapp": "ns_adv_cmp_msapp",
+ "ns_cmp_mscss": "ns_adv_cmp_mscss",
+ "ns_nocmp_mozilla_47": "ns_adv_nocmp_mozilla_47",
+ "ns_nocmp_xml_ie": "ns_adv_nocmp_xml_ie"
+ }
+
+ def __init__(self):
+ """
+ Information about CMP commands.
+ _cmp_bind_info - Contains CMP policies binding info.
+ key - bind point(possible values:
+ ""(which indicates the compression global
+ bind point) or ).
+ value - dictionary with the following keys
+ "bind_parse_trees",
+ "is_classic_policy_bound",
+ "is_advanced_policy_bound".
+ _initial_cmp_parameter- Initial CMP parameter policy type.
+ _classic_builtin_bind - Contains info about classic builtin policy
+ bindings.
+ key - bind command parse tree in which classic
+ built-in policy name is replaced with the
+ corresponding advanced built-in policy
+ name.
+ value - classic built-in policy name.
+ """
+ self._cmp_bind_info = OrderedDict()
+ self._initial_cmp_parameter = "advanced"
+ self._classic_builtin_bind = OrderedDict()
+
+ @common.register_for_init_call
+ def store_builtin_cmp_policies(self):
+ """
+ Creates and stores Policy object for built-in CMP policies.
+ """
+ self.store_builtin_policies()
+
+ @common.register_for_cmd("set", "cmp", "parameter")
+ def set_cmp_parameter(self, cmp_param_tree):
+ """
+ Remove policyType parameter from the command
+ Syntax:
+ set cmp parameter -policyType ADVANCED
+ """
+ if cmp_param_tree.keyword_exists("policyType"):
+ self._initial_cmp_parameter = \
+ cmp_param_tree.keyword_value("policyType")[0].value.lower()
+ cmp_param_tree.remove_keyword("policyType")
+ if cmp_param_tree.get_number_of_params() == 0:
+ return []
+ return [cmp_param_tree]
+
+ @common.register_for_cmd("set", "cmp", "policy")
+ def set_cmp_policy(self, cmp_policy_tree):
+ """
+ Classic CMP built-in policies cannot be changed using set command.
+ Advanced CMP built-in policies can be changed using set command and
+ set command is saved in ns.conf. Since we are replacing the classic
+ built-in policy name with corresponding advanced built-in policy name,
+ if advanced policy is changed with set command, then functionality
+ will change. So, if advanced built-in policy is changed then add new
+ advanced policy which will be equivalent to classic built-in policy.
+ cmp_policy_tree - set cmp policy command parse tree
+ """
+ advanced_builtin_policy = {
+ "ns_adv_cmp_content_type": {
+ "classic": "ns_cmp_content_type",
+ "rule": "HTTP.RES.HEADER(\"Content-Type\")" +
+ ".CONTAINS(\"text\")",
+ "resAction": "COMPRESS"
+ },
+ "ns_adv_cmp_msapp": {
+ "classic": "ns_cmp_msapp",
+ "rule": "ns_msie_adv && (HTTP.RES.HEADER" +
+ "(\"Content-Type\").CONTAINS(\"appl" +
+ "ication/msword\") || HTTP.RES.HEADE" +
+ "R(\"Content-Type\").CONTAINS(\"" +
+ "application/vnd.ms-excel\") || " +
+ "HTTP.RES.HEADER(\"Content-Type\")" +
+ ".CONTAINS(\"application/vnd.ms-" +
+ "powerpoint\"))",
+ "resAction": "COMPRESS"
+ },
+ "ns_adv_cmp_mscss": {
+ "classic": "ns_cmp_mscss",
+ "rule": "ns_msie_adv && HTTP.RES.HEADER" +
+ "(\"Content-Type\").CONTAINS" +
+ "(\"text/css\")",
+ "resAction": "COMPRESS"
+ },
+ "ns_adv_nocmp_mozilla_47": {
+ "classic": "ns_nocmp_mozilla_47",
+ "rule": "HTTP.REQ.HEADER(\"User-Agent\")." +
+ "CONTAINS(\"Mozilla/4.7\") && HTTP." +
+ "RES.HEADER(\"Content-Type\")." +
+ "CONTAINS(\"text/css\")",
+ "resAction": "NOCOMPRESS"
+ },
+ "ns_adv_nocmp_xml_ie": {
+ "classic": "ns_nocmp_xml_ie",
+ "rule": "ns_msie_adv && HTTP.RES.HEADER" +
+ "(\"Content-Type\").CONTAINS" +
+ "(\"text/xml\")",
+ "resAction": "NOCOMPRESS"
+ }
+ }
+ tree_list = [cmp_policy_tree]
+ adv_policy_name = cmp_policy_tree.positional_value(0).value
+ if adv_policy_name in advanced_builtin_policy:
+ # Tree construction
+ policy_name = "nspepi_adv_" + adv_policy_name
+ policy_tree = nspepi_parse_tree.CLICommand("add", "cmp", "policy")
+ pos = nspepi_parse_tree.CLIPositionalParameter(policy_name)
+ policy_tree.add_positional(pos)
+ rule_key = nspepi_parse_tree.CLIKeywordParameter(
+ nspepi_parse_tree.CLIKeywordName("rule"))
+ rule_key.add_value(
+ advanced_builtin_policy[adv_policy_name]["rule"])
+ policy_tree.add_keyword(rule_key)
+ action_key = nspepi_parse_tree.CLIKeywordParameter(
+ nspepi_parse_tree.CLIKeywordName("resAction"))
+ action_key.add_value(
+ advanced_builtin_policy[adv_policy_name]["resAction"])
+ policy_tree.add_keyword(action_key)
+ tree_list.append(policy_tree)
+ # Update policy name in built_in_policies dictionary.
+ self.built_in_policies[advanced_builtin_policy[adv_policy_name][
+ "classic"]] = policy_name
+ return tree_list
+
+ @common.register_for_cmd("add", "cmp", "policy")
+ def convert_cmp_policy(self, cmp_policy_tree):
+ """
+ Converts classic cmp policy to advanced.
+ Syntax:
+ add cmp policy -rule
+ -resAction
+ Converts to
+ add cmp policy -rule
+ -resAction
+ """
+ policy_name = cmp_policy_tree.positional_value(0).value
+ pol_obj = common.Policy(policy_name, self.__class__.__name__)
+ common.pols_binds.store_policy(pol_obj)
+ cli_cmds.ConvertConfig.convert_keyword_expr(cmp_policy_tree, 'rule')
+ pol_obj.policy_type = ("classic"
+ if cmp_policy_tree.upgraded else "advanced")
+ return [cmp_policy_tree]
+
+ @common.register_for_cmd("bind", "cmp", "global")
+ def convert_cmp_global_bind(self, bind_cmd_tree):
+ """
+ Handles CMP policy bindings to cmp global.
+ Syntax for classic policy binding:
+ bind cmp global [-priority ]
+ [-state (ENABLED/DISABLED)]
+ When classic CMP policy is bound:
+ 1. If -state is DISABLED, comment the bind command.
+ 2. Add -type RES_DEFAULT keyword.
+ 3. Throw error when functionality may change.
+ """
+ # Comment the bind command and throw warning when
+ # state is disabled.
+ if bind_cmd_tree.keyword_exists("state") and \
+ bind_cmd_tree.keyword_value("state")[0].value.lower() == \
+ "disabled":
+ logging.warning((
+ "Following bind command is commented out because"
+ " state is disabled. If command is required please take"
+ " a backup because comments will not be saved in ns.conf"
+ " after triggering 'save ns config': {}").
+ format(str(bind_cmd_tree).strip())
+ )
+ return ["#" + str(bind_cmd_tree)]
+
+ policy_name = bind_cmd_tree.positional_value(0).value
+ self.replace_builtin_policy(bind_cmd_tree, policy_name, 0)
+ bind_point = ""
+ self.update_bind_info(bind_cmd_tree, bind_point)
+ return []
+
+ @common.register_for_bind(["LB", "ContentSwitching", "CacheRedirection"])
+ def convert_cmp_policy_vserver_bind(
+ self, bind_cmd_tree, policy_name, priority_arg, goto_arg):
+ """
+ Handles CMP policy binding to LB vserver,
+ CS vserver, CR vserver.
+ Syntax for classic CMP policy binding:
+ bind lb/cr/cs vserver -policyName
+ [-priority ]
+ when classic cmp policy is bound:
+ 1. Add -type RESPONSE keyword.
+ 2. Throw error when functionality may change.
+ """
+ vserver_name = bind_cmd_tree.positional_value(0).value
+ self.replace_builtin_policy(bind_cmd_tree, policy_name, "policyName")
+ bind_point = vserver_name
+ self.update_bind_info(bind_cmd_tree, bind_point)
+ return []
+
+ def replace_builtin_policy(self, bind_cmd_tree, policy_name, policy_arg):
+ """
+ If bound policy is classic built-in policy, then replace policy name
+ with advanced built-in policy.
+ """
+ if policy_name in self.built_in_policies:
+ # Update policy name to advanced policy name.
+ self.update_tree_arg(bind_cmd_tree, policy_arg,
+ self.built_in_policies[policy_name])
+ self._classic_builtin_bind[bind_cmd_tree] = policy_name
+
+ def update_bind_info(self, bind_cmd_tree, bind_point):
+ """
+ Appends bind command parse tree to _cmp_bind_info
+ and updates the bind_info.
+ bind_cmd_tree - bind command parse tree.
+ bind_point - Policy bind point.
+ """
+ if bind_point not in self._cmp_bind_info:
+ self._cmp_bind_info[bind_point] = OrderedDict()
+ self._cmp_bind_info[bind_point]["bind_parse_trees"] = []
+ self._cmp_bind_info[bind_point]["is_classic_policy_bound"] = False
+ self._cmp_bind_info[bind_point]["is_advanced_policy_bound"] = False
+
+ # -type keyword exists only for advanced policy
+ # bindings.
+ if bind_cmd_tree.keyword_exists("type"):
+ self._cmp_bind_info[bind_point]["is_advanced_policy_bound"] = True
+ else:
+ self._cmp_bind_info[bind_point]["is_classic_policy_bound"] = True
+ self._cmp_bind_info[bind_point]["bind_parse_trees"]. \
+ append(bind_cmd_tree)
+
+ def check_functionality(self):
+ """
+ Handles if there are global bindings and
+ any vserver bindings.
+ Both classic and advanced policies can be bound
+ to CMP global. Choosing which set of policies to
+ evaluate depends on two factors.
+ 1. CMP global parameter
+ 2. policy type bound to vserver
+ First preference will be vserver, if vserver
+ exists and has classic policies bound to it,
+ then classic policy set is selected from
+ global and does not depend on global CMP parameter.
+ Same way if vserver has advanced policies, then
+ advanced policy set is selected from global.
+ If vserver does not exists, then depending on
+ global CMP parameter, policy set is choosen.
+ Functionality will change in the following
+ cases after conversion:
+ Global bindings and CMP parameter:
+ 1. When both classic and advanced policies
+ are bound to global.
+ 2. When classic policies are bound and
+ cmp parameter is advanced.
+ 3. When advanced policies are bound and
+ cmp parameter is classic.
+ Global and vserver bindings:
+ 4. When classic policies are bound to vserver and
+ advanced policies are bound to global.
+ 5. When advanced policies are bound to vserver and
+ classic policies are bound to global.
+ 6. When classic policies are bound to vserver and
+ both classic and advanced policies are bound to
+ global.
+ 7. When advanced policies are bound to vserver and
+ both classic and advanced policies are bound to
+ global.
+ Returns True if there is any conflict and functionality
+ will change, else returns False.
+ """
+ # When both classic and advanced policies are
+ # bound to cmp global. This covers case 1,6,7
+ # that are mentioned above.
+ global_bind_point = ""
+ if self._cmp_bind_info[global_bind_point][
+ "is_classic_policy_bound"] and self. \
+ _cmp_bind_info[global_bind_point]["is_advanced_policy_bound"]:
+ logging.error(
+ "Both classic and advanced policies "
+ "are bound to CMP global. Now classic policies are "
+ "converted to advanced. This will change the "
+ "functionality. CMP policy bindings are commented out. "
+ "Modify the bindings of CMP policies manually."
+ )
+ return True
+
+ conflict_exists = False
+ # When Global parameter and policies bound
+ # at global level does not match.
+ # This covers case 2 and 3
+ policy_bound = ''
+ if self._cmp_bind_info[global_bind_point]["is_classic_policy_bound"]:
+ policy_bound = "classic"
+ else:
+ policy_bound = "advanced"
+ if not policy_bound == self._initial_cmp_parameter:
+ logging.error(
+ "There is a mismatch between global "
+ "parameter and policy type that are bound. "
+ "Now classic policies are converted to advanced "
+ "and cmp global parameter policy type is set to "
+ "advanced. This will change the functionality. "
+ "CMP policy bindings are commented out. Modify "
+ "the global bindings of CMP policies manually."
+ )
+ conflict_exists = True
+
+ # Both global and vserver bindings.
+ for bind_point in self._cmp_bind_info:
+ # case 4 and 5.
+ if bind_point == global_bind_point:
+ continue
+ if self._cmp_bind_info[global_bind_point][
+ "is_classic_policy_bound"] and self. \
+ _cmp_bind_info[bind_point]["is_advanced_policy_bound"]:
+ logging.error((
+ "Classic policies are bound to cmp global "
+ "and advanced policies are bound to vserver {}."
+ " Now classic policies are converted to advanced. "
+ "This will change the functionality. CMP policy bindings "
+ "are commented out. Modify the bindings of CMP policies "
+ "manually.").format(bind_point)
+ )
+ conflict_exists = True
+ elif self._cmp_bind_info[global_bind_point][
+ "is_advanced_policy_bound"] and \
+ self._cmp_bind_info[bind_point]["is_classic_policy_bound"]:
+ logging.error((
+ "Advanced policies are bound to "
+ "cmp global and classic policies are bound "
+ "to vserver {}. Now classic policies are "
+ "converted to advanced. This will change the "
+ "functionality. CMP policy bindings are commented out. "
+ "Modify the bindings of CMP policies "
+ "manually.").format(bind_point)
+ )
+ conflict_exists = True
+ return conflict_exists
+
+ def global_binding_exists(self):
+ """
+ Returns True if there is any CMP policy
+ bound to cmp global.
+ """
+ return "" in self._cmp_bind_info
+
+ def vserver_binding_exists(self):
+ """
+ Returns True if there is any CMP policy
+ bound to any vserver.
+ """
+ # CMP policy can be bound to global/
+ # CS/CR/LB vservers.
+ for bind_point in self._cmp_bind_info:
+ if not bind_point == "":
+ return True
+ return False
+
+ def resolve_cmp_param_global_binding(self):
+ """
+ Comment the policy global bindings that do not
+ match the cmp parameter and throw warning.
+ Returns commented out bind command list.
+ """
+ commented_bind_cmd_list = []
+ global_bind_point = ""
+ if self._initial_cmp_parameter == "classic" and \
+ self._cmp_bind_info[global_bind_point][
+ "is_advanced_policy_bound"]:
+ # Comment the advanced policies that are bound.
+ logging.warning(
+ "Bindings of advanced CMP policies to cmp global "
+ "are commented out, because initial global cmp parameter "
+ "is classic but advanced policies are bound. Now global "
+ "cmp parameter policy type is set to advanced. If commands "
+ "are required please take a backup because comments will "
+ "not be saved in ns.conf after triggering 'save ns config'."
+ )
+ # Iterate in reverse order, since we will be removing
+ # elements from list.
+ for tree in \
+ reversed(self._cmp_bind_info[global_bind_point][
+ "bind_parse_trees"]):
+ if tree.keyword_exists("type"):
+ bind_cmd = '#' + str(tree)
+ # Insert at 0, to preserve the order.
+ commented_bind_cmd_list.insert(0, bind_cmd)
+ self._cmp_bind_info[global_bind_point][
+ "bind_parse_trees"].remove(tree)
+ elif self._initial_cmp_parameter == "advanced" and \
+ self._cmp_bind_info[global_bind_point][
+ "is_classic_policy_bound"]:
+ # Comment the classic policies that are bound.
+ logging.warning(
+ "Bindings of classic CMP policies to cmp global "
+ "are commented out, because initial global cmp parameter "
+ "is advanced but classic policies are bound. Now all "
+ "classic CMP policies are converted to advanced. If "
+ "commands are required please take a backup because comments "
+ "will not be saved in ns.conf after triggering "
+ "'save ns config'."
+ )
+ # Iterate in reverse order, since we will be removing
+ # elements from list.
+ for tree in \
+ reversed(self._cmp_bind_info[global_bind_point][
+ "bind_parse_trees"]):
+ if not tree.keyword_exists("type"):
+ bind_cmd = '#' + str(tree)
+ # Insert at 0, to preserve the order.
+ commented_bind_cmd_list.insert(0, bind_cmd)
+ self._cmp_bind_info[global_bind_point][
+ "bind_parse_trees"].remove(tree)
+ return commented_bind_cmd_list
+
+ def is_same_policy_type(self):
+ """
+ Returns True if all vservers have same
+ type of policies and matches with the
+ cmp parameter.
+ """
+ global_bind_point = ""
+ if self._initial_cmp_parameter == "classic":
+ key = "is_advanced_policy_bound"
+ else:
+ key = "is_classic_policy_bound"
+ for bind_point in self._cmp_bind_info:
+ # skip for global bind point.
+ if bind_point == "":
+ continue
+ if self._cmp_bind_info[bind_point][key]:
+ return False
+ return True
+
+ @common.register_for_final_call
+ def get_cmp_policy_bindings(self):
+ """
+ Checks if the functionality will change
+ after conversion. If functionality will change,
+ returns all bind command parse trees(CMP
+ policy bindings) saved in _cmp_bind_info.
+ If functionality will not change, calls the
+ Binding infra for the priority analysis.
+ This should be called only at the end of
+ processing of entire ns.conf file.
+ Return value - list of parse trees.
+ """
+ tree_list = []
+ conflict_exists = False
+
+ policy_type = self.__class__.__name__
+ priority_arg = "priority"
+ goto_arg = "gotoPriorityExpression"
+
+ if self.global_binding_exists() and not self.vserver_binding_exists():
+ # Only global bindings.
+ # Comment policy bindings which do not match
+ # with cmp parameter. This will resolve the
+ # issue and there won't be any functionality
+ # change.
+ tree_list += self.resolve_cmp_param_global_binding()
+ elif self.global_binding_exists() and self.vserver_binding_exists():
+ # Both vserver and global bindings.
+ if self.is_same_policy_type():
+ # If all the vservers uses the same policy
+ # type and matches with the cmp parameter,
+ # comment the global bindings that do not match
+ # that policy type.
+ tree_list += self.resolve_cmp_param_global_binding()
+ else:
+ # If policy type is not same then
+ # check for the funtionality change.
+ conflict_exists = self.check_functionality()
+
+ # If functionality will change, return bind commands
+ # without modifying priority and goto of bind commands.
+ if conflict_exists:
+ for bind_point in self._cmp_bind_info:
+ for tree in \
+ self._cmp_bind_info[bind_point]["bind_parse_trees"]:
+ tree_list.append('#' + str(tree))
+ else:
+ # If there is no policy type conflict, use Bind
+ # analysis infra for setting priority and goto.
+ module = self.__class__.__name__
+ for bind_point in self._cmp_bind_info:
+ if bind_point == "":
+ for tree in \
+ self._cmp_bind_info[bind_point]["bind_parse_trees"]:
+ if tree in self._classic_builtin_bind:
+ policy_name = self._classic_builtin_bind[tree]
+ else:
+ policy_name = tree.positional_value(0).value
+ tree_list += self.convert_global_bind(
+ tree, tree, policy_name, module,
+ priority_arg, goto_arg)
+ else:
+ for tree in \
+ self._cmp_bind_info[bind_point]["bind_parse_trees"]:
+ if tree in self._classic_builtin_bind:
+ policy_name = self._classic_builtin_bind[tree]
+ else:
+ policy_name = tree.keyword_value(
+ "policyName")[0].value
+ tree_list += self.convert_entity_policy_bind(
+ tree, tree, policy_name,
+ policy_type, priority_arg, goto_arg)
+ return tree_list
diff --git a/nspepi/nspepi2/convert_filter_command.py b/nspepi/nspepi2/convert_filter_command.py
new file mode 100644
index 0000000..1bbd531
--- /dev/null
+++ b/nspepi/nspepi2/convert_filter_command.py
@@ -0,0 +1,955 @@
+#!/usr/bin/env python2
+
+# Copyright 2021 Citrix Systems, Inc. All rights reserved.
+# Use of this software is governed by the license terms, if any,
+# which accompany or are included with this software.
+
+'''
+Note:
+ 1. If action has value as prebody or postbody then there will not be any conversion
+ 2. If policy bind command has parameter "state disabled" then converted bind command will be commented out
+ 3. Filter feature of actionType FORWARD is not supported currently
+ 4. bind command of filter policies gets complicated if ns.conf already contains vserver of ptotocol type HTTP/S and rewrite/responder policy bindings:
+ 1. If goto is END/USE_INVOCATION_RESULT exists in existing rewrite local bindings and|not global bindings
+ 2. If goto is END/USE_INVOCATION_RESULT exists in existing rewrite global bindings and|not local bindings
+ Then comment out all partially converted rewrite global and local bindings otherwise do proper convertion.
+ Same applies for responder conditions.
+
+---INPUT WITH ADD ACTIONTYPE---
+add filter action act1 ADD "H1:Value"
+add filter action act2 ADD "H1:::::Value"
+add filter action act3 ADD H1::::::Value
+add filter action act4 ADD H1:
+add filter action act5 ADD H1::
+add filter action act6 ADD "H1:::"
+add filter action act7 ADD "abcd:\"d1\""
+add filter action act8 ADD "abcd:::::::\"d1\""
+add filter action act10 ADD "@5%^&:Vl54"
+add filter action act11 ADD q/\t:123/
+add filter action act12 ADD q/Name:\t/
+add filter action act13 ADD q/Name:\\t/
+add filter action act14 ADD prebody:123
+add filter action act15 ADD postbody:1234
+add filter action act16 ADD "\"H1:grv\""
+add filter action act17 ADD "/H1/:/d/1"
+add filter action act18 ADD "/H1/:d/1"
+add filter action act20 ADD "\H1:d1"
+add filter action act21 add "/\H1:d1"
+add filter action act22 ADD "\\\\H1:\\\d\\\\1\\\\2\\\\"
+add filter action act23 add "H1:\\n"
+add filter action act24 add "H\\n1:\\n"
+add filter action act25 add "H \n1:\\n"
+add filter action act26 add "H1:Value1:Value2"
+add filter action act27 add "H1:%%HTTP.TRANSID%%"
+add filter action act28 add prebody
+add filter action act29 add postbody
+add filter policy add_pol1_1 -rule ns_true -resAction act1
+add filter policy add_pol1_2 -rule ns_true -reqAction act1
+add filter policy add_pol2_1 -rule ns_true -resAction act27
+add filter policy add_pol2_2 -rule ns_true -reqAction act27
+add filter policy add_pol3_1 -rule ns_true -reqAction act28
+add filter policy add_pol3_2 -rule ns_true -resAction act28
+add filter policy add_pol4_1 -rule ns_true -reqAction act29
+add filter policy add_pol4_2 -rule ns_true -resAction act29
+bind filter global add_pol1_1 [-priority ])
+bind filter global add_pol1_2 [-priority ])
+bind lb vserver -policyName add_pol1_1 [-priority ])
+bind lb vserver -policyName add_pol1_2 [-priority ])
+bind cs vserver -policyName add_pol1_1 [-priority ])
+bind cs vserver -policyName add_pol1_2 [-priority ])
+bind cr vserver -policyName add_pol1_1 [-priority ])
+bind cr vserver -policyName add_pol1_2 [-priority ])
+---CONVERTED RESULT---
+add rewrite action act1 insert_http_header f_H1 "\"Value\""
+add rewrite action act2 insert_http_header H1 "\"::::Value\""
+add rewrite action act3 insert_http_header H1 "\":::::Value\""
+add rewrite action act4 insert_http_header H1 "\"\""
+add rewrite action act5 insert_http_header H1 "\":\""
+add rewrite action act6 insert_http_header H1 "\"::\""
+add rewrite action act7 insert_http_header abcd "\"\\\"d1\\\"\""
+add rewrite action act8 insert_http_header abcd "\"::::::\\\"d1\\\"\""
+add rewrite action act10 insert_http_header @5%^& "\"Vl54\""
+add rewrite action act11 insert_http_header "\\t" "\"123\""
+add rewrite action act12 insert_http_header Name "\"\\\\t\""
+add rewrite action act13 insert_http_header Name "\"\\\\\\\\t\""
+add rewrite action act14 insert_http_header prebody "\"123\""
+add rewrite action act15 insert_http_header postbody "\"1234\""
+add rewrite action act16 insert_http_header "\"H1" "\"grv\\\"\""
+add rewrite action act17 insert_http_header /H1/ "\"/d/1\""
+add rewrite action act18 insert_http_header /H1/ "\"d/1\""
+add rewrite action act20 insert_http_header "\\H1" "\"d1\""
+add rewrite action act21 insert_http_header "/\\H1" "\"d1\""
+add rewrite action act22 insert_http_header "\\\\H1"
+ "\"\\\\\\\\d\\\\\\\\1\\\\\\\\2\\\\\\\\\""
+add rewrite action act23 insert_http_header H1 "\"\\\\n\""
+add rewrite action act24 insert_http_header "H\\n1" "\"\\\\n\""
+add rewrite action act25 insert_http_header "H \n1" "\"\\\\n\""
+add rewrite action act26 insert_http_header H1 "\"Value1:Value2\""
+add rewrite action act27 insert_http_header H1 HTTP.REQ.TXID
+add rewrite action nspepi_adv_act27 insert_http_header H1 HTTP.RES.TXID
+add filter action act28 add prebody
+add filter action act29 add postbody
+add rewrite policy add_pol1_1 TRUE act1
+add rewrite policy add_pol1_2 TRUE act1
+add rewrite policy add_pol2_1 TRUE nspepi_adv_act27
+add rewrite policy add_pol2_2 TRUE act27
+add filter policy add_pol3_1 -rule ns_true -reqAction act28
+add filter policy add_pol3_2 -rule ns_true -resAction act28
+add filter policy add_pol4_1 -rule ns_true -reqAction act29
+add filter policy add_pol4_2 -rule ns_true -resAction act29
+bind rewrite global add_pol1_1 NEXT -TYPE RES_DEFAULT
+bind rewrite global add_pol1_2 NEXT -TYPE REQ_DEFAULT
+bind lb vserver -policyName add_pol1_1 [-priority ]) -gotoPriorityExpression NEXT -TYPE RESPONSE
+bind lb vserver -policyName add_pol1_2 [-priority ]) -gotoPriorityExpression NEXT -TYPE REQUEST
+bind cs vserver -policyName add_pol1_1 [-priority ]) -gotoPriorityExpression NEXT -TYPE RESPONSE
+bind cs vserver -policyName add_pol1_2 [-priority ]) -gotoPriorityExpression NEXT -TYPE REQUEST
+bind cr vserver -policyName add_pol1_1 [-priority ]) -gotoPriorityExpression NEXT -TYPE RESPONSE
+bind cr vserver