cisco_node_utils 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rubocop.yml +81 -1
  4. data/.travis.yml +9 -0
  5. data/CHANGELOG.md +72 -6
  6. data/CONTRIBUTING.md +32 -7
  7. data/README.md +70 -7
  8. data/Rakefile +17 -0
  9. data/bin/check_metric_limits.rb +109 -0
  10. data/bin/git/hooks/commit-msg/enforce_style +81 -0
  11. data/bin/git/hooks/hook_lib +108 -0
  12. data/bin/git/hooks/hooks-wrapper +38 -0
  13. data/bin/git/hooks/post-flow-hotfix-start/update-version +24 -0
  14. data/bin/git/hooks/post-flow-release-finish/update-version +29 -0
  15. data/bin/git/hooks/post-flow-release-start/update-version +19 -0
  16. data/bin/git/hooks/post-merge/update-hooks +6 -0
  17. data/bin/git/hooks/post-rewrite/update-hooks +6 -0
  18. data/bin/git/hooks/pre-commit/rubocop +20 -0
  19. data/bin/git/hooks/pre-commit/validate-diffs +31 -0
  20. data/bin/git/hooks/pre-push/check-changelog +24 -0
  21. data/bin/git/hooks/pre-push/rubocop +7 -0
  22. data/bin/git/update-hooks +65 -0
  23. data/cisco_node_utils.gemspec +9 -3
  24. data/docs/README-develop-best-practices.md +404 -0
  25. data/docs/README-develop-node-utils-APIs.md +215 -365
  26. data/docs/README-maintainers.md +33 -3
  27. data/docs/template-router.rb +89 -91
  28. data/docs/template-test_router.rb +52 -55
  29. data/lib/.rubocop.yml +18 -0
  30. data/lib/cisco_node_utils.rb +2 -19
  31. data/lib/cisco_node_utils/README_YAML.md +1 -9
  32. data/lib/cisco_node_utils/bgp.rb +664 -0
  33. data/lib/cisco_node_utils/bgp_af.rb +530 -0
  34. data/lib/cisco_node_utils/bgp_neighbor.rb +425 -0
  35. data/lib/cisco_node_utils/bgp_neighbor_af.rb +709 -0
  36. data/lib/cisco_node_utils/cisco_cmn_utils.rb +59 -25
  37. data/lib/cisco_node_utils/command_reference.rb +72 -74
  38. data/lib/cisco_node_utils/command_reference_common.yaml +174 -9
  39. data/lib/cisco_node_utils/command_reference_common_bgp.yaml +535 -0
  40. data/lib/cisco_node_utils/command_reference_n7k.yaml +4 -0
  41. data/lib/cisco_node_utils/command_reference_n9k.yaml +0 -9
  42. data/lib/cisco_node_utils/configparser_lib.rb +152 -147
  43. data/lib/cisco_node_utils/dns_domain.rb +79 -0
  44. data/lib/cisco_node_utils/domain_name.rb +71 -0
  45. data/lib/cisco_node_utils/interface.rb +167 -161
  46. data/lib/cisco_node_utils/interface_ospf.rb +78 -81
  47. data/lib/cisco_node_utils/name_server.rb +64 -0
  48. data/lib/cisco_node_utils/node.rb +154 -198
  49. data/lib/cisco_node_utils/node_util.rb +61 -0
  50. data/lib/cisco_node_utils/ntp_config.rb +65 -0
  51. data/lib/cisco_node_utils/ntp_server.rb +76 -0
  52. data/lib/cisco_node_utils/platform.rb +174 -165
  53. data/lib/cisco_node_utils/radius_global.rb +146 -0
  54. data/lib/cisco_node_utils/radius_server.rb +295 -0
  55. data/lib/cisco_node_utils/router_ospf.rb +59 -63
  56. data/lib/cisco_node_utils/router_ospf_vrf.rb +226 -210
  57. data/lib/cisco_node_utils/snmpcommunity.rb +52 -58
  58. data/lib/cisco_node_utils/snmpgroup.rb +22 -23
  59. data/lib/cisco_node_utils/snmpserver.rb +99 -103
  60. data/lib/cisco_node_utils/snmpuser.rb +294 -274
  61. data/lib/cisco_node_utils/syslog_server.rb +92 -0
  62. data/lib/cisco_node_utils/syslog_settings.rb +69 -0
  63. data/lib/cisco_node_utils/tacacs_server.rb +137 -133
  64. data/lib/cisco_node_utils/tacacs_server_host.rb +84 -87
  65. data/lib/cisco_node_utils/version.rb +2 -1
  66. data/lib/cisco_node_utils/vlan.rb +28 -31
  67. data/lib/cisco_node_utils/vrf.rb +80 -0
  68. data/lib/cisco_node_utils/vtp.rb +100 -97
  69. data/lib/cisco_node_utils/yum.rb +15 -17
  70. data/tests/.rubocop.yml +15 -0
  71. data/tests/basetest.rb +81 -36
  72. data/tests/ciscotest.rb +38 -78
  73. data/{lib/cisco_node_utils → tests}/platform_info.rb +12 -8
  74. data/{lib/cisco_node_utils → tests}/platform_info.yaml +1 -1
  75. data/tests/test_bgp_af.rb +920 -0
  76. data/tests/test_bgp_neighbor.rb +403 -0
  77. data/tests/test_bgp_neighbor_af.rb +589 -0
  78. data/tests/test_command_config.rb +65 -62
  79. data/tests/test_command_reference.rb +31 -45
  80. data/tests/test_dns_domain.rb +113 -0
  81. data/tests/test_domain_name.rb +86 -0
  82. data/tests/test_interface.rb +424 -548
  83. data/tests/test_interface_ospf.rb +248 -432
  84. data/tests/test_interface_svi.rb +56 -79
  85. data/tests/test_interface_switchport.rb +196 -272
  86. data/tests/test_name_server.rb +85 -0
  87. data/tests/test_node.rb +7 -6
  88. data/tests/test_node_ext.rb +133 -186
  89. data/tests/test_ntp_config.rb +49 -0
  90. data/tests/test_ntp_server.rb +74 -0
  91. data/tests/test_platform.rb +58 -37
  92. data/tests/test_radius_global.rb +78 -0
  93. data/tests/test_radius_server.rb +185 -0
  94. data/tests/test_router_bgp.rb +838 -0
  95. data/tests/test_router_ospf.rb +49 -80
  96. data/tests/test_router_ospf_vrf.rb +274 -392
  97. data/tests/test_snmpcommunity.rb +128 -172
  98. data/tests/test_snmpgroup.rb +12 -14
  99. data/tests/test_snmpserver.rb +160 -189
  100. data/tests/test_snmpuser.rb +568 -717
  101. data/tests/test_syslog_server.rb +88 -0
  102. data/tests/test_syslog_settings.rb +54 -0
  103. data/tests/test_tacacs_server.rb +113 -148
  104. data/tests/test_tacacs_server_host.rb +108 -161
  105. data/tests/test_vlan.rb +63 -79
  106. data/tests/test_vrf.rb +92 -0
  107. data/tests/test_vtp.rb +108 -126
  108. data/tests/test_yum.rb +47 -41
  109. metadata +92 -56
  110. data/.rubocop_todo.yml +0 -293
  111. data/docs/.rubocop.yml +0 -13
  112. data/docs/template-feature.rb +0 -45
  113. data/docs/template-test_feature.rb +0 -51
  114. data/tests/test_all_cisco.rb +0 -46
