Tools used by OpenStack Documentation
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

773 lines
28KB

  1. #!/usr/bin/env python
  2. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  3. # not use this file except in compliance with the License. You may obtain
  4. # a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software
  9. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  10. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  11. # License for the specific language governing permissions and limitations
  12. # under the License.
  13. import argparse
  14. import os
  15. import subprocess
  16. import sys
  17. import yaml
  18. import os_doc_tools
  19. DEVNULL = open(os.devnull, 'wb')
  20. MAXLINELENGTH = 78
  21. def use_help_flag(os_command):
  22. """Use --help flag (instead of help keyword)
  23. Returns true if the command requires a --help flag instead
  24. of a help keyword.
  25. """
  26. return os_command == "swift" or "-manage" in os_command
  27. def quote_rst(line):
  28. """Convert special characters for RST output."""
  29. line = line.replace('\\', '\\\\').replace('`', '\\`').replace('*', '\\*')
  30. if 'DEPRECATED!' in line:
  31. line = line.replace('DEPRECATED!', '**DEPRECATED!**')
  32. elif 'DEPRECATED' in line:
  33. line = line.replace('DEPRECATED', '**DEPRECATED**')
  34. if 'env[' in line:
  35. line = line.replace('env[', '``env[').replace(']', ']``')
  36. # work around for "Default=env[...]" at cinder
  37. line = line.replace('=``', '= ``')
  38. return line
  39. def generate_heading(os_command, api_name, title,
  40. output_dir, os_filename, continue_on_error):
  41. """Write RST file header.
  42. :param os_command: client command to document
  43. :param api_name: string description of the API of os_command
  44. :param output_dir: directory to write output file to
  45. :param os_filename: name to create current output file as
  46. :param continue_on_error: continue even if there's an error
  47. """
  48. try:
  49. version = subprocess.check_output([os_command, "--version"],
  50. universal_newlines=True,
  51. stderr=subprocess.STDOUT)
  52. except OSError as e:
  53. if e.errno == os.errno.ENOENT:
  54. action = 'skipping' if continue_on_error else 'aborting'
  55. print("Command %s not found, %s." % (os_command, action))
  56. if continue_on_error:
  57. return
  58. else:
  59. sys.exit(1)
  60. # Extract version from "swift 0.3"
  61. version = version.splitlines()[-1].strip().rpartition(' ')[2]
  62. print("Documenting '%s help (version %s)'" % (os_command, version))
  63. os_file = open(os.path.join(output_dir, os_filename), 'w')
  64. os_file.write(".. ###################################################\n")
  65. os_file.write(".. ## WARNING ######################################\n")
  66. os_file.write(".. ############## WARNING ##########################\n")
  67. os_file.write(".. ########################## WARNING ##############\n")
  68. os_file.write(".. ###################################### WARNING ##\n")
  69. os_file.write(".. ###################################################\n")
  70. os_file.write(".. ###################################################\n")
  71. os_file.write(".. ##\n")
  72. os_file.write(".. This file is tool-generated. Do not edit manually.\n")
  73. os_file.write(".. http://docs.openstack.org/contributor-guide/\n")
  74. os_file.write(".. doc-tools/cli-reference.html\n")
  75. os_file.write(".. ##\n")
  76. os_file.write(".. ## WARNING ######################################\n")
  77. os_file.write(".. ############## WARNING ##########################\n")
  78. os_file.write(".. ########################## WARNING ##############\n")
  79. os_file.write(".. ###################################### WARNING ##\n")
  80. os_file.write(".. ###################################################\n\n")
  81. format_heading(title, 1, os_file)
  82. if os_command == "heat":
  83. os_file.write(".. warning::\n\n")
  84. os_file.write(" The " + os_command + " CLI is deprecated\n")
  85. os_file.write(" in favor of python-openstackclient.\n\n")
  86. os_file.write("The " + os_command + " client is the command-line ")
  87. os_file.write("interface (CLI) for the\n")
  88. os_file.write(api_name + "\n")
  89. os_file.write("and its extensions.\n\n")
  90. os_file.write("This chapter documents :command:`" + os_command + "` ")
  91. os_file.write("version ``" + version + "``.\n\n")
  92. os_file.write("For help on a specific :command:`" + os_command + "` ")
  93. os_file.write("command, enter:\n\n")
  94. os_file.write(".. code-block:: console\n\n")
  95. if use_help_flag(os_command):
  96. os_file.write(" $ " + os_command + " COMMAND --help\n\n")
  97. else:
  98. os_file.write(" $ " + os_command + " help COMMAND\n\n")
  99. os_file.write(".. _" + os_command + "_command_usage:\n\n")
  100. format_heading(os_command + " usage", 2, os_file)
  101. return os_file
  102. def is_option(string):
  103. """Returns True if string specifies an argument."""
  104. for x in string:
  105. if not (x.isupper() or x == '_' or x == ','):
  106. return False
  107. if string.startswith('DEPRECATED'):
  108. return False
  109. return True
  110. def extract_options(line):
  111. """Extract command or option from line."""
  112. # We have a command or parameter to handle
  113. # Differentiate:
  114. # 1. --version
  115. # 2. --timeout <seconds>
  116. # 3. --service <service>, --service-id <service>
  117. # 4. -v, --verbose
  118. # 5. -p PORT, --port PORT
  119. # 6. <backup> ID of the backup to restore.
  120. # 7. --alarm-action <Webhook URL>
  121. # 8. <NAME or ID> Name or ID of stack to resume.
  122. # 9. --json JSON JSON representation of node group template.
  123. # 10. --id <cluster_id> ID of the cluster to show.
  124. # 11. --instance "<opt=value,opt=value,...>"
  125. split_line = line.split(None, 2)
  126. if split_line[0].startswith("-"):
  127. last_was_option = True
  128. else:
  129. last_was_option = False
  130. if (len(split_line) > 1 and
  131. ('<' in split_line[0] or
  132. '<' in split_line[1] or
  133. '--' in split_line[1] or
  134. split_line[1].startswith(("-", '<', '{', '[')) or
  135. is_option(split_line[1]))):
  136. words = line.split(None)
  137. i = 0
  138. while i < len(words) - 1:
  139. if (('<' in words[i] and
  140. '>' not in words[i]) or
  141. ('[' in words[i] and
  142. ']' not in words[i])):
  143. words[i] += ' ' + words[i + 1]
  144. del words[i + 1]
  145. else:
  146. i += 1
  147. skip_is_option = False
  148. while len(words) > 1:
  149. if words[1].startswith('DEPRECATED'):
  150. break
  151. if last_was_option:
  152. if (words[1].startswith(("-", '<', '{', '[', '"')) or
  153. (is_option(words[1]) and skip_is_option is False)):
  154. skip_is_option = False
  155. if words[1].isupper() or words[1].startswith('<'):
  156. skip_is_option = True
  157. words[0] = words[0] + ' ' + words[1]
  158. del words[1]
  159. else:
  160. break
  161. else:
  162. if words[1].startswith("-"):
  163. words[0] = words[0] + ' ' + words[1]
  164. del words[1]
  165. else:
  166. break
  167. w0 = words[0]
  168. del words[0]
  169. w1 = ''
  170. if words:
  171. w1 = words[0]
  172. del words[0]
  173. for w in words:
  174. w1 += " " + w
  175. if not w1:
  176. split_line = [w0]
  177. else:
  178. split_line = [w0, w1]
  179. else:
  180. split_line = line.split(None, 1)
  181. return split_line
  182. def format_heading(heading, level, os_file):
  183. """Nicely print heading.
  184. :param heading: heading strings
  185. :param level: heading level
  186. :param os_file: open filehandle for output of RST file
  187. """
  188. if level == 1:
  189. os_file.write("=" * len(heading) + "\n")
  190. os_file.write(heading + "\n")
  191. if level == 1:
  192. os_file.write("=" * len(heading) + "\n\n")
  193. elif level == 2:
  194. os_file.write("~" * len(heading) + "\n\n")
  195. elif level == 3:
  196. os_file.write("-" * len(heading) + "\n\n")
  197. else:
  198. os_file.write("\n")
  199. return
  200. def format_help(title, lines, os_file):
  201. """Nicely print section of lines.
  202. :param title: help title, if exist
  203. :param lines: strings to format
  204. :param os_file: open filehandle for output of RST file
  205. """
  206. close_entry = False
  207. if title:
  208. os_file.write("**" + title + ":**" + "\n\n")
  209. continued_line = ''
  210. for line in lines:
  211. if not line or line[0] != ' ':
  212. break
  213. # We have to handle these cases:
  214. # 1. command Explanation
  215. # 2. command
  216. # Explanation on next line
  217. # 3. command Explanation continued
  218. # on next line
  219. # If there are more than 8 spaces, let's treat it as
  220. # explanation.
  221. if line.startswith(' '):
  222. # Explanation
  223. xline = continued_line + quote_rst(line.lstrip(' '))
  224. continued_line = ''
  225. # Concatenate the command options with "-"
  226. # For example:
  227. # see 'glance image-
  228. # show'
  229. if xline.endswith('-'):
  230. continued_line = xline
  231. continue
  232. # check niceness
  233. if len(xline) > (MAXLINELENGTH - 2):
  234. xline = xline.replace(' ', '\n ')
  235. os_file.write(" " + xline + "\n")
  236. continue
  237. # Now we have a command or parameter to handle
  238. split_line = extract_options(line)
  239. if not close_entry:
  240. close_entry = True
  241. else:
  242. os_file.write("\n")
  243. xline = split_line[0]
  244. # check niceness work around for long option name, glance
  245. xline = xline.replace('[<RESOURCE_TYPE_ASSOCIATIONS> ...]',
  246. '[...]')
  247. os_file.write("``" + xline + "``\n")
  248. if len(split_line) > 1:
  249. # Explanation
  250. xline = continued_line + quote_rst(split_line[1])
  251. continued_line = ''
  252. # Concatenate the command options with "-"
  253. # For example:
  254. # see 'glance image-
  255. # show'
  256. if xline.endswith('-'):
  257. continued_line = xline
  258. continue
  259. # check niceness
  260. if len(xline) > (MAXLINELENGTH - 2):
  261. # check niceness
  262. xline = xline.replace(' ', '\n ')
  263. os_file.write(" " + xline + "\n")
  264. os_file.write("\n")
  265. return
  266. def generate_command(os_command, os_file):
  267. """Convert os_command --help to RST.
  268. :param os_command: client command to document
  269. :param os_file: open filehandle for output of RST file
  270. """
  271. if use_help_flag(os_command):
  272. help_lines = subprocess.check_output([os_command, "--help"],
  273. universal_newlines=True,
  274. stderr=DEVNULL).split('\n')
  275. else:
  276. help_lines = subprocess.check_output([os_command, "help"],
  277. universal_newlines=True,
  278. stderr=DEVNULL).split('\n')
  279. ignore_next_lines = False
  280. next_line_screen = True
  281. line_index = -1
  282. in_screen = False
  283. subcommands = 'complete'
  284. for line in help_lines:
  285. line_index += 1
  286. if line and line[0] != ' ':
  287. # XXX: Might have whitespace before!!
  288. if '<subcommands>' in line:
  289. ignore_next_lines = False
  290. continue
  291. if 'Positional arguments' in line:
  292. ignore_next_lines = True
  293. next_line_screen = True
  294. os_file.write("\n\n")
  295. in_screen = False
  296. if os_command != "glance":
  297. format_help('Subcommands',
  298. help_lines[line_index + 2:], os_file)
  299. continue
  300. if line.startswith(('Optional arguments:', 'Optional:',
  301. 'Options:', 'optional arguments')):
  302. if in_screen:
  303. os_file.write("\n\n")
  304. in_screen = False
  305. os_file.write(".. _" + os_command + "_command_options:\n\n")
  306. format_heading(os_command + " optional arguments", 2, os_file)
  307. format_help('', help_lines[line_index + 1:], os_file)
  308. next_line_screen = True
  309. ignore_next_lines = True
  310. continue
  311. # magnum
  312. if line.startswith('Common auth options'):
  313. if in_screen:
  314. os_file.write("\n\n")
  315. in_screen = False
  316. os_file.write("\n")
  317. os_file.write(os_command)
  318. os_file.write(".. _" + os_command + "_common_auth:\n\n")
  319. format_heading(os_command + " common authentication arguments",
  320. 2, os_file)
  321. format_help('', help_lines[line_index + 1:], os_file)
  322. next_line_screen = True
  323. ignore_next_lines = True
  324. continue
  325. # neutron
  326. if line.startswith('Commands for API v2.0:'):
  327. if in_screen:
  328. os_file.write("\n\n")
  329. in_screen = False
  330. os_file.write(".. _" + os_command + "_common_api_v2:\n\n")
  331. format_heading(os_command + " API v2.0 commands", 2, os_file)
  332. format_help('', help_lines[line_index + 1:], os_file)
  333. next_line_screen = True
  334. ignore_next_lines = True
  335. continue
  336. # swift
  337. if line.startswith('Examples:'):
  338. os_file.write(".. _" + os_command + "_examples:\n\n")
  339. format_heading(os_command + " examples", 2, os_file)
  340. next_line_screen = True
  341. ignore_next_lines = False
  342. continue
  343. # all
  344. if not line.startswith('usage'):
  345. continue
  346. if not ignore_next_lines:
  347. if next_line_screen:
  348. os_file.write(".. code-block:: console\n\n")
  349. os_file.write(" " + line)
  350. next_line_screen = False
  351. in_screen = True
  352. elif line:
  353. os_file.write("\n " + line.rstrip())
  354. # subcommands (select bash-completion, complete for bash-completion)
  355. if 'bash-completion' in line:
  356. subcommands = 'bash-completion'
  357. if in_screen:
  358. os_file.write("\n\n")
  359. return subcommands
  360. def generate_subcommand(os_command, os_subcommand, os_file, extra_params,
  361. suffix, title_suffix):
  362. """Convert os_command help os_subcommand to RST.
  363. :param os_command: client command to document
  364. :param os_subcommand: client subcommand to document
  365. :param os_file: open filehandle for output of RST file
  366. :param extra_params: Extra parameter to pass to os_command
  367. :param suffix: Extra suffix to add to link ID
  368. :param title_suffix: Extra suffix for title
  369. """
  370. print("Documenting subcommand '%s'..." % os_subcommand)
  371. args = [os_command]
  372. if extra_params:
  373. args.extend(extra_params)
  374. if use_help_flag(os_command):
  375. args.append(os_subcommand)
  376. args.append("--help")
  377. else:
  378. args.append("help")
  379. args.append(os_subcommand)
  380. help_lines = subprocess.check_output(args,
  381. universal_newlines=True,
  382. stderr=DEVNULL)
  383. help_lines_lower = help_lines.lower()
  384. if 'positional arguments' in help_lines_lower:
  385. index = help_lines_lower.index('positional arguments')
  386. elif 'optional arguments' in help_lines_lower:
  387. index = help_lines_lower.index('optional arguments')
  388. else:
  389. index = len(help_lines_lower)
  390. if 'deprecated' in (help_lines_lower[0:index]):
  391. print("Subcommand '%s' is deprecated, skipping." % os_subcommand)
  392. return
  393. help_lines = help_lines.split('\n')
  394. os_subcommandid = os_subcommand.replace(' ', '_')
  395. os_file.write(".. _" + os_command + "_" + os_subcommandid + suffix)
  396. os_file.write(":\n\n")
  397. format_heading(os_command + " " + os_subcommand + title_suffix, 3, os_file)
  398. if os_command == "swift":
  399. next_line_screen = False
  400. os_file.write(".. code-block:: console\n\n")
  401. os_file.write("Usage: swift " + os_subcommand + "\n\n")
  402. in_para = True
  403. else:
  404. next_line_screen = True
  405. in_para = False
  406. if extra_params:
  407. extra_paramstr = ' '.join(extra_params)
  408. help_lines[0] = help_lines[0].replace(os_command, "%s %s" %
  409. (os_command, extra_paramstr))
  410. line_index = -1
  411. # Content is:
  412. # usage...
  413. #
  414. # Description
  415. #
  416. # Arguments
  417. skip_lines = False
  418. for line in help_lines:
  419. line_index += 1
  420. if line.startswith('Usage:') and os_command == "swift":
  421. line = line[len("Usage: "):]
  422. if line.startswith(('Arguments:',
  423. 'Positional arguments:', 'positional arguments',
  424. 'Optional arguments', 'optional arguments',
  425. 'Required arguments', 'required arguments')):
  426. if in_para:
  427. in_para = False
  428. os_file.write("\n")
  429. if line.startswith(('Positional arguments',
  430. 'positional arguments')):
  431. format_help('Positional arguments',
  432. help_lines[line_index + 1:], os_file)
  433. skip_lines = True
  434. continue
  435. elif line.startswith(('Optional arguments:',
  436. 'optional arguments')):
  437. format_help('Optional arguments',
  438. help_lines[line_index + 1:], os_file)
  439. skip_lines = True
  440. continue
  441. elif line.startswith(('Required arguments:',
  442. 'required arguments')):
  443. format_help('Required arguments',
  444. help_lines[line_index + 1:], os_file)
  445. skip_lines = True
  446. continue
  447. else:
  448. format_help('Arguments', help_lines[line_index + 1:], os_file)
  449. break
  450. if skip_lines:
  451. continue
  452. if not line:
  453. if not in_para:
  454. os_file.write("\n")
  455. in_para = True
  456. continue
  457. if next_line_screen:
  458. os_file.write(".. code-block:: console\n\n")
  459. os_file.write(" " + line + "\n")
  460. next_line_screen = False
  461. elif line.startswith(' '):
  462. # ceilometer alarm-gnocchi-aggregation-by-metrics-threshold-create
  463. # has 7 white space indentation
  464. if not line.isspace():
  465. # skip blank line, such as "trove help cluster-grow" command.
  466. os_file.write(" " + line + "\n")
  467. else:
  468. xline = quote_rst(line)
  469. if (len(xline) > MAXLINELENGTH):
  470. # check niceness
  471. xline = xline.replace(' ', '\n')
  472. os_file.write(xline + "\n")
  473. if in_para:
  474. os_file.write("\n")
  475. def discover_subcommands(os_command, subcommands, extra_params):
  476. """Discover all help subcommands for the given command"
  477. :param os_command: client command whose subcommands need to be discovered
  478. :param subcommands: list or type ('complete' or 'bash-completion')
  479. of subcommands to document
  480. :param extra_params: Extra parameter to pass to os_command.
  481. :return: the list of subcommands discovered
  482. :rtype: list(str)
  483. """
  484. if extra_params is None:
  485. extra_params = ''
  486. print(("Discovering subcommands of '%s' %s ..."
  487. % (os_command, extra_params)))
  488. blacklist = ['bash-completion', 'complete', 'help']
  489. if type(subcommands) is str:
  490. args = [os_command]
  491. if extra_params:
  492. args.extend(extra_params)
  493. if subcommands == 'complete':
  494. subcommands = []
  495. args.append('complete')
  496. lines = subprocess.check_output(
  497. args, universal_newlines=True, stderr=DEVNULL).split('\n')
  498. delim = ' '
  499. # if the cmds= line contains '-' then use that as a delim
  500. for line in lines:
  501. if '-' in line and 'cmds=' in line:
  502. delim = '-'
  503. break
  504. for line in [x.strip() for x in lines
  505. if x.strip().startswith('cmds_') and '-' in x]:
  506. subcommand, _ = line.split('=')
  507. subcommand = subcommand.replace(
  508. 'cmds_', '').replace('_', delim)
  509. subcommands.append(subcommand)
  510. else:
  511. args.append('bash-completion')
  512. subcommands = subprocess.check_output(
  513. args,
  514. universal_newlines=True).strip().split('\n')[-1].split()
  515. subcommands = sorted([o for o in subcommands if not (o.startswith('-') or
  516. o in blacklist)])
  517. print("%d subcommands discovered." % len(subcommands))
  518. return subcommands
  519. def generate_subcommands(os_command, os_file, subcommands, extra_params,
  520. suffix, title_suffix):
  521. """Convert os_command help subcommands for all subcommands to RST.
  522. :param os_command: client command to document
  523. :param os_file: open filehandle for output of RST file
  524. :param subcommands: list or type ('complete' or 'bash-completion')
  525. of subcommands to document
  526. :param extra_params: Extra parameter to pass to os_command.
  527. :param suffix: Extra suffix to add to link ID
  528. :param title_suffix: Extra suffix for title
  529. """
  530. for subcommand in subcommands:
  531. generate_subcommand(os_command, subcommand, os_file, extra_params,
  532. suffix, title_suffix)
  533. print("%d subcommands documented." % len(subcommands))
  534. def discover_and_generate_subcommands(os_command, os_file, subcommands,
  535. extra_params, suffix, title_suffix):
  536. """Convert os_command help subcommands for all subcommands to RST.
  537. :param os_command: client command to document
  538. :param os_file: open filehandle for output of RST file
  539. :param subcommands: list or type ('complete' or 'bash-completion')
  540. of subcommands to document
  541. :param extra_params: Extra parameter to pass to os_command.
  542. :param suffix: Extra suffix to add to link ID
  543. :param title_suffix: Extra suffix for title
  544. """
  545. subcommands = discover_subcommands(os_command, subcommands, extra_params)
  546. generate_subcommands(os_command, os_file, subcommands, extra_params,
  547. suffix, title_suffix)
  548. def _get_clients_filename():
  549. return os.path.join(os.path.dirname(__file__),
  550. 'resources/clients.yaml')
  551. def get_clients():
  552. """Load client definitions from the resource file."""
  553. fname = _get_clients_filename()
  554. clients = yaml.load(open(fname, 'r'))
  555. return clients
  556. def document_single_project(os_command, output_dir, continue_on_error):
  557. """Create documentation for os_command."""
  558. clients = get_clients()
  559. if os_command not in clients:
  560. print("'%s' command not yet handled" % os_command)
  561. print("(Command must be defined in '%s')" % _get_clients_filename())
  562. if continue_on_error:
  563. return False
  564. else:
  565. sys.exit(-1)
  566. print("Documenting '%s'" % os_command)
  567. data = clients[os_command]
  568. if 'name' in data:
  569. api_name = "%s API" % data['name']
  570. title = "%s command-line client" % data.get('title', data['name'])
  571. else:
  572. api_name = ''
  573. title = data.get('title', '')
  574. out_filename = os_command + ".rst"
  575. out_file = generate_heading(os_command, api_name, title,
  576. output_dir, out_filename,
  577. continue_on_error)
  578. if not out_file:
  579. if continue_on_error:
  580. return False
  581. else:
  582. sys.exit(-1)
  583. subcommands = generate_command(os_command, out_file)
  584. if subcommands == 'complete' and data.get('subcommands'):
  585. subcommands = data.get('subcommands')
  586. discover_and_generate_subcommands(os_command, out_file, subcommands,
  587. None, "", "")
  588. print("Finished.\n")
  589. out_file.close()
  590. return True
  591. def main():
  592. clients = get_clients()
  593. api_clients = sorted([x for x in clients if not x.endswith('-manage')])
  594. manage_clients = sorted([x for x in clients if x.endswith('-manage')])
  595. all_clients = api_clients + manage_clients
  596. parser = argparse.ArgumentParser(description="Generate RST files "
  597. "to document python-PROJECTclients.")
  598. parser.add_argument('clients', metavar='client', nargs='*',
  599. help="OpenStack command to document. Specify "
  600. "multiple times to generate documentation for "
  601. "multiple clients. One of: " +
  602. ", ".join(all_clients) + ".")
  603. parser.add_argument("--all", help="Document all clients. "
  604. "Namely " + ", ".join(all_clients) + ".",
  605. action="store_true")
  606. parser.add_argument("--all-api", help="Document all API clients. "
  607. "Namely " + ", ".join(clients.keys()) + ".",
  608. action="store_true")
  609. parser.add_argument("--all-manage", help="Document all manage clients. "
  610. "Namely " + ", ".join(manage_clients) + ".",
  611. action="store_true")
  612. parser.add_argument("--output-dir", default=".",
  613. help="Directory to write generated files to")
  614. parser.add_argument("--continue-on-error", default=False,
  615. help="Continue with remaining clients even if an "
  616. "error occurs generating a client file.",
  617. action="store_true")
  618. parser.add_argument("--version", default=False,
  619. help="Show program's version number and exit.",
  620. action="store_true")
  621. prog_args = parser.parse_args()
  622. client_list = []
  623. if prog_args.all or prog_args.all_api or prog_args.all_manage:
  624. if prog_args.all or prog_args.all_api:
  625. client_list = api_clients
  626. if prog_args.all or prog_args.all_manage:
  627. client_list.extend(manage_clients)
  628. elif prog_args.clients:
  629. client_list = prog_args.clients
  630. if not prog_args or 'help' in [client.lower() for client in client_list]:
  631. parser.print_help()
  632. sys.exit(0)
  633. elif prog_args.version:
  634. print(os_doc_tools.__version__)
  635. sys.exit(0)
  636. if not client_list:
  637. parser.print_help()
  638. sys.exit(1)
  639. print("OpenStack Auto Documenting of Commands (using "
  640. "openstack-doc-tools version %s)\n"
  641. % os_doc_tools.__version__)
  642. success_list = []
  643. error_list = []
  644. for client in client_list:
  645. if document_single_project(
  646. client, prog_args.output_dir, prog_args.continue_on_error):
  647. success_list.append(client)
  648. else:
  649. error_list.append(client)
  650. if success_list:
  651. print("Generated documentation for: %s" % ", ".join(success_list))
  652. if error_list:
  653. print("Generation failed for: %s" % ", ".join(error_list))
  654. sys.exit(1)
  655. if __name__ == "__main__":
  656. sys.exit(main())