entry.py 8.13 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2012 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

29
import argparse
30 31
import ConfigParser
import os
32
import sys
33

34
from slapos.cli_legacy.bang import main as bang
35
from slapos.cli_legacy.console import console
Marco Mariani's avatar
Marco Mariani committed
36
from slapos.cli_legacy.request import request
37 38
from slapos.cli_legacy.remove import remove
from slapos.cli_legacy.supply import supply
39 40
from slapos.cli_legacy.format import main as format
from slapos.cli_legacy.cache import cache_lookup
Marco Mariani's avatar
Marco Mariani committed
41 42 43
from slapos.cli_legacy.slapgrid import runComputerPartition as instance
from slapos.cli_legacy.slapgrid import runSoftwareRelease as software
from slapos.cli_legacy.slapgrid import runUsageReport as report
44 45
from slapos.cli_legacy.svcbackend import supervisord
from slapos.cli_legacy.svcbackend import supervisorctl
46
from slapos.cli_legacy.register import main as register
47
from slapos.version import version
48

49 50 51
# Note: this whole file is a hack. We should better try dedicated library
# like https://github.com/dhellmann/cliff or https://github.com/docopt/docopt.

52 53 54 55 56 57
GLOBAL_SLAPOS_CONFIGURATION = os.environ.get(
    'SLAPOS_CONFIGURATION',
    '/etc/opt/slapos/slapos.cfg')
USER_SLAPOS_CONFIGURATION = os.environ.get(
    'SLAPOS_CLIENT_CONFIGURATION',
    os.environ.get('SLAPOS_CONFIGURATION', '~/.slapos/slapos.cfg'))
58

59

60 61 62 63
class EntryPointNotImplementedError(NotImplementedError):
  def __init__(self, *args, **kw_args):
    NotImplementedError.__init__(self, *args, **kw_args)

64

Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
65
def checkSlaposCfg():
66 67 68 69
  """
  Check if a slapos configuration file was given as a argument.
  If a slapos configuration file is given it return True else False
  """
70 71 72
  # XXX-Cedric: dangerous but quick way to achieve way to not provide
  # configuration file for each command without changing underlying code.
  # It the long term, it should be done in a better way (no guessing).
73 74 75
  for element in sys.argv:
    if '.cfg' in element:
      if os.path.exists(element):
76 77 78
        configp = ConfigParser.SafeConfigParser()
        configp.read(element)
        if configp.has_section('slapos'):
79 80 81
          return True
  return False

82

83 84 85 86 87 88 89 90 91 92 93
def checkOption(option):
  """
  Check if a given option is already in call line
  Add it and its values if missing
  """
  option = option.split()
  key = option[0]
  for element in sys.argv:
    if key in element:
      return True
  sys.argv.append(key)
94
  if len(option) > 1:
95 96 97
    sys.argv = sys.argv + option[1:]
  return True

98

99
def call(fun, config_path=False, option=None):
100
  """
Cédric Le Ninivin's avatar
Cédric Le Ninivin committed
101
  Add missing options to sys.argv
102 103 104
  Add config if asked and it is missing
  Call function fun
  """
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
105 106
  if option is None:
    option = []
107 108
  for element in option:
    checkOption(element)
109
  if config_path:
110
    if not checkSlaposCfg():
111
      sys.argv = [sys.argv[0]] + [os.path.expanduser(config_path)] + sys.argv[1:]
112
  fun()
113
  sys.exit(0)
114

115

116
def dispatch(command, is_node_command):
117 118 119
  """ Dispatch to correct SlapOS module.
  Here we could use introspection to get rid of the big "if" statements,
  but we want to control every input.
120 121
  Here we give default option and configuration file if they are needed, i.e
  If configuration file is not given: define it arbitrarily, and so on.
122
  """
123
  if is_node_command:
124 125

    if os.getuid() != 0:
126 127
      sys.stderr.write('This command must be run as root.\n')
      sys.exit()
128

129
    if command == 'register':
130
      call(register)
131
    elif command == 'software':
132
      call(software, config_path=GLOBAL_SLAPOS_CONFIGURATION,
133
           option=['--pidfile /opt/slapos/slapgrid-sr.pid'])
134
    elif command == 'instance':
135
      call(instance, config_path=GLOBAL_SLAPOS_CONFIGURATION,
136
           option=['--pidfile /opt/slapos/slapgrid-cp.pid'])
