Also rename some commands according to some basic principles:
* select - all commands for filtering the list of objects
* show - displaying the current state (e.g. show selection now shows the
filtered list of objects)
* list - listing all items of a type, without filtering
Signed-off-by: Zane Bitter <zbitter(a)redhat.com>
---
src/python/matahari/api.py | 63 ++++++++++++--
src/python/matahari/core.py | 19 ++--
src/python/matahari/shell/interpreter/mode.py | 6 +
src/python/matahari/shell/mode.py | 114 ++++++++++++++++---------
4 files changed, 145 insertions(+), 57 deletions(-)
diff --git a/src/python/matahari/api.py b/src/python/matahari/api.py
index 718d814..3f3094b 100644
--- a/src/python/matahari/api.py
+++ b/src/python/matahari/api.py
@@ -9,14 +9,17 @@ class BrokerConnection(object):
class Proxy(object):
def __init__(self, state):
self._state = state
- agents = state.manager.agents(state._hosts)
self._objects = state.manager.get(state._class,
- package=state._package,
- agents=agents)
- self._schemata = set(o.getSchema() for o in self._objects)
+ state._package,
+ state.agents(),
+ **state._properties)
+ self._schemata = set(o.getSchema() for o in self)
self._methodnames = set(m.name for m in self._methods())
def __getattr__(self, attr):
+ if not self:
+ raise IndexError("No objects selected")
+
if attr in self._methodnames:
def call_method(*args):
return self._state.manager.invoke_method(self._objects,
@@ -24,14 +27,17 @@ class Proxy(object):
*args)
return call_method
else:
- return [getattr(o, attr) for o in self._objects]
+ return [getattr(o, attr) for o in self]
def __setattr__(self, attr, val):
if attr.startswith('_'):
self.__dict__[attr] = val
return
- for o in self._objects:
+ if not self:
+ raise IndexError("No objects selected")
+
+ for o in self:
setattr(o, attr, val)
def __str__(self):
@@ -40,14 +46,26 @@ class Proxy(object):
def __repr__(self):
return repr(self._objects)
+ def __len__(self):
+ return len(self._objects)
+
+ def __iter__(self):
+ return iter(self._objects)
+
+ def _set_from_schemata(self, transform):
+ if self:
+ return set.union(*[set(transform(s)) for s in self._schemata])
+ else:
+ return set()
+
def _properties(self):
- return set.union(*[set(s.getProperties()) for s in self._schemata])
+ return self._set_from_schemata(lambda s: s.getProperties())
def _statistics(self):
- return set.union(*[set(s.getStatistics()) for s in self._schemata])
+ return self._set_from_schemata(lambda s: s.getStatistics())
def _methods(self):
- return set.union(*[set(s.getMethods()) for s in self._schemata])
+ return self._set_from_schemata(lambda s: s.getMethods())
class CacheDescriptor(object):
def __init__(self, constructor):
@@ -80,26 +98,52 @@ class Matahari(object):
self._hosts = None
self._package = None
self._class = None
+ self._properties = {}
def set_hosts(self, hosts=None):
+ """Filter on a list of hosts"""
self._hosts = hosts
del self.objects
def set_class(self, klass, package='org.matahariproject'):
+ """Filter on a particular class and package"""
self._class = klass
self._package = package
+ self._properties = {}
del self.objects
+ def set_property_filter(self, name, value):
+ """Add a filter on a property value"""
+ self._properties[name] = value
+ del self.objects
+
+ def clear_property_filter(self):
+ """Clear filters on property values"""
+ self._properties = {}
+ del self.objects
+
+ def clear_state(self):
+ """Clear all state"""
+ self.clear_property_filter()
+ self.set_class(None, None)
+ self.set_hosts()
+
def selected_classname(self):
+ """Return the name of the currently selected
class"""
+ if self._class is None:
+ return ''
return '%s:%s' % (self._package, self._class)
def list_hosts(self):
+ """List all hosts with connected agents"""
return set(self.manager.hosts())
def list_packages(self):
+ """List all available packages"""
return set(self._qmf.packages)
def list_classnames(self, package=None):
+ """List all available classnames, optionally for only a given
package"""
classes = list(self._qmf.classes)
pkgmatch = (package is None and
(lambda k: True) or
@@ -107,4 +151,5 @@ class Matahari(object):
return set([k.getClassName() for k in classes if pkgmatch(k)])
def agents(self):
+ """Return a list of all connected agents on the selected
hosts"""
return set(self.manager.agents(self._hosts))
diff --git a/src/python/matahari/core.py b/src/python/matahari/core.py
index 0dd8ecb..0f28463 100644
--- a/src/python/matahari/core.py
+++ b/src/python/matahari/core.py
@@ -138,20 +138,21 @@ class Manager(object):
agents = _chain(self._agents(uuid=h.uuid()) for h in hl)
return frozenset(agents)
- def get(self, classname, package='org.matahariproject', agents=None):
+ def get(self, _class, _package='org.matahariproject',
+ _agents=None, **kwargs):
"""Get all objects of the specified type"""
- def getObjects(**kwargs):
- return self._session.getObjects(_class=classname,
- _package=package, **kwargs)
+ def getObjects(**kws):
+ return self._session.getObjects(_class=_class,
+ _package=_package, **kws)
- if agents is None:
- obj = getObjects()
+ if _agents is None:
+ obj = getObjects(**kwargs)
else:
try:
- al = iter(agents)
+ al = iter(_agents)
except TypeError:
- al = [agents]
- obj = _chain(getObjects(_agent=a.agent()) for a in al)
+ al = [_agents]
+ obj = _chain(getObjects(_agent=a.agent(), **kwargs) for a in al)
return tuple(obj)
def invoke_method(self, objs, method, *args, **kwargs):
diff --git a/src/python/matahari/shell/interpreter/mode.py
b/src/python/matahari/shell/interpreter/mode.py
index a70dc50..82c781a 100644
--- a/src/python/matahari/shell/interpreter/mode.py
+++ b/src/python/matahari/shell/interpreter/mode.py
@@ -19,6 +19,12 @@ class Mode(object):
def __init__(self, *commands):
"""Initialise with an initial set of commands."""
+
+ # Avoid double initialisation
+ if hasattr(self, '__initialised'):
+ return
+ self.__initialised = True
+
self.shell = None
self.commands = {}
for m in dir(type(self)):
diff --git a/src/python/matahari/shell/mode.py b/src/python/matahari/shell/mode.py
index 7d05b5f..57a09c1 100644
--- a/src/python/matahari/shell/mode.py
+++ b/src/python/matahari/shell/mode.py
@@ -18,67 +18,73 @@ class RootMode(Mode):
select_wrapper = Command('select', 'host',
[types.Host(self.state)])
- self += select_wrapper(self.select)
+ self += select_wrapper(self.select_hosts)
pkg_state = types.SharedInfo('org.matahariproject')
- class_wrapper = Command('class',
+ class_wrapper = Command('select', 'class',
('package',
types.Package(self.state, pkg_state)),
types.Class(self.state, pkg_state))
- self += class_wrapper(self.package_class)
+ self += class_wrapper(self.select_class)
def writelist(self, objects):
self.write(*[str(o) for o in objects])
- def getFilteredMode(self):
- return FilteredMode(self.state)
+ def getHostMode(self):
+ return HostMode(self.state)
def getClassMode(self):
return ClassMode(self.state)
- @Command('show', 'hosts')
- def show_hosts(self, *kws):
+ @Command('list', 'hosts')
+ def list_hosts(self, *kws):
"""Show the list of connected hosts."""
self.writelist(self.state.list_hosts())
- @Command('show', 'agents')
- def show_agents(self, *kws):
+ @Command('list', 'agents')
+ def list_agents(self, *kws):
"""Show the list of connected agents."""
self.writelist(self.state.agents())
- def select(self, kw_select, kw_host, *arg_hosts):
- """ Select a set of hosts to restrict commands
to."""
+ @Command('show', 'selection')
+ def show_selection(self, *kws):
+ """Show the list of selected objects."""
+ self.writelist(self.state.objects)
+
+ @Command('clear', 'all')
+ def clear_all(self, kw_clear, kw_class):
+ """Clear all selection filters"""
+ self.state.clear_state()
+ self.shell.set_mode(RootMode(self.state))
+
+ def select_hosts(self, kw_select, kw_host, *arg_hosts):
+ """Select a set of hosts to restrict commands
to."""
self.state.set_hosts(arg_hosts)
- self.shell.set_mode(self.getFilteredMode())
+ self.shell.set_mode(self.getHostMode())
- def package_class(self, kw_class, (kw_package, package), klass):
- """ Select an object class to operate on."""
+ def select_class(self, kw_select, kw_class, (kw_package, package), klass):
+ """Select an object class to operate on."""
self.state.set_class(klass, package)
self.shell.set_mode(self.getClassMode())
-class FilteredMode(RootMode):
+class HostMode(RootMode):
"""Shell mode filtered by host"""
def __init__(self, state):
RootMode.__init__(self, state)
- def getUnfilteredMode(self):
+ def getNoHostMode(self):
return RootMode(self.state)
def getClassMode(self):
- return FilteredClassMode(self.state)
+ return HostClassMode(self.state)
- @Command('clear', 'selection')
- def clear_selection(self, kw_clear, kw_selection):
+ @Command('clear', 'host')
+ def clear_host(self, kw_clear, kw_selection):
"""Clear the current host selection."""
self.state.set_hosts(None)
- self.shell.set_mode(self.getUnfilteredMode())
-
- @Command('show', 'selection')
- def show_selection(self, *kws):
- """Show the current host selection."""
- self.writelist(self.state.hosts)
+ self.shell.set_mode(self.getNoHostMode())
def prompt(self):
return '(host)'
@@ -164,6 +170,30 @@ class SetPropertyHandler(object):
doc = "Set the value of the %s property for the selected objects"
return (doc % (str(self._arg),)) + '\nDetails:\n' + self._arg.help()
+class FilterPropertyHandler(object):
+ """CLI handler for setting an object property"""
+
+ def __new__(cls, mode, prop):
+ handler = super(FilterPropertyHandler, cls).__new__(cls, mode, prop)
+ FilterPropertyHandler.__init__(handler, mode, prop)
+ wrapper = Command('select', 'property', repr(prop),
handler._arg)
+ return wrapper(handler)
+
+ def __init__(self, mode, prop):
+ self._mode = mode
+ self._prop = prop
+ self._arg = types.QMFProperty(prop)
+ self.__name__ = 'filter_property'
+ self.__doc__ = self.help()
+
+ def __call__(self, kw_select, kw_property, arg_property, arg_value):
+ self._mode.state.set_property_filter(arg_property, arg_value)
+ self._mode.shell.set_mode(self._mode)
+
+ def help(self):
+ doc = "Select objects with a specified value for their %s property"
+ return (doc % (str(self._arg),)) + '\nDetails:\n' + self._arg.help()
+
class ClassMode(RootMode):
"""Shell mode filtered by QMF class."""
@@ -177,44 +207,50 @@ class ClassMode(RootMode):
if int(prop.access) == READ_WRITE:
self += SetPropertyHandler(self, prop)
+ self += FilterPropertyHandler(self, prop)
+
for stat in self.state.objects._statistics():
self += GetPropertyHandler(self, stat, True)
for method in self.state.objects._methods():
self += CallMethodHandler(self, method)
- def getFilteredMode(self):
- return FilteredClassMode(self.state)
+ def getHostMode(self):
+ return HostClassMode(self.state)
- def getUnclassifiedMode(self):
+ def getNoClassMode(self):
return RootMode(self.state)
@Command('clear', 'class')
def clear_class(self, kw_clear, kw_class):
+ """Clear the current class selection"""
self.state.set_class(None, None)
- self.shell.set_mode(self.getUnclassifiedMode())
+ self.shell.set_mode(self.getNoClassMode())
- @Command('select', 'property', 'PROPERTY', 'VALUE')
- def select_property(self, kw_select, kw_property, arg_property, arg_value):
- pass
+ @Command('clear', 'properties')
+ def clear_properties(self, kw_clear, kw_class):
+ """Clear any current property selection filters"""
+ self.state.clear_property_filter()
+ self.shell.set_mode(self)
def prompt(self):
- return '[%s]' % (self.state.selected_classname(),)
+ return '[%s]%s' % (self.state.selected_classname(),
+ self.state._properties and '*' or '')
-class FilteredClassMode(FilteredMode, ClassMode):
+class HostClassMode(HostMode, ClassMode):
"""Shell mode filtered by host and QMF class."""
def __init__(self, state):
- FilteredMode.__init__(self, state)
+ HostMode.__init__(self, state)
ClassMode.__init__(self, state)
- def getUnfilteredMode(self):
+ def getNoHostMode(self):
return ClassMode(self.state)
- def getUnclassifiedMode(self):
- return FilteredMode(self.state)
+ def getNoClassMode(self):
+ return HostMode(self.state)
def prompt(self):
- return FilteredMode.prompt(self) + ClassMode.prompt(self)
+ return HostMode.prompt(self) + ClassMode.prompt(self)