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