-
Notifications
You must be signed in to change notification settings - Fork 4
/
ixvision_ztp_filter.py
194 lines (169 loc) · 9.26 KB
/
ixvision_ztp_filter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
###############################################################################
#
# Zero-Touch Automation utility for Ixia Vision Network Packet Brokers
#
# File: ixvision_ztp_filter.py
# Author: Alex Bortok (https://github.com/bortok)
#
# Description: A module to create/update a dynamic filter between network and tool port groups
# 1. Starting point is an Ixia Vision NPB with network and tool port groups formed
# 2. Each run of this script would configure a single dynamic filter using supplied name and filter criteria
# - Search for an existing DF with the same name. If found with the matching type, continue by referencing that DF
# - If not found, create a new DF
# 3. Update the DF criteria with provided rules
# 4. Search for network port group with a specified name and, if found, connect it to the input of the DF
# 5. Search for tool port group with a specified name and, if found, connect it to the output of the DF
#
# COPYRIGHT 2018 - 2019 Keysight Technologies.
#
# This code is provided under the MIT license.
# You can find the complete terms in LICENSE.txt
#
###############################################################################
from ksvisionlib import *
from ixvision_ztp_ntolib import *
# DEFINE VARs HERE
# NOTE - Priority-based filtering mode is not supported, but we don't check if the system is in such mode
df_modes_supported = {'all': 'PASS_ALL', 'none': 'DISABLE', 'pbc': 'PASS_BY_CRITERIA', 'dbc': 'DENY_BY_CRITERIA', 'pbcu': 'PBC_UNMATCHED', 'dbcm': 'DBC_MATCHED'}
# DEFINE FUNCTIONS HERE
# Check if DF mode used requires a criteria
def df_criteria_required(df_mode):
return df_mode in df_modes_supported and (df_mode == 'pbc' or df_mode == 'dbc')
## Search for port groups and return a list if IDs
def search_port_group_id_list(nto, params):
pg_id_list = []
pg_list = nto.searchPortGroups(params)
for pg in pg_list:
pg_id_list.append(pg['id'])
return pg_id_list
## Remove empty port groups from a list if port group IDs
def remove_empty_port_groups_from_id_list(nto, pg_id_list):
non_empty_pg_id_list = []
for pg_id in pg_id_list:
pg_port_list = nto.getPortGroupProperty(str(pg_id), 'port_list')
if isinstance(pg_port_list, list) and len(pg_port_list) > 0:
non_empty_pg_id_list.append(pg_id)
return non_empty_pg_id_list
## Filter create/update
# Input
# - Connection to an NPB
# - Dynamic filter name
# - Network port group name
# - Tool port group name
# - DF mode - use keys from df_modes_supported global dict
def form_dynamic_filter(host_ip, port, username, password, df_name, df_input, df_output, df_mode, df_criteria = {}, use_tag_mode = False):
df_params = {}
df_mode_value = 'DISABLE' # Default DF mode value for a new filter to use, if not overridden
if isinstance(df_modes_supported, dict) and df_mode in df_modes_supported.keys():
df_mode_value = df_modes_supported[df_mode]
if df_criteria_required(df_mode) and (isinstance(df_criteria, dict) and len(df_criteria) == 0 or not isinstance(df_criteria, dict)):
print("Non-empty criteria are required for filter mode %s" % (df_mode))
return
nto = VisionWebApi(host=host_ip, username=username, password=password, port=port, debug=True, logFile="ixvision_ztp_filter_debug.log")
# Search for existing DF, create a new one if not found
df_list = nto.searchFilters({'name': df_name})
ztp_df = None
ztp_df_source_port_group_id_list = []
ztp_df_dest_port_group_id_list = []
if len(df_list) == 0:
# No existing filter with such name, will create a new one
df_params.update({'name': df_name, 'keywords': ['ZTP'], 'mode': df_mode_value})
if isinstance(df_criteria, dict) and len(df_criteria) > 0:
df_params.update({'criteria': df_criteria})
new_df = nto.createFilter(df_params, True) # the last parameter is for allowTemporayDataLoss
if new_df is not None and len(new_df) > 0:
print("No existing DF found, created a new one with id %s" % (str(new_df['id'])))
ztp_df = new_df
else:
print("No existing DF found, failed to created a new one!")
return
elif len(df_list) == 1:
# An existing DF found
df = df_list[0]
df_details = nto.getFilter(str(df['id'])) # TODO handle 404 not found situation
ztp_df = df
ztp_df_source_port_group_id_list = df_details['source_port_group_list']
ztp_df_dest_port_group_id_list = df_details['dest_port_group_list']
print("Found an existing DF %s in %s mode" % (df_details['default_name'], df_details['mode']))
df_needs_updating = False
# TODO update keywords with ZTP
if df_mode_value != df_details['mode']:
print("Updating DF %s to a new mode %s" % (df_details['default_name'], df_mode_value))
df_params.update({'mode': df_mode_value})
df_needs_updating = True
if isinstance(df_criteria, dict) and len(df_criteria) > 0:
df_criteria.update(df_details['criteria'])
if df_criteria != df_details['criteria']:
print("Updating DF %s criteria" % (df_details['default_name']))
df_params.update({'criteria': df_criteria})
df_needs_updating = True
if df_needs_updating:
nto.modifyFilter(str(df['id']), df_params)
# TODO handle errors
else:
# This should never happen, but just in case, provide details to look into
print("Found more than one DF named %s, can't continue:" % (df_name)),
for df in df_list:
df_details = nto.getFilter(str(df['id']))
if df_details is not None:
print (" %s," % (df_details['default_name'])),
print("")
return
# TODO update DF criteria
if not use_tag_mode:
# Search for network and tool port groups matching given names.
# Make sure they are not empty before connecting to filters, since ports can't be added later to an empty but connected port group
ztp_df_source_port_group_id_list.extend(remove_empty_port_groups_from_id_list(nto, search_port_group_id_list(nto, {'name': df_input})))
ztp_df_dest_port_group_id_list.extend (remove_empty_port_groups_from_id_list(nto, search_port_group_id_list(nto, {'name': df_output})))
# TODO update DF connections only if there is an actual change in list of PGs connected to it
df_params.update({'source_port_group_list': ztp_df_source_port_group_id_list, 'dest_port_group_list': ztp_df_dest_port_group_id_list})
nto.modifyFilter(str(ztp_df['id']), df_params)
else:
# Connect input ports using tags
df_connect_via_tags(nto, str(ztp_df['id']), [df_input], 'input')
# Connect output ports using tags
df_connect_via_tags(nto, str(ztp_df['id']), [df_output], 'output')
def update_dynamic_filter(host_ip, port, username, password, df_name, df_criteria_field, df_append_values, df_remove_values):
df_criterion = None
if isinstance(df_criteria_fields_supported, dict) and df_criteria_field in df_criteria_fields_supported.keys():
df_criterion = df_criteria_fields_supported[df_criteria_field]
else:
print("Error: unsupported filter criteria %s" % df_criteria_field)
return
nto = VisionWebApi(host=host_ip, username=username, password=password, port=port, debug=True, logFile="ixvision_ztp_filter_debug.log")
# Search for the DF
df_list = nto.searchFilters({'name': df_name})
if len(df_list) == 0:
# No existing filter with such name
print("Error: can't find a dynamic filter with name %s" % df_name)
return
elif len(df_list) == 1:
# An existing DF found
df = df_list[0]
df_criteria = nto.getFilterProperty(str(df['id']), 'criteria') # TODO handle 404 not found situation
if df_criterion not in df_criteria.keys():
print("Criteria field %s is not in use by filter %s" % (df_criterion, df_name))
return
for key in df_criteria[df_criterion]:
if key in df_append_values.keys():
if isinstance(df_criteria[df_criterion][key], list):
df_criteria[df_criterion][key].extend(df_append_values[key])
else:
df_criteria[df_criterion][key] = df_append_values[key]
if key in df_remove_values.keys():
if isinstance(df_criteria[df_criterion][key], list):
for value in df_remove_values[key]:
if value in df_criteria[df_criterion][key]:
df_criteria[df_criterion][key].remove(value)
nto.modifyFilter(str(df['id']), {'criteria': df_criteria})
# TODO validate the update was successful
print("Updated filter %s with new values for %s criteria field" % (df_name, df_criterion))
else:
# This should never happen, but just in case, provide details to look into
print("Found more than one DF named %s, can't continue:" % (df_name)),
for df in df_list:
df_details = nto.getFilter(str(df['id']))
if df_details is not None:
print (" %s," % (df_details['default_name'])),
print("")
return