@@ -0,0 +1,92 @@
1
+ # Syslog Server provider class
2
+ #
3
+ # Jonathan Tripathy et al., September 2015
4
+ #
5
+ # Copyright (c) 2014-2015 Cisco and/or its affiliates.
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require_relative 'node_util'
20
+
21
+ module Cisco
22
+ # NtpServer - node utility class for NTP Server configuration management
23
+ class SyslogServer < NodeUtil
24
+ attr_reader :name, :level, :vrf
25
+
26
+ def initialize(name,
27
+ level=nil,
28
+ vrf=nil,
29
+ instantiate=true)
30
+ fail TypeError unless name.is_a?(String)
31
+ fail TypeError unless name.length > 0
32
+ @name = name
33
+
34
+ fail TypeError unless level.is_a?(Integer) unless level.nil?
35
+ @level = level
36
+
37
+ fail TypeError unless vrf.is_a?(String) unless vrf.nil?
38
+ @vrf = vrf
39
+
40
+ create if instantiate
41
+ end
42
+
43
+ def self.syslogservers
44
+ hash = {}
45
+
46
+ syslogservers_list = config_get('syslog_server', 'server')
47
+ return hash if syslogservers_list.nil?
48
+
49
+ syslogservers_list.each do |id|
50
+ level = config_get('syslog_server', 'level', id)
51
+ level = level[0].to_i unless level.nil?
52
+
53
+ vrf = config_get('syslog_server', 'vrf', id)
54
+ if vrf.nil?
55
+ vrf = 'default'
56
+ else
57
+ vrf = vrf[0]
58
+ end
59
+
60
+ hash[id] = SyslogServer.new(id, level, vrf, false)
61
+ end
62
+
63
+ hash
64
+ end
65
+
66
+ def ==(other)
67
+ (name == other.name) && (vrf == other.vrf)
68
+ end
69
+
70
+ def create
71
+ # Set timestamp units
72
+ config_set('syslog_server',
73
+ 'server',
74
+ state: '',
75
+ ip: "#{name}",
76
+ level: level.nil? ? '' : "#{level}",
77
+ vrf: vrf.nil? ? '' : "use-vrf #{vrf}",
78
+ )
79
+ end
80
+
81
+ def destroy
82
+ # Set timestamp units
83
+ config_set('syslog_server',
84
+ 'server',
85
+ state: 'no',
86
+ ip: "#{name}",
87
+ level: '',
88
+ vrf: '',
89
+ )
90
+ end
91
+ end # class
92
+ end # module
@@ -0,0 +1,69 @@
1
+ # Syslog Settings provider class
2
+ #
3
+ # Jonathan Tripathy et al., September 2015
4
+ #
5
+ # Copyright (c) 2014-2015 Cisco and/or its affiliates.
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require_relative 'node_util'
20
+
21
+ module Cisco
22
+ # SyslogSettings - node utility class for
23
+ # Syslog Settings configuration management
24
+ class SyslogSettings < NodeUtil
25
+ attr_reader :name
26
+
27
+ def initialize(name)
28
+ fail TypeError unless name.is_a?(String)
29
+ fail ArgumentError,
30
+ "This provider only accepts an id of 'default'" \
31
+ unless name.eql?('default')
32
+ @name = name
33
+ end
34
+
35
+ def self.syslogsettings
36
+ hash = {}
37
+ hash['default'] = SyslogSettings.new('default')
38
+ hash
39
+ end
40
+
41
+ def ==(other)
42
+ name == other.name
43
+ end
44
+
45
+ def timestamp
46
+ timestamp = config_get('syslog_settings', 'timestamp')
47
+ if timestamp.nil?
48
+ # NXOS doesn't show if timestamp units is set to seconds, so we assume
49
+ # that no config displayed means that the parameter is set to seconds.
50
+ timestamp = config_get_default('syslog_settings', 'timestamp')
51
+ else
52
+ timestamp = config_get('syslog_settings', 'timestamp')[0]
53
+ end
54
+ timestamp
55
+ end
56
+
57
+ def timestamp=(val)
58
+ fail TypeError unless val.is_a?(String)
59
+ fail TypeError \
60
+ unless %w(seconds milliseconds).include?(timestamp)
61
+
62
+ # There is no unset version as timestamp has a default value
63
+ config_set('syslog_settings',
64
+ 'timestamp',
65
+ state: '',
66
+ units: val)
67
+ end
68
+ end # class
69
+ end # module
@@ -1,5 +1,3 @@
1
- # TacacsServer provider class
2
- #
3
1
  # Mike Wiebe, January 2015
