Commit d399c393 authored by David Johnson's avatar David Johnson
Browse files

Make our FreeNAS CLI wrapper work for version 9.x .

This wasn't too bad, but there were some model changes -- and a few
Django/whatever changes.

There are a few backwards-incompat changes to the CLI args for some
commands; probably we can finesse that if it matters.
parent 1ef72c38
#!/usr/bin/env python
import sys
import os
sys.path.append('/usr/local/www')
sys.path.append('/usr/local/www/freenasUI')
from freenasUI import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'freenasUI.settings')
# Make sure to load all modules
from django.db.models.loading import cache
cache.get_apps()
import django.http
import freenasUI.middleware.notifier
import freenasUI.urls
import freenasUI.network.models
import freenasUI.storage.models
import freenasUI.services.models
import freenasUI.freeadmin.views
from freenasUI.freeadmin.options import BaseFreeAdmin
class FakeHttpRequestPOST(django.http.HttpRequest):
def __init__(self,params=None):
super(FakeHttpRequestPOST,self).__init__()
self.META['HTTP_USER_AGENT'] = 'fake'
self.META['REMOTE_ADDR'] = '127.0.0.1'
self.META['REMOTE_HOST'] = 'localhost'
self.META['REQUEST_METHOD'] = 'POST'
# self.FILES = django.http.MultiValueDict()
self.POST = django.http.QueryDict('',mutable=True)
self.method = 'POST'
if params != None:
for (k,v) in params.iteritems():
self.add(k,v)
pass
pass
pass
def add(self,key,value):
if self.POST.has_key(key):
self.POST.appendlist(key,value)
else:
self.POST[key] = value
pass
pass
class CommandError(Exception):
pass
class ArgError(Exception):
pass
class InstanceNotFoundError(Exception):
pass
class ValueDoesNotExistError(Exception):
pass
class ValueExistsError(Exception):
pass
class MultipleValuesError(Exception):
pass
class CommandState(object):
def __init__(self,command,args,kwargs):
self.command = command
self.args = args
self.kwargs = kwargs
self.inline_adds = []
self.inline_dels = []
pass
pass
class CommandSet(object):
def __init__(self):
self.commands = {}
pass
def _check(self,stateobj):
command = stateobj.command
args = stateobj.args
kwargs = stateobj.kwargs
if command == 'list' or command == 'plist':
return
if not self.commands.has_key(command):
raise CommandError("Unknown command '%s'" % command)
cdict = self.commands[command]
alen = len(args)
if cdict.has_key('minArgs') and alen < cdict['minArgs']:
raise ArgError("Not enough arguments")
if cdict.has_key('maxArgs') \
and cdict['maxArgs'] > -1 and alen > cdict['maxArgs']:
raise ArgError("Too many arguments")
for (k,v) in kwargs.iteritems():
if not cdict.has_key('kwArgs') or not k in cdict['kwArgs']:
raise ArgError("Unknown keyword argument '%s'" % k)
pass
if cdict.has_key('reqKwArgs'):
for k in cdict['reqKwArgs']:
if not kwargs.has_key(k):
raise ArgError("Required keyword argument '%s' missing" % k)
pass
pass
#
# Only call this function after calling _check!
#
def _assembleRequest(self,stateobj):
command = stateobj.command
args = stateobj.args
kwargs = stateobj.kwargs
cdict = self.commands[command]
fakeReq = FakeHttpRequestPOST()
i = 0
model_var_prefix = self.loadattr(command,'_model_var_prefix')
for k in args:
if i >= len(cdict['argDictNames']):
# Vararg:
valist = self.handleVarArg(k,stateobj)
for (vk,vv) in valist:
fakeReq.add(vk,vv)
else:
rk = cdict['argDictNames'][i]
tk = cdict['argDictNames'][i]
if model_var_prefix != None and len(model_var_prefix) > 0:
tk = "%s_%s" % (model_var_prefix,cdict['argDictNames'][i])
# if this field needs a foreign filter lookup, do it!
if cdict.has_key('argForeignKeyModel') \
and cdict['argForeignKeyModel'].has_key(rk):
modarr = cdict['argForeignKeyModel'][rk].rsplit('.',1)
__import__(modarr[0])
model = getattr(sys.modules[modarr[0]],modarr[1])
vfilter = {}
vfilter[cdict['argForeignKeyName'][rk]] = k
values = model.objects.values().filter(**vfilter)
k = values[0]['id']
fakeReq.add(tk,k)
i += 1
pass
for (k,v) in kwargs.iteritems():
tk = k
if model_var_prefix != None and len(model_var_prefix) > 0:
tk = "%s_%s" % (model_var_prefix,k)
# if this field needs a foreign filter lookup, do it!
rk = k
if cdict.has_key('argForeignKeyModel') \
and cdict['argForeignKeyModel'].has_key(rk):
modarr = cdict['argForeignKeyModel'][rk].rsplit('.',1)
__import__(modarr[0])
model = getattr(sys.modules[modarr[0]],modarr[1])
vfilter = {}
vfilter[cdict['argForeignKeyName'][rk]] = v
values = model.objects.values().filter(**vfilter)
v = values[0]['id']
fakeReq.add(tk,v)
pass
for (k,v) in cdict['kwDefaults'].iteritems():
tk = k
if model_var_prefix != None and len(model_var_prefix) > 0:
tk = "%s_%s" % (model_var_prefix,k)
if not self.loadattr(command,'NO_AUTO_FILL') \
and not fakeReq.POST.has_key(tk):
fakeReq.add(tk,v)
pass
return fakeReq
def _getInstances(self,stateobj,filters):
#
# Only one of these, ever.
#
command = stateobj.command
model_prefix = self.loadattr(command,'_model_prefix')
modelname = self.loadattr(command,'_model')
model = eval('%s.%s' % (model_prefix,modelname))
if filters is not None:
values = model.objects.values().filter(**filters)
else:
values = model.objects.values()
return values
def _run(self,stateobj):
command = stateobj.command
args = stateobj.args
kwargs = stateobj.kwargs
self._check(stateobj)
model = self.loadattr(command,'_model')
model_prefix = self.loadattr(command,'_model_prefix')
model_var_prefix = self.loadattr(command,'_model_var_prefix')
rmodel_prefix = self.loadattr(command,'_model_prefix')
rmodelname = self.loadattr(command,'_model')
if rmodel_prefix and rmodelname:
rmodel = eval('%s.%s' % (rmodel_prefix,rmodelname))
else:
rmodel = None
if self.commands.has_key(command):
cdict = self.commands[command]
elif command == 'plist' and self.commands.has_key('list'):
cdict = self.commands['list']
else:
cdict = {}
#
# Handle lists first; they don't need a fake request
#
if command == 'list' or command == 'plist':
if cdict.has_key('translateKeys'):
tk = cdict['translateKeys']
else:
tk = {}
pretty = False
if command == 'plist':
pretty = True
pass
if self.loadattr(command,'_list_values') is not None:
values = self.loadattr(command,'_list_values')()
elif model is not None:
values = self._getInstances(stateobj,None)
else:
raise CommandError("no way to list instances -- bug?!")
fields = []
fieldwidths = {}
newvalues = []
if values is not None and len(values) > 0:
fields = values[0].keys()
fields.sort()
for f in fields:
if tk.has_key(f):
nf = tk[f]
else:
nf = f
pass
if model_var_prefix is not None \
and nf.startswith(model_var_prefix + "_"):
fieldwidths[f] = len(nf[len(model_var_prefix + "_"):])
else:
fieldwidths[f] = len(nf)
pass
pass
pass
else:
return
#
# Calc fieldwidths
#
for v in values:
nv = {}
for (k,v2) in v.iteritems():
nvi = "%s" % (str(v2),)
nl = len(nvi)
if not fieldwidths.has_key(k):
fieldwidths[k] = nl
elif nl > fieldwidths[k]:
fieldwidths[k] = nl
pass
nv[k] = nvi
pass
newvalues.append(nv)
pass
if pretty:
hw = 1
for fw in fieldwidths.values():
hw += 1 + fw + 1 + 1
pass
pass
# print header
if pretty:
print '-' * hw
sys.stdout.write("|")
if 'id' in fields:
if pretty:
sys.stdout.write(" %*s |" % (fieldwidths['id'],'id',))
else:
sys.stdout.write("id\t")
for f in fields:
if f == 'id':
continue
fn = f
if tk.has_key(fn):
fn = tk[fn]
pass
if model_var_prefix is not None \
and fn.startswith(model_var_prefix + "_"):
fn = fn[len(model_var_prefix + "_"):]
pass
if pretty:
sys.stdout.write(" %*s |" % (fieldwidths[f],fn,))
else:
sys.stdout.write("%s\t" % (fn,))
pass
pass
print ""
if pretty:
print '-' * hw
for v in newvalues:
if pretty:
sys.stdout.write("|")
if 'id' in fields:
if pretty:
sys.stdout.write(" %*s |" % (fieldwidths['id'],
str(v['id']),))
else:
sys.stdout.write("%s\t" % (str(v['id']),))
for f in fields:
if f == 'id':
continue
if pretty:
sys.stdout.write(" %*s |" % (fieldwidths[f],str(v[f]),))
else:
sys.stdout.write("%s\t" % (str(v[f]),))
pass
print ""
if pretty:
print '-' * hw
return
#
# Do other commands
#
fakeReq = self._assembleRequest(stateobj)
values = []
filters = {}
if model != None and cdict.has_key('instanceFilterArgs') \
and cdict['instanceFilterArgs'] != None:
for (k,v) in fakeReq.POST.iteritems():
#print "D: considering %s,%s" % (str(k),str(v))
if (model_var_prefix != None \
and k[len(model_var_prefix)+1:] \
in cdict['instanceFilterArgs']) \
or k in cdict['instanceFilterArgs']:
#print "D: adding %s" % (str(k),)
filters[k] = v
values = self._getInstances(stateobj,filters)
pass
print "D: filters=%s, values=%s" % (str(filters),str(values))
mf = self.loadattr(command,'_model_form')
if cdict['type'] == 'add':
if len(values):
raise ValueExistsError("already exists (%s)" % (str(values),))
# Handle formset values. In this case, we just add a bunch of new
# formset values.
inline = self.loadattr(command,'_inline')
if inline != None:
imodel = inline['model']
iformset_prefix = inline['formset_prefix']
imodel_var_prefix = inline['model_var_prefix']
iforeign_key = inline['foreign_key']
iforeign_key_short = inline['foreign_key_short']
ifields = inline['fields']
# dump the formset params
fakeReq.add('%s-TOTAL_FORMS' % (iformset_prefix,),
len(stateobj.inline_adds))
fakeReq.add('%s-INITIAL_FORMS' % (iformset_prefix,),0)
i = 0
for iadd in stateobj.inline_adds:
fakeReq.add("%s-%d-%s_%s" % (iformset_prefix,i,
imodel_var_prefix,
iforeign_key_short),'')
fakeReq.add("%s-%d-id" % (iformset_prefix,i),'')
j = 0
while j < len(inline['fields']):
fn = inline['fields'][j]
fakeReq.add("%s-%d-%s_%s" % (iformset_prefix,i,
imodel_var_prefix,fn),
iadd[j])
j += 1
pass
i += 1
pass
print "generic_model_add(%s,%s,%s)" \
% (str(fakeReq),str(self._app),str(model))
ret = BaseFreeAdmin(model=rmodel).add(fakeReq,mf=mf)
print "ret = %s" % (str(ret),)
pass
elif cdict['type'] == 'edit':
if len(values) == 0:
raise ValueDoesNotExistError("instance does not exist")
elif len(values) != 1:
raise MultipleValuesError("more than one matching value to edit")
if not self.loadattr(command,'NO_AUTO_EDIT'):
print "values = %s" % (str(values),)
for (k,v) in values[0].iteritems():
if k != 'id' and not fakeReq.POST.has_key(k):
tk = k
if model_var_prefix:
tk = k[len(model_var_prefix)+1:]
if v == None and cdict['kwDefaults'].has_key(tk):
fakeReq.add(k,cdict['kwDefaults'][tk])
elif v != None:
fakeReq.add(k,v)
pass
# Handle formset values. Basically, we load the referenced values,
# filtered by our editing value's id; and if we have scheduled
# anything for deletion, we add those attrs to teh formset; if we
# don't do anything to an attr
inline = self.loadattr(command,'_inline')
if inline != None:
pk_id = values[0]['id']
imodel = inline['model']
iformset_prefix = inline['formset_prefix']
imodel_var_prefix = inline['model_var_prefix']
iforeign_key = inline['foreign_key']
iforeign_key_short = inline['foreign_key_short']
ifields = inline['fields']
# load the values
__import__(model_prefix)
im = getattr(sys.modules[model_prefix],imodel)
ivfilter = {}
ivfilter['%s_%s' % (imodel_var_prefix,iforeign_key_short)] = pk_id
ivalues = im.objects.filter(**ivfilter).values()
print "pk = %d ; ivalues = %s" % (pk_id,str(ivalues),)
# dump the formset params
fakeReq.add('%s-TOTAL_FORMS' % (iformset_prefix,),
ivalues.count() + len(stateobj.inline_adds))
fakeReq.add('%s-INITIAL_FORMS' % (iformset_prefix,),
ivalues.count())
i = 0
for iv in ivalues:
fakeReq.add("%s-%d-%s_%s" % (iformset_prefix,i,
imodel_var_prefix,
iforeign_key_short),pk_id)
fakeReq.add("%s-%d-id" % (iformset_prefix,i),iv['id'])
for fn in inline['fields']:
fakeReq.add("%s-%d-%s_%s" % (iformset_prefix,i,
imodel_var_prefix,fn),
iv["%s_%s" % (imodel_var_prefix,fn)])
pass
# if this one is supposed to be deleted, mark it!
for idel in stateobj.inline_dels:
should_del = True
j = 0
while j < len(inline['fields']):
fn = inline['fields'][j]
if str(iv["%s_%s" % (imodel_var_prefix,fn)]) \
!= str(idel[j]):
should_del = False
break
j += 1
pass
if should_del:
fakeReq.add("%s-%d-DELETE" % (iformset_prefix,i),
'on')
break
pass
i += 1
pass
for iadd in stateobj.inline_adds:
fakeReq.add("%s-%d-%s_%s" % (iformset_prefix,i,
imodel_var_prefix,
iforeign_key_short),pk_id)
fakeReq.add("%s-%d-id" % (iformset_prefix,i),'')
j = 0
while j < len(inline['fields']):
fn = inline['fields'][j]
fakeReq.add("%s-%d-%s_%s" % (iformset_prefix,i,
imodel_var_prefix,fn),
iadd[j])
j += 1
pass
i += 1
pass
print "generic_model_edit(%s,%s,%s,%s)" \
% (str(fakeReq),str(self._app),str(model),
str(values[0]['id']))
ret = BaseFreeAdmin(model=rmodel).edit(fakeReq,values[0]['id'],mf=mf)
print "ret = %s" % (str(ret),)
pass
elif cdict['type'] == 'del':
if len(values) == 0:
raise ValueDoesNotExistError("instance does not exist")
for v in values:
print "generic_model_delete(%s,%s,%s,%s)" \
% (str(fakeReq),str(self._app),str(model),
str(v['id']))
ret = BaseFreeAdmin(model=rmodel).delete(fakeReq,v['id'],mf=mf)
print "ret = %s" % (str(ret),)
pass
elif cdict['type'] == 'custom':
modfunc = cdict['function'].rsplit('.',1)
if len(modfunc) == 2:
__import__(modfunc[0])
mod = sys.modules[modfunc[0]]
func = getattr(mod,modfunc[1])
else:
func = eval(modfunc[0])
args = self.assembleArgs(fakeReq,values,stateobj.command,
stateobj.args,stateobj.kwargs)
print 'DEBUG: calling custom %s on %s' % (str(func),str(args))
ret = func(*args)
print 'DEBUG: custom ret: %s' % (str(ret),)
pass
pass
def run(self,command,args,kwargs):
stateobj = CommandState(command,args,kwargs)
self._run(stateobj)
def loadattr(self,command,attr):
if self.commands.has_key(command) \
and self.commands[command].has_key(attr):
return self.commands[command][attr]
else:
return getattr(self,attr,None)
pass
class Network(CommandSet):
_help = 'Configure generic network settings'
def __init__(self):
super(Network,self).__init__()
self._app = 'network'
self._model_var_prefix = 'gc'
self._model_prefix = 'freenasUI.network.models'
self._model = 'GlobalConfiguration'
self.commands['config'] = \
dict({ 'minArgs' : 0, 'maxArgs' : 0,
'argDictNames' : [],
'kwArgs' : [ 'hostname','domain','ipv4gateway','ipv6gateway',
'nameserver1','nameserver2','nameserver3' ],
'kwDefaults' : {},
'instanceFilterArgs' : [],
'type' : 'edit' })
self.commands['list'] = self.commands['plist'] = {}
pass
pass
class Interface(CommandSet):
_help = 'Configure network interfaces'
def __init__(self):
super(Interface,self).__init__()
self._app = 'network'
self._model_var_prefix = 'int'
self._model_prefix = 'freenasUI.network.models'
self._model = 'Interfaces'
self._inline = { 'model' : 'Alias',
'formset_prefix' : 'alias_set',
'model_var_prefix' : 'alias',
'foreign_key' : 'interface_id',
'foreign_key_short' : 'interface',
'fields' : [ 'v4address','v4netmaskbit',
'v6address','v6netmaskbit' ] }
self.commands['add'] = \
dict({ 'minArgs' : 2, 'maxArgs' : -1,
'argDictNames' : [ 'interface','name' ],
'varArgs' : True,
'kwArgs' : [ 'dhcp','ipv6auto','options' ],
'kwDefaults' : {},
'instanceFilterArgs' : [ 'interface' ],
'type' : 'add' })
self.commands['edit'] = \
dict({ 'minArgs' : 1, 'maxArgs' : -1,
'argDictNames' : [ 'interface' ],
'varArgs' : True,
'kwArgs' : [ 'name','dhcp','ipv6auto','options' ],
'kwDefaults' : {},
'instanceFilterArgs' : [ 'interface' ],
'type' : 'edit' })
self.commands['del'] = \
dict({ 'minArgs' : 1, 'maxArgs' : 1,
'argDictNames' : [ 'interface' ],
'kwArgs' : [],
'kwDefaults' : {},
'instanceFilterArgs' : [ 'interface' ],
'type' : 'del' })
self.commands['list'] = self.commands['plist'] = {}
pass
def handleVarArg(self,arg,stateobj):
command = stateobj.command
retval = []
v6 = False
alias_add = False
alias_del = False
iface = False
if arg.startswith('+'):
# adding an alias
arg = arg[1:]
alias_add = True
elif arg.startswith('-'):