CPlatform.py revision 501fb3b4
1#!/router/bin/python
2
3from interfaces_e import IFType
4from platform_cmd_link import *
5import CustomLogger
6import misc_methods
7import re
8import time
9import CProgressDisp
10from CShowParser import CShowParser
11
12class CPlatform(object):
13    def __init__(self, silent_mode):
14        self.if_mngr            = CIfManager()
15        self.cmd_link           = CCommandLink(silent_mode)
16        self.nat_config         = None
17        self.stat_route_config  = None
18        self.running_image      = None
19        self.needed_image_path  = None
20        self.tftp_cfg           = None
21        self.config_history     = { 'basic_if_config' : False, 'tftp_server_config' : False }
22
23    def configure_basic_interfaces(self, mtu = 4000):
24
25        cache = CCommandCache()
26        for dual_if in self.if_mngr.get_dual_if_list():
27            client_if_command_set   = []
28            server_if_command_set   = []
29
30            client_if_command_set.append ('mac-address {mac}'.format( mac = dual_if.client_if.get_src_mac_addr()) )
31            client_if_command_set.append ('mtu %s' % mtu)
32            client_if_command_set.append ('ip address {ip} 255.255.255.0'.format( ip = dual_if.client_if.get_ipv4_addr() ))
33            client_if_command_set.append ('ipv6 address {ip}/64'.format( ip = dual_if.client_if.get_ipv6_addr() ))
34
35            cache.add('IF', client_if_command_set, dual_if.client_if.get_name())
36
37            server_if_command_set.append ('mac-address {mac}'.format( mac = dual_if.server_if.get_src_mac_addr()) )
38            server_if_command_set.append ('mtu %s' % mtu)
39            server_if_command_set.append ('ip address {ip} 255.255.255.0'.format( ip = dual_if.server_if.get_ipv4_addr() ))
40            server_if_command_set.append ('ipv6 address {ip}/64'.format( ip = dual_if.server_if.get_ipv6_addr() ))
41
42            cache.add('IF', server_if_command_set, dual_if.server_if.get_name())
43
44        self.cmd_link.run_single_command(cache)
45        self.config_history['basic_if_config'] = True
46
47
48
49    def configure_basic_filtered_interfaces(self, intf_list, mtu = 4000):
50
51        cache = CCommandCache()
52        for intf in intf_list:
53            if_command_set   = []
54
55            if_command_set.append ('mac-address {mac}'.format( mac = intf.get_src_mac_addr()) )
56            if_command_set.append ('mtu %s' % mtu)
57            if_command_set.append ('ip address {ip} 255.255.255.0'.format( ip = intf.get_ipv4_addr() ))
58            if_command_set.append ('ipv6 address {ip}/64'.format( ip = intf.get_ipv6_addr() ))
59
60            cache.add('IF', if_command_set, intf.get_name())
61
62        self.cmd_link.run_single_command(cache)
63
64
65    def load_clean_config (self, config_filename = "clean_config.cfg", cfg_drive = "bootflash"):
66        self.clear_nat_translations()
67
68        cache = CCommandCache()
69        cache.add('EXEC', "configure replace {drive}:{file} force".format(drive = cfg_drive, file = config_filename))
70        res = self.cmd_link.run_single_command(cache)
71        if 'Rollback Done' not in res:
72            raise UserWarning('Could not load clean config, please verify file exists. Response:\n%s' % res)
73
74    def config_pbr (self, mode = 'config'):
75        idx = 1
76        unconfig_str = '' if mode=='config' else 'no '
77
78        cache = CCommandCache()
79        pre_commit_cache = CCommandCache()
80        pre_commit_set = set([])
81
82        for dual_if in self.if_mngr.get_dual_if_list():
83            client_if_command_set   = []
84            server_if_command_set   = []
85            conf_t_command_set      = []
86            client_net_next_hop = misc_methods.get_single_net_client_addr(dual_if.server_if.get_ipv4_addr() )
87            server_net_next_hop = misc_methods.get_single_net_client_addr(dual_if.client_if.get_ipv4_addr() )
88
89            if dual_if.is_duplicated():
90                # define the relevant VRF name
91                pre_commit_set.add('{mode}ip vrf {dup}'.format( mode = unconfig_str, dup = dual_if.get_vrf_name()) )
92
93                # assign VRF to interfaces, config interfaces with relevant route-map
94                client_if_command_set.append ('{mode}ip vrf forwarding {dup}'.format( mode = unconfig_str, dup = dual_if.get_vrf_name()) )
95                client_if_command_set.append ('{mode}ip policy route-map {dup}_{p1}_to_{p2}'.format(
96                    mode = unconfig_str,
97                    dup = dual_if.get_vrf_name(),
98                    p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
99                server_if_command_set.append ('{mode}ip vrf forwarding {dup}'.format( mode = unconfig_str, dup = dual_if.get_vrf_name()) )
100                server_if_command_set.append ('{mode}ip policy route-map {dup}_{p2}_to_{p1}'.format(
101                    mode = unconfig_str,
102                    dup = dual_if.get_vrf_name(),
103                    p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
104
105                # config route-map routing
106                conf_t_command_set.append('{mode}route-map {dup}_{p1}_to_{p2} permit 10'.format(
107                    mode = unconfig_str,
108                    dup = dual_if.get_vrf_name(),
109                    p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
110                if mode == 'config':
111                    conf_t_command_set.append('set ip next-hop {next_hop}'.format(
112                         next_hop = client_net_next_hop) )
113                conf_t_command_set.append('{mode}route-map {dup}_{p2}_to_{p1} permit 10'.format(
114                    mode = unconfig_str,
115                    dup = dual_if.get_vrf_name(),
116                    p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
117                if mode == 'config':
118                    conf_t_command_set.append('set ip next-hop {next_hop}'.format(
119                         next_hop = server_net_next_hop) )
120
121                # config global arp to interfaces net address and vrf
122                conf_t_command_set.append('{mode}arp vrf {dup} {next_hop} {dest_mac} arpa'.format(
123                    mode = unconfig_str,
124                    dup = dual_if.get_vrf_name(),
125                    next_hop = server_net_next_hop,
126                    dest_mac = dual_if.client_if.get_dest_mac()))
127                conf_t_command_set.append('{mode}arp vrf {dup} {next_hop} {dest_mac} arpa'.format(
128                    mode = unconfig_str,
129                    dup = dual_if.get_vrf_name(),
130                    next_hop = client_net_next_hop,
131                    dest_mac = dual_if.server_if.get_dest_mac()))
132            else:
133                # config interfaces with relevant route-map
134                client_if_command_set.append ('{mode}ip policy route-map {p1}_to_{p2}'.format(
135                    mode = unconfig_str,
136                    p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
137                server_if_command_set.append ('{mode}ip policy route-map {p2}_to_{p1}'.format(
138                    mode = unconfig_str,
139                    p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
140
141                # config route-map routing
142                conf_t_command_set.append('{mode}route-map {p1}_to_{p2} permit 10'.format(
143                    mode = unconfig_str,
144                    p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
145                if mode == 'config':
146                    conf_t_command_set.append('set ip next-hop {next_hop}'.format(
147                         next_hop = client_net_next_hop) )
148                conf_t_command_set.append('{mode}route-map {p2}_to_{p1} permit 10'.format(
149                    mode = unconfig_str,
150                    p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
151                if mode == 'config':
152                    conf_t_command_set.append('set ip next-hop {next_hop}'.format(
153                         next_hop = server_net_next_hop) )
154
155                # config global arp to interfaces net address
156                conf_t_command_set.append('{mode}arp {next_hop} {dest_mac} arpa'.format(
157                    mode = unconfig_str,
158                    next_hop = server_net_next_hop,
159                    dest_mac = dual_if.client_if.get_dest_mac()))
160                conf_t_command_set.append('{mode}arp {next_hop} {dest_mac} arpa'.format(
161                    mode = unconfig_str,
162                    next_hop = client_net_next_hop,
163                    dest_mac = dual_if.server_if.get_dest_mac()))
164
165            # assign generated config list to cache
166            cache.add('IF', server_if_command_set, dual_if.server_if.get_name())
167            cache.add('IF', client_if_command_set, dual_if.client_if.get_name())
168            cache.add('CONF', conf_t_command_set)
169            idx += 2
170
171        # finish handling pre-config cache
172        pre_commit_set = list(pre_commit_set)
173#       pre_commit_set.append('exit')
174        pre_commit_cache.add('CONF', pre_commit_set )
175        # deploy the configs (order is important!)
176        self.cmd_link.run_command( [pre_commit_cache, cache] )
177        if self.config_history['basic_if_config']:
178            # in this case, duplicated interfaces will lose its ip address.
179            # re-config IPv4 addresses
180            self.configure_basic_filtered_interfaces(self.if_mngr.get_duplicated_if() )
181
182    def config_no_pbr (self):
183        self.config_pbr(mode = 'unconfig')
184
185    def config_static_routing (self, stat_route_obj, mode = 'config'):
186
187        if mode == 'config':
188            self.stat_route_config = stat_route_obj   # save the latest static route config for future removal purposes
189
190        unconfig_str = '' if mode=='config' else 'no '
191        cache              = CCommandCache()
192        pre_commit_cache   = CCommandCache()
193        pre_commit_set     = set([])
194        current_dup_intf   = None
195        # client_net       = None
196        # server_net       = None
197        client_net         = stat_route_obj.client_net_start
198        server_net         = stat_route_obj.server_net_start
199        conf_t_command_set = []
200
201        for dual_if in self.if_mngr.get_dual_if_list():
202
203            # handle duplicated addressing generation
204            if dual_if.is_duplicated():
205                if dual_if.get_vrf_name() != current_dup_intf:
206                    # if this is a dual interfaces, and it is different from the one we proccessed so far, reset static route addressing
207                    current_dup_intf = dual_if.get_vrf_name()
208                    client_net       = stat_route_obj.client_net_start
209                    server_net       = stat_route_obj.server_net_start
210            else:
211                if current_dup_intf is not None:
212                    current_dup_intf = None
213                    client_net       = stat_route_obj.client_net_start
214                    server_net       = stat_route_obj.server_net_start
215
216            client_net_next_hop = misc_methods.get_single_net_client_addr(dual_if.server_if.get_ipv4_addr() )
217            server_net_next_hop = misc_methods.get_single_net_client_addr(dual_if.client_if.get_ipv4_addr() )
218
219            # handle static route configuration for the interfaces
220            if dual_if.is_duplicated():
221                client_if_command_set   = []
222                server_if_command_set   = []
223
224                # define the relevant VRF name
225                pre_commit_set.add('{mode}ip vrf {dup}'.format( mode = unconfig_str, dup = dual_if.get_vrf_name()) )
226
227                # assign VRF to interfaces, config interfaces with relevant route-map
228                client_if_command_set.append ('{mode}ip vrf forwarding {dup}'.format( mode = unconfig_str, dup = dual_if.get_vrf_name()) )
229                server_if_command_set.append ('{mode}ip vrf forwarding {dup}'.format( mode = unconfig_str, dup = dual_if.get_vrf_name()) )
230
231                conf_t_command_set.append( "{mode}ip route vrf {dup} {next_net} {dest_mask} {next_hop}".format(
232                    mode = unconfig_str,
233                    dup = dual_if.get_vrf_name(),
234                    next_net = client_net,
235                    dest_mask = stat_route_obj.client_mask,
236                    next_hop = client_net_next_hop))
237                conf_t_command_set.append( "{mode}ip route vrf {dup} {next_net} {dest_mask} {next_hop}".format(
238                    mode = unconfig_str,
239                    dup = dual_if.get_vrf_name(),
240                    next_net = server_net,
241                    dest_mask = stat_route_obj.server_mask,
242                    next_hop = server_net_next_hop))
243
244                # config global arp to interfaces net address and vrf
245                conf_t_command_set.append('{mode}arp vrf {dup} {next_hop} {dest_mac} arpa'.format(
246                    mode = unconfig_str,
247                    dup = dual_if.get_vrf_name(),
248                    next_hop = server_net_next_hop,
249                    dest_mac = dual_if.client_if.get_dest_mac()))
250                conf_t_command_set.append('{mode}arp vrf {dup} {next_hop} {dest_mac} arpa'.format(
251                    mode = unconfig_str,
252                    dup = dual_if.get_vrf_name(),
253                    next_hop = client_net_next_hop,
254                    dest_mac = dual_if.server_if.get_dest_mac()))
255
256                # assign generated interfaces config list to cache
257                cache.add('IF', server_if_command_set, dual_if.server_if.get_name())
258                cache.add('IF', client_if_command_set, dual_if.client_if.get_name())
259
260            else:
261                conf_t_command_set.append( "{mode}ip route {next_net} {dest_mask} {next_hop}".format(
262                    mode = unconfig_str,
263                    next_net = client_net,
264                    dest_mask = stat_route_obj.client_mask,
265                    next_hop = server_net_next_hop))
266                conf_t_command_set.append( "{mode}ip route {next_net} {dest_mask} {next_hop}".format(
267                    mode = unconfig_str,
268                    next_net = server_net,
269                    dest_mask = stat_route_obj.server_mask,
270                    next_hop = client_net_next_hop))
271
272                # config global arp to interfaces net address
273                conf_t_command_set.append('{mode}arp {next_hop} {dest_mac} arpa'.format(
274                    mode = unconfig_str,
275                    next_hop = server_net_next_hop,
276                    dest_mac = dual_if.client_if.get_dest_mac()))
277                conf_t_command_set.append('{mode}arp {next_hop} {dest_mac} arpa'.format(
278                    mode = unconfig_str,
279                    next_hop = client_net_next_hop,
280                    dest_mac = dual_if.server_if.get_dest_mac()))
281
282            # bump up to the next client network address
283            client_net = misc_methods.get_single_net_client_addr(client_net, stat_route_obj.net_increment)
284            server_net = misc_methods.get_single_net_client_addr(server_net, stat_route_obj.net_increment)
285
286
287        # finish handling pre-config cache
288        pre_commit_set = list(pre_commit_set)
289#       pre_commit_set.append('exit')
290        pre_commit_cache.add('CONF', pre_commit_set )
291        # assign generated config list to cache
292        cache.add('CONF', conf_t_command_set)
293        # deploy the configs (order is important!)
294        self.cmd_link.run_command( [pre_commit_cache, cache] )
295        if self.config_history['basic_if_config']:
296            # in this case, duplicated interfaces will lose its ip address.
297            # re-config IPv4 addresses
298            self.configure_basic_filtered_interfaces(self.if_mngr.get_duplicated_if() )
299
300
301    def config_no_static_routing (self, stat_route_obj = None):
302
303        if stat_route_obj is None and self.stat_route_config is not None:
304            self.config_static_routing(self.stat_route_config, mode = 'unconfig')
305            self.stat_route_config = None  # reverse current static route config back to None (no nat config is known to run).
306        elif stat_route_obj is not None:
307            self.config_static_routing(stat_route_obj, mode = 'unconfig')
308        else:
309            raise UserWarning('No static route configuration is available for removal.')
310
311    def config_nbar_pd (self, mode = 'config'):
312        unconfig_str = '' if mode=='config' else 'no '
313        cache = CCommandCache()
314
315        for intf in self.if_mngr.get_if_list(if_type = IFType.Client):
316            cache.add('IF', "{mode}ip nbar protocol-discovery".format( mode = unconfig_str ), intf.get_name())
317
318        self.cmd_link.run_single_command( cache )
319
320    def config_no_nbar_pd (self):
321        self.config_nbar_pd (mode = 'unconfig')
322
323
324    def config_nat_verify (self, mode = 'config'):
325
326        # toggle all duplicate interfaces
327        # dup_ifs = self.if_mngr.get_duplicated_if()
328        if mode=='config':
329            self.toggle_duplicated_intf(action = 'down')
330            # self.__toggle_interfaces(dup_ifs, action = 'down' )
331        else:
332            # if we're in 'unconfig', toggle duplicated interfaces back up
333            self.toggle_duplicated_intf(action = 'up')
334            # self.__toggle_interfaces(dup_ifs)
335
336    def config_no_nat_verify (self):
337        self.config_nat_verify(mode = 'unconfig')
338
339    def config_nat (self, nat_obj, mode = 'config'):
340
341        if mode == 'config':
342            self.nat_config = nat_obj   # save the latest nat config for future removal purposes
343
344        cache               = CCommandCache()
345        conf_t_command_set  = []
346        client_net          = nat_obj.clients_net_start
347        pool_net            = nat_obj.nat_pool_start
348        unconfig_str        = '' if mode=='config' else 'no '
349
350        # toggle all duplicate interfaces
351        # dup_ifs = self.if_mngr.get_duplicated_if()
352        if mode=='config':
353            self.toggle_duplicated_intf(action = 'down')
354            # self.__toggle_interfaces(dup_ifs, action = 'down' )
355        else:
356            # if we're in 'unconfig', toggle duplicated interfaces back up
357            self.toggle_duplicated_intf(action = 'up')
358            # self.__toggle_interfaces(dup_ifs)
359
360        for dual_if in self.if_mngr.get_dual_if_list(is_duplicated = False):
361            cache.add('IF', "{mode}ip nat inside".format( mode = unconfig_str ), dual_if.client_if.get_name())
362            cache.add('IF', "{mode}ip nat outside".format( mode = unconfig_str ), dual_if.server_if.get_name())
363            pool_id = dual_if.get_id() + 1
364
365            conf_t_command_set.append("{mode}ip nat pool pool{pool_num} {start_addr} {end_addr} netmask {mask}".format(
366                mode = unconfig_str,
367                pool_num = pool_id,
368                start_addr = pool_net,
369                end_addr = CNatConfig.calc_pool_end(pool_net, nat_obj.nat_netmask),
370                mask = nat_obj.nat_netmask))
371
372            conf_t_command_set.append("{mode}ip nat inside source list {num} pool pool{pool_num} overload".format(
373                mode = unconfig_str,
374                num = pool_id,
375                pool_num = pool_id ))
376            conf_t_command_set.append("{mode}access-list {num} permit {net_addr} {net_wildcard}".format(
377                mode = unconfig_str,
378                num = pool_id,
379                net_addr = client_net,
380                net_wildcard = nat_obj.client_acl_wildcard))
381
382            # bump up to the next client address
383            client_net = misc_methods.get_single_net_client_addr(client_net, nat_obj.net_increment)
384            pool_net   = misc_methods.get_single_net_client_addr(pool_net, nat_obj.net_increment)
385
386
387        # assign generated config list to cache
388        cache.add('CONF', conf_t_command_set)
389
390        # deploy the configs (order is important!)
391        self.cmd_link.run_single_command( cache )
392
393
394    def config_no_nat (self, nat_obj = None):
395        # first, clear all nat translations
396        self.clear_nat_translations()
397
398        # then, decompose the known config
399        if nat_obj is None and self.nat_config is not None:
400            self.config_nat(self.nat_config, mode = 'unconfig')
401            self.nat_config = None  # reverse current NAT config back to None (no nat config is known to run).
402        elif nat_obj is not None:
403            self.config_nat(nat_obj, mode = 'unconfig')
404        else:
405            raise UserWarning('No NAT configuration is available for removal.')
406
407
408    def config_zbf (self, mode = 'config'):
409        cache               = CCommandCache()
410        pre_commit_cache    = CCommandCache()
411        conf_t_command_set  = []
412
413        # toggle all duplicate interfaces down
414        self.toggle_duplicated_intf(action = 'down')
415        # dup_ifs = self.if_mngr.get_duplicated_if()
416        # self.__toggle_interfaces(dup_ifs, action = 'down' )
417
418        # define security zones and security service policy to be applied on the interfaces
419        conf_t_command_set.append('class-map type inspect match-any c1')
420        conf_t_command_set.append('match protocol tcp')
421        conf_t_command_set.append('match protocol udp')
422        conf_t_command_set.append('policy-map type inspect p1')
423        conf_t_command_set.append('class type inspect c1')
424        conf_t_command_set.append('inspect')
425        conf_t_command_set.append('class class-default')
426        conf_t_command_set.append('pass')
427
428        conf_t_command_set.append('zone security z_in')
429        conf_t_command_set.append('zone security z_out')
430
431        conf_t_command_set.append('zone-pair security in2out source z_in destination z_out')
432        conf_t_command_set.append('service-policy type inspect p1')
433        conf_t_command_set.append('zone-pair security out2in source z_out destination z_in')
434        conf_t_command_set.append('service-policy type inspect p1')
435        conf_t_command_set.append('exit')
436
437        pre_commit_cache.add('CONF', conf_t_command_set)
438
439        for dual_if in self.if_mngr.get_dual_if_list(is_duplicated = False):
440            cache.add('IF', "zone-member security z_in", dual_if.client_if.get_name() )
441            cache.add('IF', "zone-member security z_out", dual_if.server_if.get_name() )
442
443        self.cmd_link.run_command( [pre_commit_cache, cache] )
444
445    def config_no_zbf (self):
446        cache               = CCommandCache()
447        conf_t_command_set  = []
448
449        # define security zones and security service policy to be applied on the interfaces
450        conf_t_command_set.append('no zone-pair security in2out source z_in destination z_out')
451        conf_t_command_set.append('no zone-pair security out2in source z_out destination z_in')
452
453        conf_t_command_set.append('no policy-map type inspect p1')
454        conf_t_command_set.append('no class-map type inspect match-any c1')
455
456        conf_t_command_set.append('no zone security z_in')
457        conf_t_command_set.append('no zone security z_out')
458
459        cache.add('CONF', conf_t_command_set)
460
461        for dual_if in self.if_mngr.get_dual_if_list(is_duplicated = False):
462            cache.add('IF', "no zone-member security z_in", dual_if.client_if.get_name() )
463            cache.add('IF', "no zone-member security z_out", dual_if.server_if.get_name() )
464
465        self.cmd_link.run_command( [cache] )
466        # toggle all duplicate interfaces back up
467        self.toggle_duplicated_intf(action = 'up')
468        # dup_ifs = self.if_mngr.get_duplicated_if()
469        # self.__toggle_interfaces(dup_ifs)
470
471
472    def config_ipv6_pbr (self, mode = 'config'):
473        idx = 1
474        unconfig_str = '' if mode=='config' else 'no '
475        cache               = CCommandCache()
476        conf_t_command_set  = []
477
478        conf_t_command_set.append('{mode}ipv6 unicast-routing'.format(mode = unconfig_str) )
479
480        for dual_if in self.if_mngr.get_dual_if_list():
481            client_if_command_set   = []
482            server_if_command_set   = []
483
484            client_net_next_hop = misc_methods.get_single_net_client_addr(dual_if.server_if.get_ipv6_addr(), {'7':1}, ip_type = 'ipv6' )
485            server_net_next_hop = misc_methods.get_single_net_client_addr(dual_if.client_if.get_ipv6_addr(), {'7':1}, ip_type = 'ipv6' )
486
487
488            client_if_command_set.append ('{mode}ipv6 enable'.format(mode = unconfig_str))
489            server_if_command_set.append ('{mode}ipv6 enable'.format(mode = unconfig_str))
490
491            if dual_if.is_duplicated():
492                prefix = 'ipv6_' + dual_if.get_vrf_name()
493            else:
494                prefix = 'ipv6'
495
496            # config interfaces with relevant route-map
497            client_if_command_set.append ('{mode}ipv6 policy route-map {pre}_{p1}_to_{p2}'.format(
498                mode = unconfig_str,
499                pre = prefix,
500                p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
501            server_if_command_set.append ('{mode}ipv6 policy route-map {pre}_{p2}_to_{p1}'.format(
502                mode = unconfig_str,
503                pre = prefix,
504                p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
505
506            # config global arp to interfaces net address and vrf
507            conf_t_command_set.append('{mode}ipv6 neighbor {next_hop} {intf} {dest_mac}'.format(
508                    mode = unconfig_str,
509                    next_hop = server_net_next_hop,
510                    intf = dual_if.client_if.get_name(),
511                    dest_mac = dual_if.client_if.get_dest_mac()))
512            conf_t_command_set.append('{mode}ipv6 neighbor {next_hop} {intf} {dest_mac}'.format(
513                    mode = unconfig_str,
514                    next_hop = client_net_next_hop,
515                    intf = dual_if.server_if.get_name(),
516                    dest_mac = dual_if.server_if.get_dest_mac()))
517
518            conf_t_command_set.append('{mode}route-map {pre}_{p1}_to_{p2} permit 10'.format(
519                    mode = unconfig_str,
520                    pre = prefix,
521                    p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
522            if (mode == 'config'):
523                conf_t_command_set.append('set ipv6 next-hop {next_hop}'.format(next_hop = client_net_next_hop ) )
524            conf_t_command_set.append('{mode}route-map {pre}_{p2}_to_{p1} permit 10'.format(
525                    mode = unconfig_str,
526                    pre = prefix,
527                    p1 = 'p'+str(idx), p2 = 'p'+str(idx+1) ) )
528            if (mode == 'config'):
529                conf_t_command_set.append('set ipv6 next-hop {next_hop}'.format(next_hop = server_net_next_hop ) )
530                conf_t_command_set.append('exit')
531
532            # assign generated config list to cache
533            cache.add('IF', server_if_command_set, dual_if.server_if.get_name())
534            cache.add('IF', client_if_command_set, dual_if.client_if.get_name())
535            idx += 2
536
537        cache.add('CONF', conf_t_command_set)
538
539        # deploy the configs (order is important!)
540        self.cmd_link.run_command( [cache] )
541
542    def config_no_ipv6_pbr (self):
543        self.config_ipv6_pbr(mode = 'unconfig')
544
545    # show methods
546    def get_cpu_util (self):
547        response    = self.cmd_link.run_single_command('show platform hardware qfp active datapath utilization | inc Load')
548        return CShowParser.parse_cpu_util_stats(response)
549
550    def get_cft_stats (self):
551        response    = self.cmd_link.run_single_command('test platform hardware qfp active infrastructure cft datapath function cft-cpp-show-all-instances')
552        return CShowParser.parse_cft_stats(response)
553
554    def get_nbar_stats (self):
555        per_intf_stats = {}
556        for intf in self.if_mngr.get_if_list(if_type = IFType.Client):
557            response = self.cmd_link.run_single_command("show ip nbar protocol-discovery interface {interface} stats packet-count protocol".format( interface = intf.get_name() ), flush_first = True)
558            per_intf_stats[intf.get_name()] = CShowParser.parse_nbar_stats(response)
559        return per_intf_stats
560
561    def get_nbar_profiling_stats (self):
562        response = self.cmd_link.run_single_command("show platform hardware qfp active feature nbar profiling")
563        return CShowParser.parse_nbar_profiling_stats(response)
564
565    def get_drop_stats (self):
566
567        response    = self.cmd_link.run_single_command('show platform hardware qfp active interface all statistics', flush_first = True)
568        # print response
569        # response    = self.cmd_link.run_single_command('show platform hardware qfp active interface all statistics')
570        # print response
571        if_list_by_name = [x.get_name() for x in self.if_mngr.get_if_list()]
572        return CShowParser.parse_drop_stats(response, if_list_by_name )
573
574    def get_nat_stats (self):
575        response    = self.cmd_link.run_single_command('show ip nat statistics')
576        return CShowParser.parse_nat_stats(response)
577
578    def get_cvla_memory_usage(self):
579        response    = self.cmd_link.run_single_command('show platform hardware qfp active infrastructure cvla client handles')
580        # (res, res2) = CShowParser.parse_cvla_memory_usage(response)
581        return CShowParser.parse_cvla_memory_usage(response)
582
583
584    # clear methods
585    def clear_nat_translations(self):
586        pre_commit_cache = CCommandCache()
587        pre_commit_cache.add('EXEC', 'clear ip nat translation *')
588        self.cmd_link.run_single_command( pre_commit_cache )
589
590    def clear_cft_counters (self):
591        """ clear_cft_counters(self) -> None
592
593        Clears the CFT counters on the platform
594        """
595        self.cmd_link.run_single_command('test platform hardware qfp active infrastructure cft datapath function cft-cpp-clear-instance-stats')
596
597    def clear_counters(self):
598        """ clear_counters(self) -> None
599
600        Clears the platform counters
601        """
602
603        pre_commit_cache = CCommandCache()
604        pre_commit_cache.add('EXEC', ['clear counters','\r'] )
605        self.cmd_link.run_single_command( pre_commit_cache )
606
607    def clear_nbar_stats(self):
608        """ clear_nbar_stats(self) -> None
609
610        Clears the NBAR-PD classification stats
611        """
612        pre_commit_cache = CCommandCache()
613        pre_commit_cache.add('EXEC', ['clear ip nbar protocol-discovery','\r'] )
614        self.cmd_link.run_single_command( pre_commit_cache )
615
616    def clear_packet_drop_stats(self):
617        """ clear_packet_drop_stats(self) -> None
618
619        Clears packet-drop stats
620        """
621#       command = "show platform hardware qfp active statistics drop clear"
622        self.cmd_link.run_single_command('show platform hardware qfp active interface all statistics clear_drop')
623
624    ###########################################
625    # file transfer and image loading methods #
626    ###########################################
627    def get_running_image_details (self):
628        """ get_running_image_details() -> dict
629
630        Check for the currently running image file on the platform.
631        Returns a dictionary, where 'drive' key is the drive in which the image is installed,
632        and 'image' key is the actual image file used.
633        """
634        response = self.cmd_link.run_single_command('show version | include System image')
635        parsed_info = CShowParser.parse_show_image_version(response)
636        self.running_image = parsed_info
637        return parsed_info
638
639
640    def check_image_existence (self, img_name):
641        """ check_image_existence(self, img_name) -> boolean
642
643        Parameters
644        ----------
645        img_name : str
646            a string represents the image name.
647
648        Check if the image file defined in the platform_config already loaded into the platform.
649        """
650        search_drives = ['bootflash', 'harddisk', self.running_image['drive']]
651        for search_drive in search_drives:
652            command = "dir {drive}: | include {image}".format(drive = search_drive, image = img_name)
653            response = self.cmd_link.run_single_command(command, timeout = 10)
654            if CShowParser.parse_image_existence(response, img_name):
655                self.needed_image_path = '%s:/%s' % (search_drive, img_name)
656                print('Found image in platform:', self.needed_image_path)
657                return True
658        return False
659
660    def config_tftp_server(self, device_cfg_obj, external_tftp_config = None, applyToPlatform = False):
661        """ configure_tftp_server(self, external_tftp_config, applyToPlatform) -> str
662
663        Parameters
664        ----------
665        external_tftp_config : dict (Not is use)
666            A path to external tftp config file other than using the one defined in the instance.
667        applyToPlatform : boolean
668            set to True in order to apply the config into the platform
669
670        Configures the tftp server on an interface of the platform.
671        """
672#       tmp_tftp_config = external_tftp_config if external_tftp_config is not None else self.tftp_server_config
673        self.tftp_cfg   = device_cfg_obj.get_tftp_info()
674        cache           = CCommandCache()
675
676        command = "ip tftp source-interface {intf}".format( intf = device_cfg_obj.get_mgmt_interface() )
677        cache.add('CONF', command )
678        self.cmd_link.run_single_command(cache)
679        self.config_history['tftp_server_config'] = True
680
681    def load_platform_image(self, img_filename, external_tftp_config = None):
682        """ load_platform_image(self, img_filename, external_tftp_config) -> None
683
684        Parameters
685        ----------
686        external_tftp_config : dict
687            A path to external tftp config file other than using the one defined in the instance.
688        img_filename : str
689            image name to be saved into the platforms drive.
690
691        This method loads the configured image into the platform's harddisk (unless it is already loaded),
692        and sets that image to be the boot_image of the platform.
693        """
694        if not self.check_image_existence(img_filename): # check if this image isn't already saved in platform
695            #tmp_tftp_config = external_tftp_config if external_tftp_config is not None else self.tftp_cfg
696
697            if self.config_history['tftp_server_config']:  # make sure a TFTP configuration has been loaded
698                cache = CCommandCache()
699                if self.running_image is None:
700                    self.get_running_image_details()
701
702                command = "copy tftp://{tftp_ip}/{img_path}/{image} harddisk:".format(
703                    tftp_ip  = self.tftp_cfg['ip_address'],
704                    img_path = self.tftp_cfg['images_path'],
705                    image    = img_filename)
706                cache.add('EXEC', [command, '\r', '\r'])
707
708                progress_thread = CProgressDisp.ProgressThread(notifyMessage = "Copying image via tftp, this may take a while...\n")
709                progress_thread.start()
710
711                response = self.cmd_link.run_single_command(cache, timeout = 900, read_until = ['\?', '\#'])
712                print("RESPONSE:")
713                print(response)
714                progress_thread.join()
715                copy_ok = CShowParser.parse_file_copy(response)
716
717                if not copy_ok:
718                    raise UserWarning('Image file loading failed. Please make sure the accessed image exists and has read privileges')
719            else:
720                raise UserWarning('TFTP configuration is not available. Please make sure a valid TFTP configuration has been provided')
721
722    def set_boot_image(self, boot_image):
723        """ set_boot_image(self, boot_image) -> None
724
725        Parameters
726        ----------
727        boot_image : str
728            An image file to be set as boot_image
729
730        Configures boot_image as the boot image of the platform into the running-config + config-register
731        """
732        cache = CCommandCache()
733        if self.needed_image_path is None:
734            if not self.check_image_existence(boot_image):
735                raise Exception("Trying to set boot image that's not found in router, please copy it first.")
736
737        boot_img_cmd = "boot system flash %s" % self.needed_image_path
738        config_register_cmd = "config-register 0x2021"
739        cache.add('CONF', ["no boot system", boot_img_cmd, config_register_cmd, '\r'])
740        response = self.cmd_link.run_single_command( cache )
741        print("RESPONSE:")
742        print(response)
743        self.save_config_to_startup_config()
744
745    def is_image_matches(self, needed_image):
746        """ set_boot_image(self, needed_image) -> boolean
747
748        Parameters
749        ----------
750        needed_image : str
751            An image file to compare router running image
752
753        Compares image name to router running image, returns match result.
754
755        """
756        if self.running_image is None:
757            self.get_running_image_details()
758        needed_image = needed_image.lower()
759        current_image = self.running_image['image'].lower()
760        if needed_image.find(current_image) != -1:
761            return True
762        if current_image.find(needed_image) != -1:
763            return True
764        return False
765
766    # misc class related methods
767
768    def load_platform_data_from_file (self, device_cfg_obj):
769        self.if_mngr.load_config(device_cfg_obj)
770
771    def launch_connection (self, device_cfg_obj):
772        self.running_image = None # clear the image name "cache"
773        self.cmd_link.launch_platform_connectivity(device_cfg_obj)
774
775    def reload_connection (self, device_cfg_obj):
776        self.cmd_link.close_platform_connection()
777        self.launch_connection(device_cfg_obj)
778
779    def save_config_to_startup_config (self):
780        """ save_config_to_startup_config(self) -> None
781
782        Copies running-config into startup-config.
783        """
784        cache = CCommandCache()
785        cache.add('EXEC', ['wr', '\r'] )
786        self.cmd_link.run_single_command(cache)
787
788    def reload_platform(self, device_cfg_obj):
789        """ reload_platform(self) -> None
790
791        Reloads the platform.
792        """
793        from subprocess import call
794        import os
795        i = 0
796        sleep_time = 30 # seconds
797
798        try:
799            cache = CCommandCache()
800
801            cache.add('EXEC', ['reload','n\r','\r'] )
802            self.cmd_link.run_single_command( cache )
803
804            progress_thread = CProgressDisp.ProgressThread(notifyMessage = "Reloading the platform, this may take a while...\n")
805            progress_thread.start()
806            time.sleep(60) # need delay for device to shut down before polling it
807            # poll the platform until ping response is received.
808            while True:
809                time.sleep(sleep_time)
810                try:
811                    x = call(["ping", "-c 1", device_cfg_obj.get_ip_address()], stdout = open(os.devnull, 'wb'))
812                except:
813                    x = 1
814                if x == 0:
815                    break
816                elif i > 20:
817                    raise TimeoutError('Platform failed to reload after reboot for over {minutes} minutes!'.format(minutes = round(1 + i * sleep_time / 60)))
818                else:
819                    i += 1
820
821            time.sleep(30)
822            self.reload_connection(device_cfg_obj)
823            progress_thread.join()
824        except Exception as e:
825            print(e)
826
827    def get_if_manager(self):
828        return self.if_mngr
829
830    def dump_obj_config (self, object_name):
831        if object_name=='nat' and self.nat_config is not None:
832            self.nat_config.dump_config()
833        elif object_name=='static_route' and self.stat_route_config is not None:
834            self.stat_route_config.dump_config()
835        else:
836            raise UserWarning('No known configuration exists.')
837
838    def toggle_duplicated_intf(self, action = 'down'):
839
840        dup_ifs = self.if_mngr.get_duplicated_if()
841        self.__toggle_interfaces( dup_ifs, action = action )
842
843
844    def __toggle_interfaces (self, intf_list, action = 'up'):
845        cache = CCommandCache()
846        mode_str = 'no ' if action == 'up' else ''
847
848        for intf_obj in intf_list:
849            cache.add('IF', '{mode}shutdown'.format(mode = mode_str), intf_obj.get_name())
850
851        self.cmd_link.run_single_command( cache )
852
853
854class CStaticRouteConfig(object):
855
856    def __init__(self, static_route_dict):
857        self.clients_start  = static_route_dict['clients_start']
858        self.servers_start  = static_route_dict['servers_start']
859        self.net_increment  = misc_methods.gen_increment_dict(static_route_dict['dual_port_mask'])
860        self.client_mask    = static_route_dict['client_destination_mask']
861        self.server_mask    = static_route_dict['server_destination_mask']
862        self.client_net_start = self.extract_net_addr(self.clients_start, self.client_mask)
863        self.server_net_start = self.extract_net_addr(self.servers_start, self.server_mask)
864        self.static_route_dict = static_route_dict
865
866    def extract_net_addr (self, ip_addr, ip_mask):
867        addr_lst = ip_addr.split('.')
868        mask_lst = ip_mask.split('.')
869        mask_lst = [str(int(x) & int(y)) for x, y in zip(addr_lst, mask_lst)]
870        return '.'.join(mask_lst)
871
872    def dump_config (self):
873        import yaml
874        print(yaml.dump( self.static_route_dict , default_flow_style=False))
875
876
877class CNatConfig(object):
878    def __init__(self, nat_dict):
879        self.clients_net_start  = nat_dict['clients_net_start']
880        self.client_acl_wildcard= nat_dict['client_acl_wildcard_mask']
881        self.net_increment      = misc_methods.gen_increment_dict(nat_dict['dual_port_mask'])
882        self.nat_pool_start     = nat_dict['pool_start']
883        self.nat_netmask        = nat_dict['pool_netmask']
884        self.nat_dict           = nat_dict
885
886    @staticmethod
887    def calc_pool_end (nat_pool_start, netmask):
888        pool_start_lst  = [int(x) for x in nat_pool_start.split('.')]
889        pool_end_lst    = list( pool_start_lst )   # create new list object, don't point to the original one
890        mask_lst        = [int(x) for x in netmask.split('.')]
891        curr_octet      = 3 # start with the LSB octet
892        inc_val         = 1
893
894        while True:
895            tmp_masked = inc_val & mask_lst[curr_octet]
896            if tmp_masked == 0:
897                if (inc_val << 1) > 255:
898                    inc_val = 1
899                    pool_end_lst[curr_octet] = 255
900                    curr_octet -= 1
901                else:
902                    inc_val <<= 1
903            else:
904                pool_end_lst[curr_octet] += (inc_val - 1)
905                break
906        return '.'.join([str(x) for x in pool_end_lst])
907
908    def dump_config (self):
909        import yaml
910        print(yaml.dump( self.nat_dict , default_flow_style=False))
911
912
913if __name__ == "__main__":
914    pass
915