4
2
  #
5
3
  # Copyright (c) 2015 Cisco and/or its affiliates.
@@ -16,160 +14,166 @@
16
14
  # See the License for the specific language governing permissions and
17
15
  # limitations under the License.
18
16
 
19
- require File.join(File.dirname(__FILE__), 'node')
17
+ require_relative 'node_util'
20
18
 
19
+ # Add some TACACS+ server related constants to the Cisco namespace
21
20
  module Cisco
22
- TACACS_SERVER_ENC_NONE = 0
23
- TACACS_SERVER_ENC_CISCO_TYPE_7 = 7
24
- TACACS_SERVER_ENC_UNKNOWN = 8
25
-
26
- class TacacsServer
27
- @@node = Cisco::Node.instance
28
-
29
- def initialize(instantiate=true)
30
- enable if instantiate and not TacacsServer.enabled
31
- end
21
+ TACACS_SERVER_ENC_NONE = 0
22
+ TACACS_SERVER_ENC_CISCO_TYPE_7 = 7
23
+ TACACS_SERVER_ENC_UNKNOWN = 8
24
+
25
+ # TacacsServer - node utility class for TACACS+ server config management
26
+ class TacacsServer < NodeUtil
27
+ def initialize(instantiate=true)
28
+ enable if instantiate && !TacacsServer.enabled
29
+ end
32
30
 