137
    elif command == 'report':
138
      call(report, config_path=GLOBAL_SLAPOS_CONFIGURATION,
139
           option=['--pidfile /opt/slapos/slapgrid-ur.pid'])
140
    elif command == 'bang':
141
      call(bang, config_path=GLOBAL_SLAPOS_CONFIGURATION)
142
    elif command == 'format':
143
      call(format, config_path=GLOBAL_SLAPOS_CONFIGURATION, option=['-c', '-v'])
144
    elif command == 'supervisord':
145
      call(supervisord, config_path=GLOBAL_SLAPOS_CONFIGURATION)
146
    elif command == 'supervisorctl':
147
      call(supervisorctl, config_path=GLOBAL_SLAPOS_CONFIGURATION)
148
    elif command in ['start', 'stop', 'restart', 'status', 'tail']:
149 150
      # Again, too hackish
      sys.argv[-2:-2] = [command]
151
      call(supervisorctl, config_path=GLOBAL_SLAPOS_CONFIGURATION)
152
    else:
153
      return False
154
  elif command == 'request':
155
    call(request, config_path=USER_SLAPOS_CONFIGURATION)
156
  elif command == 'supply':
157
    call(supply, config_path=USER_SLAPOS_CONFIGURATION)
158
  elif command == 'remove':
159
    call(remove, config_path=USER_SLAPOS_CONFIGURATION)
160 161 162 163
  elif command == 'start':
    raise EntryPointNotImplementedError(command)
  elif command == 'stop':
    raise EntryPointNotImplementedError(command)
164 165
  elif command == 'destroy':
    raise EntryPointNotImplementedError(command)
166
  elif command == 'console':
167
    call(console, config_path=USER_SLAPOS_CONFIGURATION)
168
  elif command == 'cache-lookup':
169
    call(cache_lookup, config_path=GLOBAL_SLAPOS_CONFIGURATION)
170 171
  else:
    return False
172

173

174
def main():
175 176 177 178 179 180
  """
  Main entry point of SlapOS Node. Used to dispatch commands to python
  module responsible of the operation.
  """
  # If "node" arg is the first: we strip it and set a switch
  if len(sys.argv) > 1 and sys.argv[1] == "node":
181
    sys.argv.pop(1)
182 183 184
    # Hackish way to show status if no argument is specified
    if len(sys.argv) is 1:
      sys.argv.append('status')
185 186 187 188
    is_node = True
  else:
    is_node = False

189
  usage = """SlapOS %s command line interface.
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
190 191 192 193 194 195
For more informations, refer to SlapOS documentation.

Client subcommands usage:
  slapos request <instance-name> <software-url> [--configuration arg1=value1 arg2=value2 ... argN=valueN]
  slapos supply <software-url> <node-id>
  slapos console
196
  slapos cache-lookup <software-url-or-md5>
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
197 198 199 200 201 202
Node subcommands usage:
  slapos node
  slapos node register <node-id>
  slapos node software
  slapos node instance
  slapos node report
203
  slapos node format
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
204 205 206 207 208 209 210
  slapos node start <process>
  slapos node stop <process>
  slapos node restart <process>
  slapos node tail [process]
  slapos node status <process>
  slapos node supervisorctl
  slapos node supervisord
211
""" % version
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
212

213
  # Parse arguments
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
214
  # XXX remove the "positional arguments" from help message
215 216 217
  ap = argparse.ArgumentParser(usage=usage)
  ap.add_argument('command')
  ap.add_argument('argument_list', nargs=argparse.REMAINDER)
218

219
  args = ap.parse_args()
220
  # Set sys.argv for the sub-entry point that we will call
221 222
  command_line = [args.command]
  command_line.extend(args.argument_list)
223 224 225
  sys.argv = command_line

  try:
226 227
    if not dispatch(args.command, is_node):
      ap.print_help()
228
      sys.exit(1)
229
  except EntryPointNotImplementedError, exception:
230
    print ('The command %s does not exist or is not yet implemented. Please '
231 232 233
           'have a look at http://community.slapos.org to read documentation or '
           'forum. Please also make sure that SlapOS Node is up to '
           'date.' % exception)
Cédric de Saint Martin's avatar
Cédric de Saint Martin committed
234
    sys.exit(1)