vagrant-salt 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rst +131 -170
- data/example/complete/Vagrantfile +67 -0
- data/example/complete/salt/custom-bootstrap-salt.sh +2425 -0
- data/example/complete/salt/key/master.pem +30 -0
- data/example/complete/salt/key/master.pub +14 -0
- data/example/complete/salt/key/minion.pem +30 -0
- data/example/complete/salt/key/minion.pub +14 -0
- data/example/complete/salt/master +459 -0
- data/example/{salt/minion.conf → complete/salt/minion} +1 -2
- data/example/{salt → complete/salt}/roots/pillar/top.sls +0 -0
- data/example/complete/salt/roots/salt/nginx.sls +5 -0
- data/example/complete/salt/roots/salt/top.sls +3 -0
- data/example/masterless/Vagrantfile +18 -0
- data/example/masterless/salt/minion +219 -0
- data/example/{salt/roots/salt → masterless/salt/roots/pillar}/top.sls +0 -0
- data/example/masterless/salt/roots/salt/nginx.sls +5 -0
- data/example/masterless/salt/roots/salt/top.sls +3 -0
- data/lib/vagrant-salt.rb +16 -3
- data/lib/vagrant-salt/config.rb +103 -0
- data/lib/vagrant-salt/errors.rb +11 -0
- data/lib/vagrant-salt/plugin.rb +31 -0
- data/lib/vagrant-salt/provisioner.rb +211 -104
- data/lib/vagrant-salt/version.rb +5 -0
- data/scripts/.travis.yml +16 -0
- data/scripts/ChangeLog +39 -0
- data/scripts/LICENSE +16 -0
- data/scripts/README.rst +124 -35
- data/scripts/bootstrap-salt-minion.sh +1815 -381
- data/scripts/bootstrap-salt.sh +2425 -0
- data/scripts/salt-bootstrap.sh +2425 -0
- data/scripts/tests/README.rst +38 -0
- data/scripts/tests/bootstrap/__init__.py +11 -0
- data/{example/salt/key/KEYPAIR_GOES_HERE → scripts/tests/bootstrap/ext/__init__.py} +0 -0
- data/scripts/tests/bootstrap/ext/console.py +100 -0
- data/scripts/tests/bootstrap/ext/os_data.py +199 -0
- data/scripts/tests/bootstrap/test_install.py +586 -0
- data/scripts/tests/bootstrap/test_lint.py +27 -0
- data/scripts/tests/bootstrap/test_usage.py +28 -0
- data/scripts/tests/bootstrap/unittesting.py +216 -0
- data/scripts/tests/ext/checkbashisms +640 -0
- data/scripts/tests/install-testsuite-deps.py +99 -0
- data/scripts/tests/runtests.py +207 -0
- data/templates/locales/en.yml +14 -0
- data/vagrant-salt.gemspec +2 -2
- metadata +43 -10
- data/example/Vagrantfile +0 -26
- data/lib/vagrant_init.rb +0 -1
@@ -0,0 +1,99 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
'''
|
4
|
+
install-testsuite-deps.py
|
5
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~
|
6
|
+
|
7
|
+
Install the required dependencies to properly run the test-suite.
|
8
|
+
|
9
|
+
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
|
10
|
+
:copyright: © 2013 by the SaltStack Team, see AUTHORS for more details.
|
11
|
+
:license: Apache 2.0, see LICENSE for more details.
|
12
|
+
'''
|
13
|
+
|
14
|
+
# Import python libs
|
15
|
+
import re
|
16
|
+
import sys
|
17
|
+
import pprint
|
18
|
+
import subprocess
|
19
|
+
|
20
|
+
# Import bootstrap libs
|
21
|
+
from bootstrap.ext.os_data import GRAINS
|
22
|
+
|
23
|
+
|
24
|
+
COMMANDS = []
|
25
|
+
if GRAINS['os'] == 'SmartOS':
|
26
|
+
COMMANDS.extend([
|
27
|
+
'pkgin up',
|
28
|
+
'pkgin -y in scmgit-base py27-pip',
|
29
|
+
'pip install unittest2'
|
30
|
+
])
|
31
|
+
elif GRAINS['os'] == 'openSUSE':
|
32
|
+
COMMANDS.extend([
|
33
|
+
'zypper --non-interactive addrepo --refresh http://download.opensuse.org/repositories'
|
34
|
+
'/devel:/languages:/python/{0}/devel:languages:python.repo'.format(
|
35
|
+
GRAINS['osrelease']
|
36
|
+
),
|
37
|
+
'zypper --gpg-auto-import-keys --non-interactive refresh',
|
38
|
+
'zypper --non-interactive install --auto-agree-with-licenses git python-pip',
|
39
|
+
'pip install unittest2'
|
40
|
+
])
|
41
|
+
elif GRAINS['osfullname'].startswith('SUSE Linux Enterprise Server'):
|
42
|
+
match = re.search(
|
43
|
+
r'PATCHLEVEL(?:[\s]+)=(?:[\s]+)([0-9]+)',
|
44
|
+
open('/etc/SuSE-release').read()
|
45
|
+
)
|
46
|
+
#if not match:
|
47
|
+
# print 'Failed to get the current patch level for:\n{0}'.format(
|
48
|
+
# pprint.pformat(GRAINS)
|
49
|
+
# )
|
50
|
+
COMMANDS.extend([
|
51
|
+
'zypper --non-interactive addrepo --refresh http://download.opensuse.org/repositories'
|
52
|
+
'/devel:/languages:/python/SLE_{0}{1}/devel:languages:python.repo'.format(
|
53
|
+
GRAINS['osrelease'],
|
54
|
+
match and '_SP{0}'.format(match.group(1)) or ''
|
55
|
+
),
|
56
|
+
'zypper --gpg-auto-import-keys --non-interactive refresh',
|
57
|
+
'zypper --non-interactive install --auto-agree-with-licenses git python-pip',
|
58
|
+
'pip install unittest2'
|
59
|
+
])
|
60
|
+
elif GRAINS['os'] == 'Amazon':
|
61
|
+
COMMANDS.extend([
|
62
|
+
'rpm -Uvh --force http://mirrors.kernel.org/fedora-epel/6/'
|
63
|
+
'{0}/epel-release-6-8.noarch.rpm'.format(
|
64
|
+
GRAINS['cpuarch'] == 'i686' and 'i386' or GRAINS['cpuarch']
|
65
|
+
),
|
66
|
+
'yum -y update',
|
67
|
+
'yum -y install python-pip --enablerepo=epel-testing',
|
68
|
+
'pip-python install unittest2'
|
69
|
+
])
|
70
|
+
elif GRAINS['os'] == 'Fedora':
|
71
|
+
COMMANDS.extend([
|
72
|
+
'yum -y update',
|
73
|
+
'yum -y install python-pip',
|
74
|
+
'pip-python install unittest2'
|
75
|
+
])
|
76
|
+
elif GRAINS['os_family'] == 'Debian':
|
77
|
+
COMMANDS.extend([
|
78
|
+
'apt-get update',
|
79
|
+
'apt-get install -y -o DPkg::Options::=--force-confold '
|
80
|
+
'-o Dpkg::Options::="--force-confdef" python-pip',
|
81
|
+
'pip install unittest2'
|
82
|
+
])
|
83
|
+
else:
|
84
|
+
print(
|
85
|
+
'Failed gather the proper commands to allow the tests suite to be '
|
86
|
+
'executed in this system.\nSystem Grains:\n{0}'.format(
|
87
|
+
pprint.pformat(GRAINS)
|
88
|
+
)
|
89
|
+
)
|
90
|
+
sys.exit(1)
|
91
|
+
|
92
|
+
|
93
|
+
for command in COMMANDS:
|
94
|
+
print 'Executing {0!r}'.format(command)
|
95
|
+
process = subprocess.Popen(command, shell=True)
|
96
|
+
process.communicate()
|
97
|
+
|
98
|
+
print('\nDONE\n')
|
99
|
+
exit(0)
|
@@ -0,0 +1,207 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
'''
|
4
|
+
test-bootstrap.py
|
5
|
+
~~~~~~~~~~~~~~~~~
|
6
|
+
|
7
|
+
salt-bootstrap script unit-testing
|
8
|
+
|
9
|
+
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
|
10
|
+
:copyright: © 2013 by the SaltStack Team, see AUTHORS for more details.
|
11
|
+
:license: Apache 2.0, see LICENSE for more details.
|
12
|
+
'''
|
13
|
+
|
14
|
+
import os
|
15
|
+
import pprint
|
16
|
+
import shutil
|
17
|
+
import optparse
|
18
|
+
|
19
|
+
from bootstrap.unittesting import TestLoader, TextTestRunner
|
20
|
+
from bootstrap.ext.os_data import GRAINS
|
21
|
+
try:
|
22
|
+
from bootstrap.ext import console
|
23
|
+
width, height = console.getTerminalSize()
|
24
|
+
PNUM = width
|
25
|
+
except:
|
26
|
+
PNUM = 70
|
27
|
+
|
28
|
+
try:
|
29
|
+
import xmlrunner
|
30
|
+
except ImportError:
|
31
|
+
xmlrunner = None
|
32
|
+
|
33
|
+
|
34
|
+
TEST_DIR = os.path.abspath(os.path.dirname(__file__))
|
35
|
+
XML_OUTPUT_DIR = os.environ.get(
|
36
|
+
'XML_TEST_REPORTS', os.path.join(TEST_DIR, 'xml-test-reports')
|
37
|
+
)
|
38
|
+
|
39
|
+
|
40
|
+
def print_header(header, sep='~', top=True, bottom=True, inline=False,
|
41
|
+
centered=False):
|
42
|
+
'''
|
43
|
+
Allows some pretty printing of headers on the console, either with a
|
44
|
+
"ruler" on bottom and/or top, inline, centered, etc.
|
45
|
+
'''
|
46
|
+
if top and not inline:
|
47
|
+
print(sep * PNUM)
|
48
|
+
|
49
|
+
if centered and not inline:
|
50
|
+
fmt = u'{0:^{width}}'
|
51
|
+
elif inline and not centered:
|
52
|
+
fmt = u'{0:{sep}<{width}}'
|
53
|
+
elif inline and centered:
|
54
|
+
fmt = u'{0:{sep}^{width}}'
|
55
|
+
else:
|
56
|
+
fmt = u'{0}'
|
57
|
+
print(fmt.format(header, sep=sep, width=PNUM))
|
58
|
+
|
59
|
+
if bottom and not inline:
|
60
|
+
print(sep * PNUM)
|
61
|
+
|
62
|
+
|
63
|
+
def run_suite(opts, path, display_name, suffix='[!_]*.py'):
|
64
|
+
'''
|
65
|
+
Execute a unit test suite
|
66
|
+
'''
|
67
|
+
loader = TestLoader()
|
68
|
+
if opts.name:
|
69
|
+
tests = tests = loader.loadTestsFromName(display_name)
|
70
|
+
else:
|
71
|
+
tests = loader.discover(path, suffix, TEST_DIR)
|
72
|
+
|
73
|
+
header = '{0} Tests'.format(display_name)
|
74
|
+
print_header('Starting {0}'.format(header))
|
75
|
+
|
76
|
+
if opts.xmlout:
|
77
|
+
if not os.path.isdir(XML_OUTPUT_DIR):
|
78
|
+
os.makedirs(XML_OUTPUT_DIR)
|
79
|
+
runner = xmlrunner.XMLTestRunner(
|
80
|
+
output=XML_OUTPUT_DIR,
|
81
|
+
verbosity=opts.verbosity
|
82
|
+
).run(tests)
|
83
|
+
else:
|
84
|
+
runner = TextTestRunner(
|
85
|
+
verbosity=opts.verbosity
|
86
|
+
).run(tests)
|
87
|
+
return runner.wasSuccessful()
|
88
|
+
|
89
|
+
|
90
|
+
def run_integration_suite(opts, display_name, suffix='[!_]*.py'):
|
91
|
+
'''
|
92
|
+
Run an integration test suite
|
93
|
+
'''
|
94
|
+
path = os.path.join(TEST_DIR, 'bootstrap')
|
95
|
+
return run_suite(opts, path, display_name, suffix)
|
96
|
+
|
97
|
+
|
98
|
+
def main():
|
99
|
+
parser = optparse.OptionParser()
|
100
|
+
|
101
|
+
test_selection_group = optparse.OptionGroup(
|
102
|
+
parser,
|
103
|
+
"Tests Selection",
|
104
|
+
"In case of no selection, all tests will be executed."
|
105
|
+
)
|
106
|
+
test_selection_group.add_option(
|
107
|
+
'-L', '--lint',
|
108
|
+
default=False,
|
109
|
+
action='store_true',
|
110
|
+
help='Run Lint tests'
|
111
|
+
)
|
112
|
+
test_selection_group.add_option(
|
113
|
+
'-U', '--usage',
|
114
|
+
default=False,
|
115
|
+
action='store_true',
|
116
|
+
help='Run Usage tests'
|
117
|
+
)
|
118
|
+
test_selection_group.add_option(
|
119
|
+
'-I', '--install',
|
120
|
+
default=False,
|
121
|
+
action='store_true',
|
122
|
+
help='Run Installation tests'
|
123
|
+
)
|
124
|
+
test_selection_group.add_option(
|
125
|
+
'-n', '--name',
|
126
|
+
action='append',
|
127
|
+
default=[],
|
128
|
+
help='Specific test to run'
|
129
|
+
)
|
130
|
+
parser.add_option_group(test_selection_group)
|
131
|
+
|
132
|
+
output_options_group = optparse.OptionGroup(parser, "Output Options")
|
133
|
+
output_options_group.add_option(
|
134
|
+
'-v',
|
135
|
+
'--verbose',
|
136
|
+
dest='verbosity',
|
137
|
+
default=1,
|
138
|
+
action='count',
|
139
|
+
help='Verbose test runner output'
|
140
|
+
)
|
141
|
+
output_options_group.add_option(
|
142
|
+
'-x',
|
143
|
+
'--xml',
|
144
|
+
dest='xmlout',
|
145
|
+
default=False,
|
146
|
+
action='store_true',
|
147
|
+
help='XML test runner output(Output directory: {0})'.format(
|
148
|
+
XML_OUTPUT_DIR
|
149
|
+
)
|
150
|
+
)
|
151
|
+
output_options_group.add_option(
|
152
|
+
'--no-clean',
|
153
|
+
default=False,
|
154
|
+
action='store_true',
|
155
|
+
help='Do not clean the XML output files before running.'
|
156
|
+
)
|
157
|
+
parser.add_option_group(output_options_group)
|
158
|
+
|
159
|
+
options, _ = parser.parse_args()
|
160
|
+
|
161
|
+
if options.xmlout and xmlrunner is None:
|
162
|
+
parser.error(
|
163
|
+
'\'--xml\' is not available. The xmlrunner library is not '
|
164
|
+
'installed.'
|
165
|
+
)
|
166
|
+
elif options.xmlout:
|
167
|
+
print(
|
168
|
+
'Generated XML reports will be stored on {0!r}'.format(
|
169
|
+
XML_OUTPUT_DIR
|
170
|
+
)
|
171
|
+
)
|
172
|
+
|
173
|
+
if not any((options.lint, options.usage, options.install, options.name)):
|
174
|
+
options.lint = True
|
175
|
+
options.usage = True
|
176
|
+
options.install = True
|
177
|
+
|
178
|
+
if not options.no_clean and os.path.isdir(XML_OUTPUT_DIR):
|
179
|
+
shutil.rmtree(XML_OUTPUT_DIR)
|
180
|
+
|
181
|
+
print 'Detected system grains:'
|
182
|
+
pprint.pprint(GRAINS)
|
183
|
+
|
184
|
+
overall_status = []
|
185
|
+
|
186
|
+
if options.name:
|
187
|
+
for name in options.name:
|
188
|
+
results = run_suite(options, '', name)
|
189
|
+
overall_status.append(results)
|
190
|
+
if options.lint:
|
191
|
+
status = run_integration_suite(options, 'Lint', "*lint.py")
|
192
|
+
overall_status.append(status)
|
193
|
+
if options.usage:
|
194
|
+
status = run_integration_suite(options, 'Usage', "*usage.py")
|
195
|
+
overall_status.append(status)
|
196
|
+
if options.install:
|
197
|
+
status = run_integration_suite(options, 'Installation', "*install.py")
|
198
|
+
overall_status.append(status)
|
199
|
+
|
200
|
+
if overall_status.count(False) > 0:
|
201
|
+
# We have some false results, the test-suite failed
|
202
|
+
parser.exit(1)
|
203
|
+
|
204
|
+
parser.exit(0)
|
205
|
+
|
206
|
+
if __name__ == '__main__':
|
207
|
+
main()
|
@@ -0,0 +1,14 @@
|
|
1
|
+
en:
|
2
|
+
salt:
|
3
|
+
missing_key: |-
|
4
|
+
You must include both public and private keys.
|
5
|
+
must_accept_keys: |-
|
6
|
+
You must accept keys when running highstate with master!
|
7
|
+
accept_key_no_minion: |-
|
8
|
+
You must install the minion to accept keys on master!
|
9
|
+
accept_key_no_master: |-
|
10
|
+
You must install the master to accept minion keys!
|
11
|
+
not_received_minion_key: |-
|
12
|
+
Salt Master did not receive minion key.
|
13
|
+
bootstrap_failed: |-
|
14
|
+
Bootstrap script failed, see /var/log/bootstrap-salt.log on VM.
|
data/vagrant-salt.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "vagrant-salt"
|
6
|
-
s.version = "0.
|
6
|
+
s.version = "0.4.0"
|
7
7
|
s.authors = ["Alec Koumjian", "Kiall Mac Innes", "Pedro Algarvio"]
|
8
8
|
s.email = ["akoumjian@gmail.com", "kiall@managedit.ie", "pedro@algarvio.me"]
|
9
9
|
s.homepage = "https://github.com/saltstack/salty-vagrant"
|
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.files = `git ls-files`.split("\n")
|
14
14
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
15
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
-
s.require_paths = ["lib"]
|
16
|
+
s.require_paths = ["lib", "templates"]
|
17
17
|
|
18
18
|
s.add_runtime_dependency "vagrant"
|
19
19
|
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 4
|
8
|
+
- 0
|
9
|
+
version: 0.4.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Alec Koumjian
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2013-
|
19
|
+
date: 2013-03-25 00:00:00 -07:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -49,17 +49,49 @@ files:
|
|
49
49
|
- README.rst
|
50
50
|
- Rakefile
|
51
51
|
- example/.gitignore
|
52
|
-
- example/Vagrantfile
|
53
|
-
- example/salt/
|
54
|
-
- example/salt/
|
55
|
-
- example/salt/
|
56
|
-
- example/salt/
|
52
|
+
- example/complete/Vagrantfile
|
53
|
+
- example/complete/salt/custom-bootstrap-salt.sh
|
54
|
+
- example/complete/salt/key/master.pem
|
55
|
+
- example/complete/salt/key/master.pub
|
56
|
+
- example/complete/salt/key/minion.pem
|
57
|
+
- example/complete/salt/key/minion.pub
|
58
|
+
- example/complete/salt/master
|
59
|
+
- example/complete/salt/minion
|
60
|
+
- example/complete/salt/roots/pillar/top.sls
|
61
|
+
- example/complete/salt/roots/salt/nginx.sls
|
62
|
+
- example/complete/salt/roots/salt/top.sls
|
63
|
+
- example/masterless/Vagrantfile
|
64
|
+
- example/masterless/salt/minion
|
65
|
+
- example/masterless/salt/roots/pillar/top.sls
|
66
|
+
- example/masterless/salt/roots/salt/nginx.sls
|
67
|
+
- example/masterless/salt/roots/salt/top.sls
|
57
68
|
- lib/vagrant-salt.rb
|
69
|
+
- lib/vagrant-salt/config.rb
|
70
|
+
- lib/vagrant-salt/errors.rb
|
71
|
+
- lib/vagrant-salt/plugin.rb
|
58
72
|
- lib/vagrant-salt/provisioner.rb
|
59
|
-
- lib/
|
73
|
+
- lib/vagrant-salt/version.rb
|
74
|
+
- templates/locales/en.yml
|
60
75
|
- vagrant-salt.gemspec
|
76
|
+
- scripts/.travis.yml
|
77
|
+
- scripts/ChangeLog
|
78
|
+
- scripts/LICENSE
|
61
79
|
- scripts/README.rst
|
62
80
|
- scripts/bootstrap-salt-minion.sh
|
81
|
+
- scripts/bootstrap-salt.sh
|
82
|
+
- scripts/salt-bootstrap.sh
|
83
|
+
- scripts/tests/README.rst
|
84
|
+
- scripts/tests/bootstrap/__init__.py
|
85
|
+
- scripts/tests/bootstrap/ext/__init__.py
|
86
|
+
- scripts/tests/bootstrap/ext/console.py
|
87
|
+
- scripts/tests/bootstrap/ext/os_data.py
|
88
|
+
- scripts/tests/bootstrap/test_install.py
|
89
|
+
- scripts/tests/bootstrap/test_lint.py
|
90
|
+
- scripts/tests/bootstrap/test_usage.py
|
91
|
+
- scripts/tests/bootstrap/unittesting.py
|
92
|
+
- scripts/tests/ext/checkbashisms
|
93
|
+
- scripts/tests/install-testsuite-deps.py
|
94
|
+
- scripts/tests/runtests.py
|
63
95
|
has_rdoc: true
|
64
96
|
homepage: https://github.com/saltstack/salty-vagrant
|
65
97
|
licenses: []
|
@@ -69,6 +101,7 @@ rdoc_options: []
|
|
69
101
|
|
70
102
|
require_paths:
|
71
103
|
- lib
|
104
|
+
- templates
|
72
105
|
required_ruby_version: !ruby/object:Gem::Requirement
|
73
106
|
requirements:
|
74
107
|
- - ">="
|
data/example/Vagrantfile
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
# require "../lib/vagrant-salt"
|
2
|
-
|
3
|
-
Vagrant::Config.run do |config|
|
4
|
-
## Chose your base box
|
5
|
-
config.vm.box = "precise64"
|
6
|
-
|
7
|
-
## For masterless, mount your salt file root
|
8
|
-
config.vm.share_folder "salt_file_root", "/srv", "salt/roots/"
|
9
|
-
|
10
|
-
|
11
|
-
## Use all the defaults:
|
12
|
-
config.vm.provision :salt do |salt|
|
13
|
-
salt.run_highstate = false
|
14
|
-
|
15
|
-
## Optional Settings:
|
16
|
-
# salt.minion_config = "salt/minion.conf"
|
17
|
-
# salt.temp_config_dir = "/existing/folder/on/basebox/"
|
18
|
-
# salt.salt_install_type = "git"
|
19
|
-
# salt.salt_install_args = "develop"
|
20
|
-
|
21
|
-
## If you have a remote master setup, you can add
|
22
|
-
## your preseeded minion key
|
23
|
-
# salt.minion_key = "salt/key/minion.pem"
|
24
|
-
# salt.minion_pub = "salt/key/minion.pub"
|
25
|
-
end
|
26
|
-
end
|