33
- # Check feature enablement
34
- def TacacsServer.enabled
35
- feat = @@node.config_get("tacacs_server", "feature")
36
- return (!feat.nil? and !feat.empty?)
37
- rescue Cisco::CliError => e
38
- # cmd will syntax reject when feature is not enabled
39
- raise unless e.clierror =~ /Syntax error/
40
- return false
41
- end
31
+ # Check feature enablement
32
+ def self.enabled
33
+ feat = config_get('tacacs_server', 'feature')
34
+ return !(feat.nil? || feat.empty?)
35
+ rescue Cisco::CliError => e
36
+ # cmd will syntax reject when feature is not enabled
37
+ raise unless e.clierror =~ /Syntax error/
38
+ return false
39
+ end
42
40
 
43
- # Enable tacacs_server feature
44
- def enable
45
- @@node.config_set("tacacs_server", "feature", "")
46
- end
41
+ # Enable tacacs_server feature
42
+ def enable
43
+ config_set('tacacs_server', 'feature', '')
44
+ end
47
45
 
48
- # Disable tacacs_server feature
49
- def destroy
50
- @@node.config_set("tacacs_server", "feature", "no")
51
- end
46
+ # Disable tacacs_server feature
47
+ def destroy
48
+ config_set('tacacs_server', 'feature', 'no')
49
+ end
52
50
 
53
- # --------------------
54
- # Getters and Setters
55
- # --------------------
51
+ # --------------------
52
+ # Getters and Setters
53
+ # --------------------
56
54
 
57
- # Set timeout
58
- def timeout=(timeout)
59
- # 'no tacacs timeout' will fail, just set it to the requested timeout value.
60
- @@node.config_set("tacacs_server", "timeout", "", timeout)
61
- end
55
+ # Set timeout
56
+ def timeout=(timeout)
57
+ # 'no tacacs timeout' will fail.
58
+ # Just set it to the requested timeout value.
59
+ config_set('tacacs_server', 'timeout', '', timeout)
60
+ end
62
61
 
63
- # Get timeout
64
- def timeout
65
- match = @@node.config_get("tacacs_server", "timeout")
66
- match.nil? ? TacacsServer.default_timeout : match.first.to_i
67
- end
62
+ # Get timeout
63
+ def timeout
64
+ match = config_get('tacacs_server', 'timeout')
65
+ match.nil? ? TacacsServer.default_timeout : match.first.to_i
66
+ end
68
67
 
69
- # Get default timeout
70
- def TacacsServer.default_timeout
71
- @@node.config_get_default("tacacs_server", "timeout")
72
- end
68
+ # Get default timeout
69
+ def self.default_timeout
70
+ config_get_default('tacacs_server', 'timeout')
71
+ end
73
72
 
