Viewing file: Init.py (29.64 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/Commands/Init.py,v 1.79 2005/04/07 00:18:47 jkloth Exp $ """ 4Suite repository init command (4ss_manager init)
Copyright 2005 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """
__doc__ = """Initialize the repository. This involves reformatting \ the storage back end (e.g. Postgres database or FlatFile root \ directory) and then adding the core data structures. You will LOSE \ ALL DATA when you execute the init, unless you use the --update \ option. You can only init on the machine on which the command is \ executed. You must have write access to where the back end stores its \ data (e.g., your userid must be able to write to the Postgres database \ if the back end is Postgres). You may be prompted for additional \ information to complete the init."""
from __future__ import generators
import sha, getpass, os, glob, time, sys, posixpath
from Ft import GetConfigVar from Ft.Lib import Uri, Wrap, Terminal from Ft.Server import FtServerBaseException, FTSERVER_NAMESPACE from Ft.Server.Common import AclConstants, ResourceTypes from Ft.Server.Common import Schema, ClAuthenticate, XmlLib from Ft.Server.Common.Install import InstallUtil, Serialize from Ft.Server.Server import Drivers from Ft.Server.Server import SCore, FtServerServerException, Error from Ft.Server.Server.Drivers import PathImp from Ft.Server.Server.Lib import ConfigFile, LogUtil from Ft.Server.Server.SCore import RepositoryImp from Ft.Xml.XLink import XLINK_NAMESPACE
def CommandLineInit(options, args): """ Init function called from the command line tool. Parse options and args, do validation, and then call the common Initialization routines. """
#Gather the command line information do_update = options.get('update', 0) repoOnly = options.get('repoOnly', 0) confirm = options.get('confirm', 0) db_port = options.get('dashboard-port', 8080)
components = args.get('components')
#Adjust the components as needed if not len(components) and not repoOnly: #Default to full full = True else: full = False
if repoOnly: #if they only want the repo, then the components can only be Core #If they specify anything else then it is an error. if components: sys.stderr.write("You cannot specify repo-only and a list of components.\n") return components = ['Core'] else: #If they didn't specify repo-only then update the component list if full: components = g_allComponentNames[:] components.insert(0, 'Core') #Make sure it is first. # data must be second if 'data' in components: components.remove('data') components.insert(1, 'data') else: sys.stderr.write("The Data component must be first in the list; other components depend on it.\n") return
#Read the config file properties = ConfigFile.Read(options.get('config-file')) coreId = options.get('core-id', os.environ.get('FTSS_CORE_ID', 'Core')) try: properties = properties[coreId] except KeyError: raise FtServerServerException(Error.CONFIG_INVALID_CORE_ID, name=coreId) properties['CoreId'] = coreId
#Validate that the user can do what they think they can driver = Drivers.Begin(LogUtil.NullLogger(), properties)
exists = driver.exists() userName = passwd = None if exists == 1: #A repository was found. Make sure we have access if do_update: sys.stderr.write("Updating existing repository\n") else: #Make sure the user knows sys.stderr.write("\nA repository currently exists!\n") sys.stderr.write("Please authenticate with a user name and password\n") sys.stderr.write("with execute permission for the init command.\n") userName, passwd = ClAuthenticate.UserAuthenticate(prompt="Manager", promptOnly=True) driver.login(userName, passwd) model = driver.getSystemModel() stmts = model.complete(None, Schema.COMMAND_FULL_NAME, '4ss_manager.init') try: p = PathImp.CreateInitialPath('/', driver) if len(stmts) == 1: p = p.normalize(stmts[0].subject + ";no-traverse") else: initCmdDefaultPath = '/ftss/commands/4ss_manager.init' sys.stderr.write("Metadata for the Init command was not found in the repo.\n") sys.stderr.write("Will attempt to check execute permission using\n") sys.stderr.write("%s, if it exists.\n" % initCmdDefaultPath) p = p.normalize('%s;no-traverse' % initCmdDefaultPath) driver.verifyAcl(p, AclConstants.EXECUTE_ACCESS, 0) except FtServerServerException, e: if e.errorCode == Error.PERMISSION_DENIED: raise e pass driver.rollback() del driver elif do_update: #You cannot update a repo that does not exist. sys.stderr.write("You cannot execute an update when a repository does not exist\n") return
#Determine if we need to destroy the repository. We destroy the repo is #a) it partially exists (exists == 0) #b) the user didn't specify update and exists == 1 (it is there) destroyRepo = (exists == 0) or (exists == 1 and not do_update)
#Hand processing off to the common init routines DoInit(properties, userName, passwd, components, confirm=confirm, destroyRepo=destroyRepo, do_update=do_update)
sys.stdout.flush() sys.stderr.write('\n') return
def DoInit(properties, userName, password, components, confirm=False, destroyRepo=False, users=None, quiet=False, do_update=False): """ Do the actual initialization. Iterate over the components and do each one. """
products = BuildProductList(components, destroyRepo, users, do_update=do_update) for (product, base) in products: repo = product.getRepo(userName, password, properties) commit = False try: product.install(repo, base, quiet, confirm, do_update=do_update) commit = True finally: if commit: product.commit(repo) else: product.rollback(repo) return
def BuildProductList(components, destroyRepo, users, do_update=False): for component in components: if component.lower() == 'core': yield (CoreProduct(destroyRepo, users), '/') elif component.lower() == 'data': pl, base = BuildDataProduct() yield (OtherProduct(pl, users), base) elif component.lower() == 'servers': pl, base = BuildServerProduct() yield (OtherProduct(pl, users), base) elif component.lower() == 'commands': pl, base = BuildCommandProduct() yield (OtherProduct(pl, users), base) elif component.lower() == 'docs': for pl, base in BuildDocProduct(do_update=do_update): yield (OtherProduct(pl, users), base) elif component.lower() == 'dashboard': path = os.path.join(GetConfigVar('DATADIR'), 'Dashboard', 'setup.xml') f = open(path) pl = InstallUtil.Deserialize(f, Uri.OsPathToUri(path)) f.close() yield (OtherProduct(pl, users), '/ftss') elif component.lower() == 'demos': path = os.path.join(GetConfigVar('DATADIR'), 'Demos', 'setup.xml') f = open(path) pl = InstallUtil.Deserialize(f, Uri.OsPathToUri(path)) f.close() yield (OtherProduct(pl, users), '/ftss') else: sys.stderr.write('Skipping unknown component %s\n' % component)
def BuildDataProduct(): l = []
#Add some containers l.append(InstallUtil.Container('docdefs',[], None, None, None)) l.append(InstallUtil.Container('data',[], None, None, None)) l.append(InstallUtil.Container('data/icons',[], None, None, None)) l.append(InstallUtil.Container('data/presmaterial',[], None, None, None))
#stBase = posixpath.join(urllib.pathname2url(GetConfigVar('DATADIR')), 'Data', 'Stylesheets') stBase = posixpath.join(Uri.OsPathToUri(GetConfigVar('DATADIR'), attemptAbsolute=False), 'Data', 'Stylesheets')
#Add the docdefs dd = InstallUtil.XPathDocumentDefinition('docdefs/docbook1',[], None, None, None) dd.setPath(posixpath.join(stBase, 'docbook1.docdef')) l.append(dd) dd = InstallUtil.XPathDocumentDefinition('docdefs/wsdl',[], None, None, None) dd.setPath(posixpath.join(stBase, 'wsdl.docdef')) l.append(dd) dd = InstallUtil.XPathDocumentDefinition('docdefs/dublin_core',[], None, None, None) dd.setPath(posixpath.join(stBase, 'dublin_core.docdef')) l.append(dd)
dd = InstallUtil.XPathDocumentDefinition('docdefs/generated-docs',[], None, None, None) dd.setPath(posixpath.join(stBase, 'generated-docs.docdef')) l.append(dd)
#Get stuff from the stylesheet package stBase = os.path.join(GetConfigVar('DATADIR'), 'Data', 'Stylesheets') icBase = os.path.join(GetConfigVar('DATADIR'), 'Data', 'Icons') schBase = os.path.join(GetConfigVar('DATADIR'), 'Schemata')
for css in [ 'sdocbook_html.css', 'commandline.css', 'extensions.css', 'modules.css', ]: r = InstallUtil.RawFile('data/' + css, [], None, 'text/css') r.setPath(posixpath.join(Uri.OsPathToUri(stBase, attemptAbsolute=False), css)) l.append(r)
for (ext, imt_suff) in [('gif', 'gif'), ('png', 'png'), ('jpg', 'jpeg')]: files = glob.glob(os.path.join(stBase, '*.'+ext)) for f in files: name = os.path.basename(f) r = InstallUtil.RawFile('data/' + name, [], None, 'image/'+imt_suff) r.setPath(Uri.OsPathToUri(f, attemptAbsolute=False)) l.append(r) files = glob.glob(os.path.join(icBase, '*.'+ext)) for f in files: name = os.path.basename(f) r = InstallUtil.RawFile('data/icons/' + name, [], None, 'image/'+imt_suff) r.setPath(Uri.OsPathToUri(f, attemptAbsolute=False)) l.append(r)
dtds = glob.glob(os.path.join(schBase, '*.dtd')) + glob.glob(os.path.join(schBase, '*.ent')) #print schBase, dtds for f in dtds: name = os.path.basename(f) r = InstallUtil.RawFile('data/' + name, [], None, 'text/xml') r.setPath(Uri.OsPathToUri(f, attemptAbsolute=False)) l.append(r)
xmls = glob.glob(os.path.join(stBase, '*.xml')) + [os.path.join(stBase, 'null')] for x in xmls: name = os.path.basename(x) d = InstallUtil.XmlDocument('data/'+name, [], None, None, None) d.setPath(Uri.OsPathToUri(x, attemptAbsolute=False)) l.append(d)
stys = [ # basic transformations 'null.xslt', 'identity.xslt', 'pretty.xslt', 'decorated-xml.xslt', # DocBook XML conversion 'docbook_html1.xslt', 'sdocbook_html.xslt', 'docbook_text1.xslt', # text formatting 'justify.xslt', # repository resource formatting 'container.xslt', 'cl-container.xslt', # API doc XML formatting 'commandline_html.xslt', 'extensions_html.xslt', 'modules_html.xslt', # misc 'xbel2rdf.xslt', ] for s in stys: path = os.path.join(stBase, s) d = InstallUtil.XsltDocument('data/'+s,[], None, None, None) d.setPath(Uri.OsPathToUri(path, attemptAbsolute=False)) l.append(d)
pl = InstallUtil.Product(l, name='Data') return pl, '/ftss'
def BuildServerProduct(): l = []
l.append(InstallUtil.Container('servers',[], None, None, None))
serverbase = os.path.join(GetConfigVar('DATADIR'), 'Data', 'Servers')
s = InstallUtil.Server('servers/FtRpc-server.xml',[], None, None, None) s.setPath(posixpath.join(Uri.OsPathToUri(serverbase, attemptAbsolute=False), 'FtRpc-server.xml')) l.append(s) s = InstallUtil.Server('servers/FtFtp-server.xml',[], None, None, None) s.setPath(posixpath.join(Uri.OsPathToUri(serverbase, attemptAbsolute=False), 'FtFtp-server.xml')) l.append(s) s = InstallUtil.Server('servers/FtSoap-server.xml',[], None, None, None) s.setPath(posixpath.join(Uri.OsPathToUri(serverbase, attemptAbsolute=False), 'FtSoap-server.xml')) l.append(s)
pl = InstallUtil.Product(l, name='Servers') return pl, '/ftss'
def BuildDocProduct(do_update=False): l = []
l.append(InstallUtil.Container('docs', [], None, None, None)) mapping = Serialize.g_defaultFileMap.copy() mapping['.doc'] = ('/ftss/docdefs/docbook1','text/xml', InstallUtil.XmlDocument) mapping['.xml'] = ('/ftss/docdefs/generated-docs','text/xml', InstallUtil.XmlDocument)
tty = Terminal.Terminal(sys.stderr) columns = tty.columns() docs_dir = os.path.join(GetConfigVar('DOCDIR'), 'xml')
if os.path.exists(docs_dir): l.extend(Serialize.MirrorDir('/ftss/docs', docs_dir, mapping, 0)) if not do_update and len(l) > 24: msg = "\nNote: %d docs will be installed." % len(l) + \ " This may be a time-consuming process. To avoid" + \ " this, abort now and either remove the " + docs_dir + \ " directory or run the init command without" + \ " specifying Docs installation (see --help)." tty.write(Wrap(msg, columns) + '\n') else: msg = "\nNote: There are no docs to " + \ ('install','update')[do_update and 1 or 0] + \ ". If docs are needed, first reinstall 4Suite with" + \ " 'setup.py install --with-docs'. Also note that" + \ " generating the docs and installing them in the" + \ " repository are time-consuming processes." tty.write(Wrap(msg, columns) + '\n')
# chunking is safe if there are no dependencies across chunks. # docs are independent XML; they only need their docdef, # /ftss/docdefs/generated-docs size = len(l) chunksize = 110 if chunksize < size: totalchunks = size / chunksize + 1 part = 0 for i in xrange(totalchunks): part += 1 chunk = l[:chunksize] pl = InstallUtil.Product(chunk, name='Docs (part %d of %d)' % (part, totalchunks)) yield pl, '/ftss' l = l[i+chunksize:] else: yield InstallUtil.Product(l, name='Docs'), 'ftss'
def BuildCommandProduct(): l = [] l.append(InstallUtil.Container('commands', [], None, None, None)) from Ft.Server.Server.Commands import ManagerCommandLineApp app = ManagerCommandLineApp()
l.extend(BuildCommandDocs( app, '', [(AclConstants.EXECUTE_ACCESS, AclConstants.SUPER_USER_GROUP_NAME, 1)] ))
from Ft.Server.Client.Commands import GeneralCommandLineApp app = GeneralCommandLineApp()
l.extend(BuildCommandDocs( app, '', [(AclConstants.EXECUTE_ACCESS, AclConstants.WORLD_GROUP_NAME, 1)] ))
pl = InstallUtil.Product(l, name='Commands') return pl, '/ftss'
class CoreProduct(InstallUtil.Product):
#There are two different flows through this code. #One, the user is doing a fresh install. Note, this could be #because they want to remove an existing repo
#Secondly, the user is doing an update.
#The main difference is how we deal with users. If, the user is not doing an update, #Then we need to add the users, and the superuser group to the installation. #If the user is doing an update then we don't touch these.
coreResources = [InstallUtil.Container('ftss',[], AclConstants.SUPER_USER_GROUP_NAME, None, None), InstallUtil.Container('ftss/users',[], AclConstants.SUPER_USER_GROUP_NAME, None, None), InstallUtil.Container('ftss/groups',[], AclConstants.SUPER_USER_GROUP_NAME, None, None), ]
users = []
def __init__(self, destroyRepo, users): self.destroyRepo = destroyRepo if users: self.users = users InstallUtil.Product.__init__(self, resourceList = self.coreResources[:], name='Repository', useIndicator=False )
def install(self, driver, basePath, quiet, confirm, do_update=False): """ Perform a few extra installation features. """ #First, see if we need to get rid of an existing repo. if self.destroyRepo: if confirm: sys.stderr.write("Initialization will erase ALL DATA in the 4Suite repository.\n") sys.stderr.write('Are you sure you want to continue (yes/no)? ') sys.stderr.flush() answer = raw_input() if answer.lower() not in ['yes', 'y']: sys.stderr.write("Aborting Initialization\n") return
#See ya! if not quiet: sys.stderr.write("Removing Current Repository\n") driver.destroy()
#now, if the user is not doing an update, then add the users to the resource list #We know the user is doing an update because we either explictly destroyed the #repo, or it never existed. if self.destroyRepo or driver.exists() == -1: #We are doing an update. See if we need to ask for the user list if not self.users: #Gather a list of users usernames = {} while 1: sys.stderr.write('Superuser name to add (or just "enter" when done): ') sys.stderr.flush() user = raw_input() if not user: if len(self.users): break sys.stderr.write("You must add at least one Superuser!\n") continue if user in usernames: sys.stderr.write("Superuser %s was already added!\n" % user) continue same = False while not same: password = getpass.getpass() same = (password == getpass.getpass('Reenter password:')) CoreProduct.users.append((user, password)) usernames[user] = True
#Now, add all of the users to the resource list for u in self.users: self.resourceList.append(InstallUtil.User( u[0], [], None, None, None, 'ftss/users', u[1]))
#Lastly add the super-user group members = [user[0] for user in self.users] self.resourceList.append(InstallUtil.Group( AclConstants.SUPER_USER_GROUP_NAME, [], None, None, None, 'ftss/groups', members))
#Now, since we know it is not an update (inside this if statement) #create the new repo if not quiet: sys.stderr.write("Initializing the Repo\n")
driver.initialize() #Create the root
#Update the log file init_time = time.asctime(time.localtime(time.time())) #open(self.errorLogFileName, 'a').write("Initialized Repo: %s\n" % init_time) else: #If the user just wants to update, then log into the driver to start a TX driver.login(self.userName, self.password)
#Now, at this point in the install, we have a initialized driver so go into the normal installation #and add all of the system containers #Create a new Repo instance so the rest of install will work
path = PathImp.CreateInitialPath('/', driver) repo = RepositoryImp.RepositoryImp(path, driver, None)
return InstallUtil.Product.install(self, repo, '/', quiet, do_update=do_update)
def getRepo(self, userName, password, properties): """ For the core product, our repo is the driver """ self.coreResources = self.coreResources[:]
#Store the user name and password incase we need them self.userName = userName self.password = password
#One extra bit of trickery. At this time we know the properties so add the errorlog to our resourceList
#from Ft.Server.Server.GlobalConfig import g_logFileName #self.errorLogFileName = g_logFileName #self.coreResources.append(InstallUtil.UriReferenceFile( # 'ftss/error.log', [], AclConstants.SUPER_USER_GROUP_NAME, # 'text/plain', Uri.OsPathToUri(self.errorLogFileName, attemptAbsolute=False))) return Drivers.Begin(LogUtil.NullLogger(), properties)
def commit(self, driver): driver.commit()
def rollback(self, driver): pass
def setIndicator(self): pass
class OtherProduct:
def __init__(self, baseProduct, users): self.users = users self.baseProduct = baseProduct return
def getRepo(self, userName, password, properties): if userName is None: if self.users is None: users = CoreProduct.users userName = CoreProduct.users[0][0] password = sha.new(CoreProduct.users[0][1]).hexdigest() else: userName, password = self.users[0] return SCore.GetRepository(userName, password, LogUtil.NullLogger(), properties)
def install(self, repo, basePath, quiet, confirm, do_update=False): if confirm: sys.stderr.write('%s %s (yes/no)? ' % ( ('Install', 'Update')[do_update and 1 or 0], self.baseProduct.name )) sys.stderr.flush() answer = raw_input() if answer.lower() not in ['yes', 'y']: print "Skipping %s" % self.baseProduct.name return 0 return self.baseProduct.install(repo, basePath, quiet, do_update=do_update)
def commit(self, repo): repo.txCommit()
def rollback(self, repo): repo.txRollback()
class ExtendedCommand(InstallUtil.Command):
def __init__(self, path, acl, owner, imt, docDef, command, cmdPath): self.command = command self.cmdPath = cmdPath InstallUtil.Command.__init__(self, path, acl, owner, imt, docDef) return
def _getContent(self): doc = """<ftss:Command xmlns:ftss="%s" xmlns:dc="%s" xmlns:xlink="%s" name='%s' full-name='%s'>""" % ( FTSERVER_NAMESPACE, Schema.DC, XLINK_NAMESPACE, self.command.name, posixpath.basename(self.path) )
if self.command.description: doc += '<dc:Description>%s</dc:Description>\n' % self.command.description if self.command.example: doc += '<ftss:Example>%s</ftss:Example>\n' % self.command.example if self.command.verbose_description: doc += '<ftss:VerboseDescription>%s</ftss:VerboseDescription>\n' % self.command.verbose_description
doc += '<ftss:Options>'
for option in self.command.options: doc += self.buildOptionDoc(option, self.command)
doc += '</ftss:Options>\n'
doc += '<ftss:Arguments>\n' for a in self.command.arguments: doc += "<ftss:Argument name='%s' requirements = '%d'>" % (a.name, a.requirements) if a.description: doc += '<dc:Description>%s</dc:Description>\n' % a.description doc += "</ftss:Argument>" doc += '</ftss:Arguments>\n'
doc += '<ftss:SubCommands>\n' for name, cmd in self.command.subCommands.items(): doc += '<ftss:CommandReference xlink:type="simple" xlink:href="%s.%s" xlink:actuate="onLoad" xlink:show="embed"/>' % (self.cmdPath, name) doc += '</ftss:SubCommands>\n'
doc += """</ftss:Command>"""
return XmlLib.MakeString(doc)
def buildOptionDoc(self, option, command): from Ft.Lib.CommandLine import Options
if isinstance(option, Options.ExclusiveOptions): doc = '<ftss:ExclusiveOption>\n' for o in option.choices: doc += self.buildOptionDoc(o, command) doc += '</ftss:ExclusiveOption>\n'
elif isinstance(option, Options.TypedOption): doc = '<ftss:TypedOption '
if option.shortName: doc += 'short-name="%s" ' % option.shortName
doc += 'long-name="%s">\n' % option.longName if option.description: doc += '<dc:Description>%s</dc:Description>\n' % option.description
for value, desc in option.allowed: doc += '<ftss:Allowed name="%s">' % value doc += '<dc:Description>%s</dc:Description>\n' % desc doc += '</ftss:Allowed>'
if option.subOptions: doc += '<ftss:SubOptions>\n' for so in command.subOptions: doc += self.buildOptionDoc(so, command) doc += '</ftss:SubOptions>\n' doc += '</ftss:TypedOption>\n' else: #Normal option doc = '<ftss:Option ' if option.shortName: doc += 'short-name="%s" ' % option.shortName
if option.takesArg: doc += 'arg-name="%s" ' % option.argName
doc += 'long-name="%s">\n' % option.longName if option.description: doc += '<dc:Description>%s</dc:Description>\n' % option.description
if option.subOptions: doc += '<ftss:SubOptions>\n' for so in command.subOptions: doc += self.buildOptionDoc(so, command) doc += '</ftss:SubOptions>\n' doc += '</ftss:Option>\n'
return unicode(doc)
def BuildCommandDocs(command, baseName, acl):
if not command._fileName: raise "Command %s did not register a file name" % command.name
if baseName: path = baseName + '.' + command.name else: path = command.name
cmds = [ExtendedCommand('commands/'+path, acl, None, None, '/ftss/docdefs/dublin_core', command, path)] uri = Uri.OsPathToUri(command._fileName, attemptAbsolute=False) + '#ExtendedCommand-XML' cmds[0].setPath(uri)
for name, cmd in command.subCommands.items(): cmds.extend(BuildCommandDocs(cmd, path, acl))
return cmds
g_allComponents = {'Demos': 'The default 4Suite Repository Demonstration applications' ' and the server to access them', 'Docs': 'All 4Suite documentation', 'Servers': 'The default FtRpc and Ftp Server', 'Commands': '4ss and 4ss_manager commands and documentation;' ' these are needed to be able to use the 4ss and ' ' 4ss_manager command-line tools', 'Dashboard': 'The web-based control panel for 4Suite', 'Data': 'Commonly used 4Suite data and icons (required; must be ' ' first component installed)' }
g_allComponentNames = [c.lower() for c in g_allComponents]
#These actually need to be sorted a bit. Data needs to be first g_allComponentNames.remove('data') g_allComponentNames.insert(0, 'data')
def VerifyComponent(comp): if str(comp).lower() in g_allComponentNames: return str(comp).lower() raise ValueError("Component argument must be one of: %s." % ', '.join(g_allComponentNames))
g_componentHelp = """Specify which components to add/update in the repository. Allowed values are:\n\n"""
for name, value in g_allComponents.items(): g_componentHelp += "'%s' - %s;\n" % (name.capitalize(), value) g_componentHelp = g_componentHelp[:-2] + '.'
def Register(): from Ft.Lib.CommandLine import Options, Command, Arguments
OPTIONS = Options.Options([ Options.Option('c', 'confirm', 'Confirm before performing each sub-task.'), Options.Option('r', 'update', "Don't destroy the current repository, just update the data."), Options.Option('o', 'repo-only', 'Only create the bare repository (destroying any current one), and don\'t install any components. Only useful for development.'), ])
ARGUMENTS = [ Arguments.ZeroOrMoreArgument('components', g_componentHelp, VerifyComponent), ]
cmd = Command.Command('init', 'Initialize the 4Suite repository', 'Data Servers Dashboard', __doc__, function=CommandLineInit, options=OPTIONS, arguments=ARGUMENTS, fileName = __file__ ) return cmd
|