Statistics
| Branch: | Tag: | Revision:

root / qa / ganeti-qa.py @ a47f574c

History | View | Annotate | Download (9 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 cec9845c Michael Hanselmann
# Copyright (C) 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 cec9845c Michael Hanselmann
"""Script for doing QA on Ganeti.
23 94508060 Michael Hanselmann

24 94508060 Michael Hanselmann
You can create the required known_hosts file using ssh-keyscan. It's mandatory
25 94508060 Michael Hanselmann
to use the full name of a node (FQDN). For security reasons, verify the keys
26 94508060 Michael Hanselmann
before using them.
27 94508060 Michael Hanselmann
Example: ssh-keyscan -t rsa node{1,2,3,4}.example.com > known_hosts
28 94508060 Michael Hanselmann
"""
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
import sys
31 c68d1f43 Michael Hanselmann
import datetime
32 c68d1f43 Michael Hanselmann
import optparse
33 a8083063 Iustin Pop
34 cec9845c Michael Hanselmann
import qa_cluster
35 cec9845c Michael Hanselmann
import qa_config
36 cec9845c Michael Hanselmann
import qa_daemon
37 cec9845c Michael Hanselmann
import qa_env
38 cec9845c Michael Hanselmann
import qa_instance
39 cec9845c Michael Hanselmann
import qa_node
40 8947cf2b Michael Hanselmann
import qa_os
41 cec9845c Michael Hanselmann
import qa_other
42 a47f574c Oleksiy Mishchenko
import qa_rapi
43 d74c2ca1 Michael Hanselmann
import qa_tags
44 1672a0d1 Michael Hanselmann
import qa_utils
45 a8083063 Iustin Pop
46 a8083063 Iustin Pop
47 cec9845c Michael Hanselmann
def RunTest(fn, *args):
48 a8083063 Iustin Pop
  """Runs a test after printing a header.
49 a8083063 Iustin Pop

50 a8083063 Iustin Pop
  """
51 cec9845c Michael Hanselmann
  if fn.__doc__:
52 cec9845c Michael Hanselmann
    desc = fn.__doc__.splitlines()[0].strip()
53 a8083063 Iustin Pop
  else:
54 cec9845c Michael Hanselmann
    desc = '%r' % fn
55 a8083063 Iustin Pop
56 c68d1f43 Michael Hanselmann
  now = str(datetime.datetime.now())
57 a8083063 Iustin Pop
58 a8083063 Iustin Pop
  print
59 a8083063 Iustin Pop
  print '---', now, ('-' * (55 - len(now)))
60 a8083063 Iustin Pop
  print desc
61 a8083063 Iustin Pop
  print '-' * 60
62 a8083063 Iustin Pop
63 cec9845c Michael Hanselmann
  return fn(*args)
64 a8083063 Iustin Pop
65 a8083063 Iustin Pop
66 b1ffe1eb Michael Hanselmann
def RunEnvTests():
67 b1ffe1eb Michael Hanselmann
  """Run several environment tests.
68 a8083063 Iustin Pop

69 a8083063 Iustin Pop
  """
70 b1ffe1eb Michael Hanselmann
  if not qa_config.TestEnabled('env'):
71 b1ffe1eb Michael Hanselmann
    return
72 a8083063 Iustin Pop
73 b1ffe1eb Michael Hanselmann
  RunTest(qa_env.TestSshConnection)
74 b1ffe1eb Michael Hanselmann
  RunTest(qa_env.TestIcmpPing)
75 b1ffe1eb Michael Hanselmann
  RunTest(qa_env.TestGanetiCommands)
76 a8083063 Iustin Pop
77 94508060 Michael Hanselmann
78 b1ffe1eb Michael Hanselmann
def SetupCluster():
79 b1ffe1eb Michael Hanselmann
  """Initializes the cluster.
80 a8083063 Iustin Pop

81 b1ffe1eb Michael Hanselmann
  """
82 cec9845c Michael Hanselmann
  RunTest(qa_cluster.TestClusterInit)
83 cec9845c Michael Hanselmann
  RunTest(qa_node.TestNodeAddAll)
84 b1ffe1eb Michael Hanselmann
  if qa_config.TestEnabled('node-info'):
85 b1ffe1eb Michael Hanselmann
    RunTest(qa_node.TestNodeInfo)
86 b1ffe1eb Michael Hanselmann
87 b1ffe1eb Michael Hanselmann
88 b1ffe1eb Michael Hanselmann
def RunClusterTests():
89 b1ffe1eb Michael Hanselmann
  """Runs tests related to gnt-cluster.
90 180bdd1f Michael Hanselmann

91 b1ffe1eb Michael Hanselmann
  """
92 cec9845c Michael Hanselmann
  if qa_config.TestEnabled('cluster-verify'):
93 cec9845c Michael Hanselmann
    RunTest(qa_cluster.TestClusterVerify)
94 e9e35aaa Michael Hanselmann
95 caea3b32 Iustin Pop
  if qa_config.TestEnabled('cluster-rename'):
96 caea3b32 Iustin Pop
    RunTest(qa_cluster.TestClusterRename)
97 caea3b32 Iustin Pop
98 cec9845c Michael Hanselmann
  if qa_config.TestEnabled('cluster-info'):
99 62843684 Michael Hanselmann
    RunTest(qa_cluster.TestClusterVersion)
100 cec9845c Michael Hanselmann
    RunTest(qa_cluster.TestClusterInfo)
101 283f9d4c Michael Hanselmann
    RunTest(qa_cluster.TestClusterGetmaster)
102 283f9d4c Michael Hanselmann
103 cec9845c Michael Hanselmann
  if qa_config.TestEnabled('cluster-copyfile'):
104 cec9845c Michael Hanselmann
    RunTest(qa_cluster.TestClusterCopyfile)
105 a8083063 Iustin Pop
106 830da270 Michael Hanselmann
  if qa_config.TestEnabled('cluster-command'):
107 830da270 Michael Hanselmann
    RunTest(qa_cluster.TestClusterCommand)
108 830da270 Michael Hanselmann
109 cec9845c Michael Hanselmann
  if qa_config.TestEnabled('cluster-burnin'):
110 cec9845c Michael Hanselmann
    RunTest(qa_cluster.TestClusterBurnin)
111 a8083063 Iustin Pop
112 cec9845c Michael Hanselmann
  if qa_config.TestEnabled('cluster-master-failover'):
113 cec9845c Michael Hanselmann
    RunTest(qa_cluster.TestClusterMasterFailover)
114 a8083063 Iustin Pop
115 a47f574c Oleksiy Mishchenko
  if qa_rapi.Enabled():
116 a47f574c Oleksiy Mishchenko
    RunTest(qa_rapi.TestVersion)
117 a47f574c Oleksiy Mishchenko
    RunTest(qa_rapi.TestEmptyCluster)
118 8947cf2b Michael Hanselmann
119 b1ffe1eb Michael Hanselmann
def RunOsTests():
120 b1ffe1eb Michael Hanselmann
  """Runs all tests related to gnt-os.
121 5d640672 Michael Hanselmann

122 b1ffe1eb Michael Hanselmann
  """
123 b1ffe1eb Michael Hanselmann
  if not qa_config.TestEnabled('os'):
124 b1ffe1eb Michael Hanselmann
    return
125 b1ffe1eb Michael Hanselmann
126 b1ffe1eb Michael Hanselmann
  RunTest(qa_os.TestOsList)
127 b1ffe1eb Michael Hanselmann
  RunTest(qa_os.TestOsDiagnose)
128 b1ffe1eb Michael Hanselmann
  RunTest(qa_os.TestOsValid)
129 b1ffe1eb Michael Hanselmann
  RunTest(qa_os.TestOsInvalid)
130 b1ffe1eb Michael Hanselmann
  RunTest(qa_os.TestOsPartiallyValid)
131 b1ffe1eb Michael Hanselmann
132 b1ffe1eb Michael Hanselmann
133 b1ffe1eb Michael Hanselmann
def RunCommonInstanceTests(instance):
134 b1ffe1eb Michael Hanselmann
  """Runs a few tests that are common to all disk types.
135 b1ffe1eb Michael Hanselmann

136 b1ffe1eb Michael Hanselmann
  """
137 b1ffe1eb Michael Hanselmann
  if qa_config.TestEnabled('instance-shutdown'):
138 b1ffe1eb Michael Hanselmann
    RunTest(qa_instance.TestInstanceShutdown, instance)
139 b1ffe1eb Michael Hanselmann
    RunTest(qa_instance.TestInstanceStartup, instance)
140 a8083063 Iustin Pop
141 b1ffe1eb Michael Hanselmann
  if qa_config.TestEnabled('instance-list'):
142 b1ffe1eb Michael Hanselmann
    RunTest(qa_instance.TestInstanceList)
143 283f9d4c Michael Hanselmann
144 b1ffe1eb Michael Hanselmann
  if qa_config.TestEnabled('instance-info'):
145 b1ffe1eb Michael Hanselmann
    RunTest(qa_instance.TestInstanceInfo, instance)
146 e9e35aaa Michael Hanselmann
147 c0f74c55 Iustin Pop
  if qa_config.TestEnabled('instance-modify'):
148 c0f74c55 Iustin Pop
    RunTest(qa_instance.TestInstanceModify, instance)
149 c0f74c55 Iustin Pop
150 4379b1fa Michael Hanselmann
  if qa_config.TestEnabled('instance-console'):
151 4379b1fa Michael Hanselmann
    RunTest(qa_instance.TestInstanceConsole, instance)
152 4379b1fa Michael Hanselmann
153 b1ffe1eb Michael Hanselmann
  if qa_config.TestEnabled('instance-reinstall'):
154 b1ffe1eb Michael Hanselmann
    RunTest(qa_instance.TestInstanceShutdown, instance)
155 b1ffe1eb Michael Hanselmann
    RunTest(qa_instance.TestInstanceReinstall, instance)
156 b1ffe1eb Michael Hanselmann
    RunTest(qa_instance.TestInstanceStartup, instance)
157 a8083063 Iustin Pop
158 8a4e8898 Michael Hanselmann
  if qa_config.TestEnabled('instance-reboot'):
159 8a4e8898 Michael Hanselmann
    RunTest(qa_instance.TestInstanceReboot, instance)
160 8a4e8898 Michael Hanselmann
161 d74c2ca1 Michael Hanselmann
  if qa_config.TestEnabled('tags'):
162 d74c2ca1 Michael Hanselmann
    RunTest(qa_tags.TestInstanceTags, instance)
163 d74c2ca1 Michael Hanselmann
164 b1ffe1eb Michael Hanselmann
  if qa_config.TestEnabled('node-volumes'):
165 b1ffe1eb Michael Hanselmann
    RunTest(qa_node.TestNodeVolumes)
166 23269c5b Michael Hanselmann
167 a47f574c Oleksiy Mishchenko
  if qa_rapi.Enabled():
168 a47f574c Oleksiy Mishchenko
    RunTest(qa_rapi.TestInstance, instance)
169 23269c5b Michael Hanselmann
170 b1ffe1eb Michael Hanselmann
def RunExportImportTests(instance, pnode):
171 b1ffe1eb Michael Hanselmann
  """Tries to export and import the instance.
172 a8083063 Iustin Pop

173 b1ffe1eb Michael Hanselmann
  """
174 b1ffe1eb Michael Hanselmann
  if qa_config.TestEnabled('instance-export'):
175 b1ffe1eb Michael Hanselmann
    expnode = qa_config.AcquireNode(exclude=pnode)
176 b1ffe1eb Michael Hanselmann
    try:
177 b1ffe1eb Michael Hanselmann
      name = RunTest(qa_instance.TestInstanceExport, instance, expnode)
178 b1ffe1eb Michael Hanselmann
179 b1ffe1eb Michael Hanselmann
      RunTest(qa_instance.TestBackupList, expnode)
180 b1ffe1eb Michael Hanselmann
181 b1ffe1eb Michael Hanselmann
      if qa_config.TestEnabled('instance-import'):
182 b1ffe1eb Michael Hanselmann
        newinst = qa_config.AcquireInstance()
183 5d640672 Michael Hanselmann
        try:
184 b1ffe1eb Michael Hanselmann
          RunTest(qa_instance.TestInstanceImport, pnode, newinst,
185 b1ffe1eb Michael Hanselmann
                  expnode, name)
186 b1ffe1eb Michael Hanselmann
          RunTest(qa_instance.TestInstanceRemove, newinst)
187 5d640672 Michael Hanselmann
        finally:
188 b1ffe1eb Michael Hanselmann
          qa_config.ReleaseInstance(newinst)
189 b1ffe1eb Michael Hanselmann
    finally:
190 b1ffe1eb Michael Hanselmann
      qa_config.ReleaseNode(expnode)
191 5d640672 Michael Hanselmann
192 283f9d4c Michael Hanselmann
193 b1ffe1eb Michael Hanselmann
def RunDaemonTests(instance, pnode):
194 b1ffe1eb Michael Hanselmann
  """Test the ganeti-watcher script.
195 9df6d173 Michael Hanselmann

196 b1ffe1eb Michael Hanselmann
  """
197 b1ffe1eb Michael Hanselmann
  automatic_restart = \
198 b1ffe1eb Michael Hanselmann
    qa_config.TestEnabled('instance-automatic-restart')
199 b1ffe1eb Michael Hanselmann
  consecutive_failures = \
200 b1ffe1eb Michael Hanselmann
    qa_config.TestEnabled('instance-consecutive-failures')
201 a8083063 Iustin Pop
202 b1ffe1eb Michael Hanselmann
  if automatic_restart or consecutive_failures:
203 b1ffe1eb Michael Hanselmann
    qa_daemon.PrintCronWarning()
204 5d640672 Michael Hanselmann
205 b1ffe1eb Michael Hanselmann
    if automatic_restart:
206 b1ffe1eb Michael Hanselmann
      RunTest(qa_daemon.TestInstanceAutomaticRestart, pnode, instance)
207 e9e35aaa Michael Hanselmann
208 b1ffe1eb Michael Hanselmann
    if consecutive_failures:
209 6623a5c1 Michael Hanselmann
      RunTest(qa_daemon.TestInstanceConsecutiveFailures, pnode, instance)
210 e9e35aaa Michael Hanselmann
211 9df6d173 Michael Hanselmann
212 b1ffe1eb Michael Hanselmann
def RunHardwareFailureTests(instance, pnode, snode):
213 b1ffe1eb Michael Hanselmann
  """Test cluster internal hardware failure recovery.
214 a8083063 Iustin Pop

215 b1ffe1eb Michael Hanselmann
  """
216 b1ffe1eb Michael Hanselmann
  if qa_config.TestEnabled('instance-failover'):
217 b1ffe1eb Michael Hanselmann
    RunTest(qa_instance.TestInstanceFailover, instance)
218 b1ffe1eb Michael Hanselmann
219 7910e7a5 Michael Hanselmann
  if qa_config.TestEnabled('instance-replace-disks'):
220 76f59a32 Michael Hanselmann
    othernode = qa_config.AcquireNode(exclude=[pnode, snode])
221 7910e7a5 Michael Hanselmann
    try:
222 7910e7a5 Michael Hanselmann
      RunTest(qa_instance.TestReplaceDisks,
223 7910e7a5 Michael Hanselmann
              instance, pnode, snode, othernode)
224 7910e7a5 Michael Hanselmann
    finally:
225 7910e7a5 Michael Hanselmann
      qa_config.ReleaseNode(othernode)
226 7910e7a5 Michael Hanselmann
227 b1ffe1eb Michael Hanselmann
  if qa_config.TestEnabled('node-evacuate'):
228 b1ffe1eb Michael Hanselmann
    RunTest(qa_node.TestNodeEvacuate, pnode, snode)
229 b1ffe1eb Michael Hanselmann
230 b1ffe1eb Michael Hanselmann
  if qa_config.TestEnabled('node-failover'):
231 b1ffe1eb Michael Hanselmann
    RunTest(qa_node.TestNodeFailover, pnode, snode)
232 b1ffe1eb Michael Hanselmann
233 b1ffe1eb Michael Hanselmann
  if qa_config.TestEnabled('instance-disk-failure'):
234 b1ffe1eb Michael Hanselmann
    RunTest(qa_instance.TestInstanceMasterDiskFailure,
235 b1ffe1eb Michael Hanselmann
            instance, pnode, snode)
236 b1ffe1eb Michael Hanselmann
    RunTest(qa_instance.TestInstanceSecondaryDiskFailure,
237 b1ffe1eb Michael Hanselmann
            instance, pnode, snode)
238 b1ffe1eb Michael Hanselmann
239 b1ffe1eb Michael Hanselmann
240 b1ffe1eb Michael Hanselmann
def main():
241 b1ffe1eb Michael Hanselmann
  """Main program.
242 b1ffe1eb Michael Hanselmann

243 b1ffe1eb Michael Hanselmann
  """
244 c68d1f43 Michael Hanselmann
  parser = optparse.OptionParser(usage="%prog [options] <config-file>"
245 c68d1f43 Michael Hanselmann
                                       " <known-hosts-file>")
246 b1ffe1eb Michael Hanselmann
  parser.add_option('--dry-run', dest='dry_run',
247 b1ffe1eb Michael Hanselmann
      action="store_true",
248 b1ffe1eb Michael Hanselmann
      help="Show what would be done")
249 b1ffe1eb Michael Hanselmann
  parser.add_option('--yes-do-it', dest='yes_do_it',
250 b1ffe1eb Michael Hanselmann
      action="store_true",
251 b1ffe1eb Michael Hanselmann
      help="Really execute the tests")
252 b1ffe1eb Michael Hanselmann
  (qa_config.options, args) = parser.parse_args()
253 5d640672 Michael Hanselmann
254 b1ffe1eb Michael Hanselmann
  if len(args) == 2:
255 b1ffe1eb Michael Hanselmann
    (config_file, known_hosts_file) = args
256 b1ffe1eb Michael Hanselmann
  else:
257 b1ffe1eb Michael Hanselmann
    parser.error("Not enough arguments.")
258 a8083063 Iustin Pop
259 b1ffe1eb Michael Hanselmann
  if not qa_config.options.yes_do_it:
260 b1ffe1eb Michael Hanselmann
    print ("Executing this script irreversibly destroys any Ganeti\n"
261 b1ffe1eb Michael Hanselmann
           "configuration on all nodes involved. If you really want\n"
262 b1ffe1eb Michael Hanselmann
           "to start testing, supply the --yes-do-it option.")
263 b1ffe1eb Michael Hanselmann
    sys.exit(1)
264 e9e35aaa Michael Hanselmann
265 b1ffe1eb Michael Hanselmann
  qa_config.Load(config_file)
266 1672a0d1 Michael Hanselmann
  qa_utils.LoadHooks()
267 a8083063 Iustin Pop
268 b1ffe1eb Michael Hanselmann
  RunTest(qa_other.UploadKnownHostsFile, known_hosts_file)
269 4b62db14 Michael Hanselmann
270 b1ffe1eb Michael Hanselmann
  RunEnvTests()
271 b1ffe1eb Michael Hanselmann
  SetupCluster()
272 b1ffe1eb Michael Hanselmann
  RunClusterTests()
273 b1ffe1eb Michael Hanselmann
  RunOsTests()
274 4b62db14 Michael Hanselmann
275 d74c2ca1 Michael Hanselmann
  if qa_config.TestEnabled('tags'):
276 d74c2ca1 Michael Hanselmann
    RunTest(qa_tags.TestClusterTags)
277 d74c2ca1 Michael Hanselmann
278 102b115b Michael Hanselmann
  if qa_config.TestEnabled('node-readd'):
279 102b115b Michael Hanselmann
    master = qa_config.GetMasterNode()
280 102b115b Michael Hanselmann
    pnode = qa_config.AcquireNode(exclude=master)
281 102b115b Michael Hanselmann
    try:
282 102b115b Michael Hanselmann
      RunTest(qa_node.TestNodeReadd, pnode)
283 102b115b Michael Hanselmann
    finally:
284 102b115b Michael Hanselmann
      qa_config.ReleaseNode(pnode)
285 102b115b Michael Hanselmann
286 b1ffe1eb Michael Hanselmann
  pnode = qa_config.AcquireNode()
287 b1ffe1eb Michael Hanselmann
  try:
288 d74c2ca1 Michael Hanselmann
    if qa_config.TestEnabled('tags'):
289 d74c2ca1 Michael Hanselmann
      RunTest(qa_tags.TestNodeTags, pnode)
290 d74c2ca1 Michael Hanselmann
291 a47f574c Oleksiy Mishchenko
    if qa_rapi.Enabled():
292 a47f574c Oleksiy Mishchenko
      RunTest(qa_rapi.TestNode, pnode)
293 a47f574c Oleksiy Mishchenko
294 b1ffe1eb Michael Hanselmann
    if qa_config.TestEnabled('instance-add-plain-disk'):
295 b1ffe1eb Michael Hanselmann
      instance = RunTest(qa_instance.TestInstanceAddWithPlainDisk, pnode)
296 b1ffe1eb Michael Hanselmann
      RunCommonInstanceTests(instance)
297 b1ffe1eb Michael Hanselmann
      RunExportImportTests(instance, pnode)
298 b1ffe1eb Michael Hanselmann
      RunDaemonTests(instance, pnode)
299 b1ffe1eb Michael Hanselmann
      RunTest(qa_instance.TestInstanceRemove, instance)
300 b1ffe1eb Michael Hanselmann
      del instance
301 9df6d173 Michael Hanselmann
302 7d7609a3 Michael Hanselmann
    multinode_tests = [
303 7d7609a3 Michael Hanselmann
      ('instance-add-drbd-disk',
304 7d7609a3 Michael Hanselmann
       qa_instance.TestInstanceAddWithDrbdDisk),
305 7d7609a3 Michael Hanselmann
    ]
306 7d7609a3 Michael Hanselmann
307 7d7609a3 Michael Hanselmann
    for name, func in multinode_tests:
308 7d7609a3 Michael Hanselmann
      if qa_config.TestEnabled(name):
309 7d7609a3 Michael Hanselmann
        snode = qa_config.AcquireNode(exclude=pnode)
310 7d7609a3 Michael Hanselmann
        try:
311 7d7609a3 Michael Hanselmann
          instance = RunTest(func, pnode, snode)
312 7d7609a3 Michael Hanselmann
          RunCommonInstanceTests(instance)
313 7d7609a3 Michael Hanselmann
          RunExportImportTests(instance, pnode)
314 7d7609a3 Michael Hanselmann
          RunHardwareFailureTests(instance, pnode, snode)
315 7d7609a3 Michael Hanselmann
          RunTest(qa_instance.TestInstanceRemove, instance)
316 7d7609a3 Michael Hanselmann
          del instance
317 7d7609a3 Michael Hanselmann
        finally:
318 7d7609a3 Michael Hanselmann
          qa_config.ReleaseNode(snode)
319 a8083063 Iustin Pop
320 a8083063 Iustin Pop
  finally:
321 b1ffe1eb Michael Hanselmann
    qa_config.ReleaseNode(pnode)
322 a8083063 Iustin Pop
323 cec9845c Michael Hanselmann
  RunTest(qa_node.TestNodeRemoveAll)
324 a8083063 Iustin Pop
325 cec9845c Michael Hanselmann
  if qa_config.TestEnabled('cluster-destroy'):
326 cec9845c Michael Hanselmann
    RunTest(qa_cluster.TestClusterDestroy)
327 a8083063 Iustin Pop
328 cec9845c Michael Hanselmann
329 cec9845c Michael Hanselmann
if __name__ == '__main__':
330 cec9845c Michael Hanselmann
  main()