74
- # Set deadtime
75
- def deadtime=(deadtime)
76
- # 'no tacacs deadtime' will fail, just set it to the requested timeout value.
77
- @@node.config_set("tacacs_server", "deadtime", "", deadtime)
78
- end
73
+ # Set deadtime
74
+ def deadtime=(deadtime)
75
+ # 'no tacacs deadtime' will fail.
76
+ # Just set it to the requested timeout value.
77
+ config_set('tacacs_server', 'deadtime', '', deadtime)
78
+ end
79
79
 
80
- # Get deadtime
81
- def deadtime
82
- match = @@node.config_get("tacacs_server", "deadtime")
83
- match.nil? ? TacacsServer.default_deadtime : match.first.to_i
84
- end
80
+ # Get deadtime
81
+ def deadtime
82
+ match = config_get('tacacs_server', 'deadtime')
83
+ match.nil? ? TacacsServer.default_deadtime : match.first.to_i
84
+ end
85
85
 
86
- # Get default deadtime
87
- def TacacsServer.default_deadtime
88
- @@node.config_get_default("tacacs_server", "deadtime")
89
- end
86
+ # Get default deadtime
87
+ def self.default_deadtime
88
+ config_get_default('tacacs_server', 'deadtime')
89
+ end
90
90
 
91
- # Set directed_request
92
- def directed_request=(state)
93
- raise TypeError unless state == true || state == false
94
- state == TacacsServer.default_directed_request ?
95
- @@node.config_set("tacacs_server", "directed_request", "no") :
96
- @@node.config_set("tacacs_server", "directed_request", "")
97
- end
91
+ # Set directed_request
92
+ def directed_request=(state)
93
+ fail TypeError unless state == true || state == false
94
+ if state == TacacsServer.default_directed_request
95
+ config_set('tacacs_server', 'directed_request', 'no')
96
+ else
97
+ config_set('tacacs_server', 'directed_request', '')
98
+ end
99
+ end
98
100
 
99
- # Check if directed request is enabled
100
- def directed_request?
101
- match = @@node.config_get("tacacs_server", "directed_request")
102
- return TacacsServer.default_directed_request if match.nil?
103
- match.first[/^no/] ? false : true
104
- end
101
+ # Check if directed request is enabled
102
+ def directed_request?
103
+ match = config_get('tacacs_server', 'directed_request')
104
+ return TacacsServer.default_directed_request if match.nil?
105
+ match.first[/^no/] ? false : true
106
+ end
105
107
 
106
- # Get default directed_request
107
- def TacacsServer.default_directed_request
108
- @@node.config_get_default("tacacs_server", "directed_request")
109
- end
108
+ # Get default directed_request
109
+ def self.default_directed_request
110
+ config_get_default('tacacs_server', 'directed_request')
111
+ end
110
112
 
111
- # Set source interface
112
- def source_interface=(name)
113
- raise TypeError unless name.is_a? String
114
- name.empty? ?
115
- @@node.config_set("tacacs_server", "source_interface", "no", "") :
116
- @@node.config_set("tacacs_server", "source_interface", "", name)
117
- end
113
+ # Set source interface
114
+ def source_interface=(name)
115
+ fail TypeError unless name.is_a? String
116
+ if name.empty?
117
+ config_set('tacacs_server', 'source_interface', 'no', '')
118
+ else
119
+ config_set('tacacs_server', 'source_interface', '', name)
120
+ end
121
+ end
118
122
 
119
- # Get source interface
120
- def source_interface
121
- # Sample output
122
- # ip tacacs source-interface Ethernet1/1
123
- # no tacacs source-interface
124
- match = @@node.config_get("tacacs_server", "source_interface")
125
- return TacacsServer.default_source_interface if match.nil?
126
- # match_data will contain one of the following
127
- # [nil, " Ethernet1/1"] or ["no", nil]
128
- match[0][0] == "no" ? TacacsServer.default_source_interface : match[0][1]
129
- end
123
+ # Get source interface
124
+ def source_interface
125
+ # Sample output
126
+ # ip tacacs source-interface Ethernet1/1
127
+ # no tacacs source-interface
128
+ match = config_get('tacacs_server', 'source_interface')
129
+ return TacacsServer.default_source_interface if match.nil?
130
+ # match_data will contain one of the following
131
+ # [nil, " Ethernet1/1"] or ["no", nil]
132
+ match[0][0] == 'no' ? TacacsServer.default_source_interface : match[0][1]
133
+ end
130
134
 
