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