131
- # Get default source interface
132
- def TacacsServer.default_source_interface
133
- @@node.config_get_default("tacacs_server", "source_interface")
134
- end
135
+ # Get default source interface
136
+ def self.default_source_interface
137
+ config_get_default('tacacs_server', 'source_interface')
138
+ end
135
139
 
136
- # Get encryption type used for the key
137
- def encryption_type
138
- match = @@node.config_get("tacacs_server", "encryption_type")
139
- match.nil? ? TACACS_SERVER_ENC_UNKNOWN : match[0][0].to_i
140
- end
140
+ # Get encryption type used for the key
141
+ def encryption_type
142
+ match = config_get('tacacs_server', 'encryption_type')
143
+ match.nil? ? TACACS_SERVER_ENC_UNKNOWN : match[0][0].to_i
144
+ end
141
145
 
142
- # Get default encryption type
143
- def TacacsServer.default_encryption_type
144
- @@node.config_get_default("tacacs_server", "encryption_type")
145
- end
146
+ # Get default encryption type
147
+ def self.default_encryption_type
148
+ config_get_default('tacacs_server', 'encryption_type')
149
+ end
146
150
 
147
- # Get encryption password
148
- def encryption_password
149
- match = @@node.config_get("tacacs_server", "encryption_password")
150
- match.nil? ? TacacsServer.default_encryption_password : match[0][1]
151
- end
151
+ # Get encryption password
152
+ def encryption_password
153
+ match = config_get('tacacs_server', 'encryption_password')
154
+ match.nil? ? TacacsServer.default_encryption_password : match[0][1]
155
+ end
152
156
 
153
- # Get default encryption password
154
- def TacacsServer.default_encryption_password
155
- @@node.config_get_default("tacacs_server", "encryption_password")
156
- end
157
+ # Get default encryption password
158
+ def self.default_encryption_password
159
+ config_get_default('tacacs_server', 'encryption_password')
160
+ end
157
161
 
158
- # Set encryption type and password
159
- def encryption_key_set(enctype, password)
160
- # if enctype is TACACS_SERVER_ENC_UNKNOWN, we will unset the key
161
- if enctype == TACACS_SERVER_ENC_UNKNOWN
162
- # if current encryption type is not TACACS_SERVER_ENC_UNKNOWN, we
163
- # need to unset it. Otherwise the box is not configured with key, we
164
- # don't need to do anything
165
- if encryption_type != TACACS_SERVER_ENC_UNKNOWN
166
- @@node.config_set("tacacs_server", "encryption", "no",
167
- encryption_type,
168
- encryption_password)
169
- end
170
- else
171
- @@node.config_set("tacacs_server", "encryption", "", enctype, password)
162
+ # Set encryption type and password
163
+ def encryption_key_set(enctype, password)
164
+ # if enctype is TACACS_SERVER_ENC_UNKNOWN, we will unset the key
165
+ if enctype == TACACS_SERVER_ENC_UNKNOWN
166
+ # if current encryption type is not TACACS_SERVER_ENC_UNKNOWN, we
167
+ # need to unset it. Otherwise the box is not configured with key, we
168
+ # don't need to do anything
169
+ if encryption_type != TACACS_SERVER_ENC_UNKNOWN
170
+ config_set('tacacs_server', 'encryption', 'no',
171
+ encryption_type,
172
+ encryption_password)
173
+ end
174
+ else
175
+ config_set('tacacs_server', 'encryption', '', enctype, password)
176
+ end
172
177
  end
173
178
  end
174
179
  end
175
- end