[cura-tools] master: introduce constant values access (62011f5)
by phatina@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/cura-tools.git
On branch : master
>---------------------------------------------------------------
commit 62011f550d197423715d91d9d0aa664fb5bc835c
Author: Peter Hatina <phatina(a)redhat.com>
Date: Wed Jul 17 09:54:04 2013 +0200
introduce constant values access
>---------------------------------------------------------------
cli-tools/lmi/lmi_client_shell.py | 175 +++++++++++++++++++++++++++++++++----
cli-tools/lmishell | 26 +++++-
2 files changed, 181 insertions(+), 20 deletions(-)
diff --git a/cli-tools/lmi/lmi_client_shell.py b/cli-tools/lmi/lmi_client_shell.py
index 8c25cec..87d0cb6 100644
--- a/cli-tools/lmi/lmi_client_shell.py
+++ b/cli-tools/lmi/lmi_client_shell.py
@@ -494,6 +494,92 @@ def lmi_class_fetch_lazy(fn):
return fn(self, *args, **kwargs)
return wrapped
+class _LmiConstantValues(BaseObject):
+ """
+ Abstract class for constant value objects.
+ """
+ __metaclass__ = abc.ABCMeta
+
+ def __init__(self, cim_property):
+ # Keys can contain various undesirable characters, such as python
+ # operators, etc. So we drop them.
+ keys = map(lambda v: "".join(c for c in v if c.isalnum()),
+ cim_property.qualifiers["Values"].value)
+ values = cim_property.qualifiers["ValueMap"].value
+ self._value_map = {}
+ for i in range(0, len(keys)):
+ try:
+ self._value_map[keys[i]] = cast_lmi_to_cim(self._cast_type, values[i])
+ except ValueError, e:
+ # Can not cast such value as interval. Can be found in
+ # DMTFReserved, VendorReserved values.
+ pass
+
+ def __repr__(self):
+ """
+ Returns a string of all constant names with corresponding value.
+ """
+ result = ""
+ for (k, v) in self._value_map.iteritems():
+ result += "%s = %s\n" % (k, v)
+ return result[:-1]
+
+ def __getattr__(self, name):
+ """
+ Returns either a member of the class, or a constant value.
+
+ Simplifies the code and constant value can be retreived by
+ object.constant_value.
+ """
+ if name in self.__dict__:
+ return self.__dict__[name]
+ if name in self._value_map:
+ return self._value_map[name]
+ raise AttributeError(name)
+
+ def print_values(self):
+ """
+ Prints all available constant names.
+ """
+ for k in self._value_map.keys():
+ sys.stdout.write("%s\n" % k)
+
+ def values_dict(self):
+ """
+ Returns a dictionary of constants' names and values.
+ """
+ return self._value_map
+
+ def values(self):
+ """
+ Returns a list of all available constant values.
+ """
+ return self._value_map.keys()
+
+ def value(self, value_name):
+ """
+ Returns a constant value.
+ Arguments:
+ value_name -- string containing a constant name
+ """
+ return getattr(self, value_name)
+
+class _LmiConstantValuesParamProp(_LmiConstantValues):
+ """
+ Derived class used for constant values of CIMProperty and CIMParameter.
+ """
+ def __init__(self, cim_property):
+ self._cast_type = cim_property.type
+ super(self.__class__, self).__init__(cim_property)
+
+class _LmiContantValuesMethodReturnType(_LmiConstantValues):
+ """
+ Derived class used for constant values of CIMMethod.
+ """
+ def __init__(self, cim_property):
+ self._cast_type = cim_property.return_type
+ super(self.__class__, self).__init__(cim_property)
+
class _LmiClass(BaseObject):
def __init__(self, client, namespace, classname):
super(self.__class__, self).__init__()
@@ -506,6 +592,21 @@ class _LmiClass(BaseObject):
return "%s(classname='%s', ...)" % (self.__class__.__name__, self.classname)
@lmi_class_fetch_lazy
+ def __getattr__(self, name):
+ """
+ Returns either a class member, or a constant value.
+
+ Simplifies the code and constant value can be retreived by
+ object.constant_value.
+ """
+ if name in self.__dict__:
+ return self.__dict__[name]
+ if name.endswith("Values"):
+ property_name = name[:-6]
+ return _LmiConstantValuesParamProp(self._cim_class.properties[property_name])
+ raise AttributeError(name)
+
+ @lmi_class_fetch_lazy
@lmi_return_val_if_fail("_namespace", None)
def create_instance(self, properties = {}, qualifiers = {}, property_list = None):
self_properties = self._cim_class.properties
@@ -537,6 +638,9 @@ class _LmiClass(BaseObject):
return
(self._cim_class, _, _) = self._client._get_class(self._cim_classname,
self._namespace.name, LocalOnly = False)
+ # Store the constant values as a list. This can consume some time, if computed on demand.
+ self._valuemap_properties_list = [ k for (k, v) in self._cim_class.properties.iteritems() \
+ if "ValueMap" in v.qualifiers ]
# NOTE: usage with Key=something, Value=something is deprecated
# NOTE: inst_filter is either None or dict
@@ -594,6 +698,14 @@ class _LmiClass(BaseObject):
@lmi_class_fetch_lazy
@lmi_return_val_if_fail("_namespace", [])
+ def valuemap_properties(self):
+ """
+ Returns a list of constant names.
+ """
+ return self._valuemap_properties_list
+
+ @lmi_class_fetch_lazy
+ @lmi_return_val_if_fail("_namespace", [])
def properties(self):
return self._cim_class.properties.keys()
@@ -687,26 +799,26 @@ def possibly_deleted(fn):
class _LmiInstanceMethod(BaseObject):
# 15 seconds sleep timeout for main waiting thread
- COND_WAIT_TIME = 15
+ _COND_WAIT_TIME = 15
# Wake count of main thread, when the GetInstance is performed to check,
# if the job object is present. Prevents infinite waiting for indication
# delivery. Maximum waiting time, before the GetInstance for job object
- # will be called is: COND_WAIT_TIME * COND_WAIT_WAKE_CNT
- COND_WAIT_WAKE_CNT = 8
+ # will be called is: _COND_WAIT_TIME * _COND_WAIT_WAKE_CNT
+ _COND_WAIT_WAKE_CNT = 8
# Default tcp port, where the indications will be delivered.
# TODO: create a configuration option for the port
- INDICATION_DESTINATION_PORT = 10240
+ _INDICATION_DESTINATION_PORT = 10240
# Job classes, which can be used for synchro method calls
# TODO: create a configuration option for the static filters' classnames
- INDICATION_FILTER_CLASSNAMES = (
+ _INDICATION_FILTER_CLASSNAMES = (
"LMI_StorageJob",
"LMI_SoftwareInstallationJob",
"LMI_NetworkJob"
)
# When performing a synchronous method call and using the polling method to
# get a job object status, the sleep time between 2 polls doubles if it is
- # less than POLLING_ADAPT_MAX_WAITING_TIME.
- POLLING_ADAPT_MAX_WAITING_TIME = 128
+ # less than _POLLING_ADAPT_MAX_WAITING_TIME.
+ _POLLING_ADAPT_MAX_WAITING_TIME = 128
def __init__(self, client, lmi_instance, method, sync_method):
super(self.__class__, self).__init__()
@@ -714,9 +826,15 @@ class _LmiInstanceMethod(BaseObject):
self._lmi_instance = lmi_instance
self._method = lmi_instance._lmi_class._cim_class.methods[method]
self._sync_method = sync_method
+ # Store the constant values as a list. This can consume some time, if computed on demand.
+ self._valuemap_parameters = [ k for (k, v) in self._method.parameters.iteritems() \
+ if "ValueMap" in v.qualifiers ]
+ # For simplicity, we add return value constans to the same list
+ if "ValueMap" in self._method.qualifiers:
+ self._valuemap_parameters.append(self._method.name)
@staticmethod
- def is_job_finished(job):
+ def _is_job_finished(job):
"""
Checks if a job object is in finished state. The range comes from the standard.
Arguments:
@@ -793,7 +911,7 @@ class _LmiInstanceMethod(BaseObject):
job_exception = PassByRef(None)
# There needs to be a pattern of at least 8 "X" in a row at the end of the indication_name
indication_name = "synchro-method-call-XXXXXXXX"
- listener = LmiIndicationListener("0.0.0.0", _LmiInstanceMethod.INDICATION_DESTINATION_PORT)
+ listener = LmiIndicationListener("0.0.0.0", _LmiInstanceMethod._INDICATION_DESTINATION_PORT)
indication_name = listener.add_handler(indication_name, handle_job, cond, job_finished,
job_out_params, job_exception)
if not listener.start():
@@ -820,7 +938,7 @@ class _LmiInstanceMethod(BaseObject):
# additional configuration to make this work. See LmiInstanceMethod() and PreferPolling.
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
- s.connect((netloc, _LmiInstanceMethod.INDICATION_DESTINATION_PORT))
+ s.connect((netloc, _LmiInstanceMethod._INDICATION_DESTINATION_PORT))
except socket.gaierror, e:
listener.stop()
errorstr = "Can not determine IP address of this machine"
@@ -832,7 +950,7 @@ class _LmiInstanceMethod(BaseObject):
cim_handler_props = {
"Name" : indication_name,
"Destination" : "http://%s:%d/%s" % (destination,
- _LmiInstanceMethod.INDICATION_DESTINATION_PORT, indication_name),
+ _LmiInstanceMethod._INDICATION_DESTINATION_PORT, indication_name),
}
(cim_handler, _, err) = self._client._create_instance("CIM_IndicationHandlerCIMXML",
"root/PG_InterOp", cim_handler_props)
@@ -857,7 +975,7 @@ class _LmiInstanceMethod(BaseObject):
# Check, if the job is not already in finished state,
# while we were subscribing for the indications
job_inst.refresh()
- job_finished.value = _LmiInstanceMethod.is_job_finished(job_inst)
+ job_finished.value = _LmiInstanceMethod._is_job_finished(job_inst)
if job_finished.value:
job_out_params.value = job_inst.JobOutParameters
@@ -869,19 +987,19 @@ class _LmiInstanceMethod(BaseObject):
while True:
if interrupt or job_finished.value:
break
- cond.wait(_LmiInstanceMethod.COND_WAIT_TIME)
+ cond.wait(_LmiInstanceMethod._COND_WAIT_TIME)
wake_cnt += 1
# XXX: threading.Condition.wait() does not inform about timeout or being awaken by
# notify call. There is a counting to 4 sleep cycles before we actually check for
# job status manually. This number can be increased, so we rely more on indications,
# rather then on manual polling.
- if not job_finished.value and wake_cnt >= _LmiInstanceMethod.COND_WAIT_WAKE_CNT:
+ if not job_finished.value and wake_cnt >= _LmiInstanceMethod._COND_WAIT_WAKE_CNT:
wake_cnt = 0
(refreshed, _, errorstr) = job_inst.refresh()
if not refreshed:
job_exception.value = errorstr
break
- if _LmiInstanceMethod.is_job_finished(job_inst):
+ if _LmiInstanceMethod._is_job_finished(job_inst):
job_out_params.value = job_inst.JobOutParameters
listener.stop()
break
@@ -905,10 +1023,10 @@ class _LmiInstanceMethod(BaseObject):
def __handle_synchro_method_call_polling(self, job_inst):
try:
sleep_time = 1
- while not _LmiInstanceMethod.is_job_finished(job_inst):
+ while not _LmiInstanceMethod._is_job_finished(job_inst):
# Sleep, a bit longer in every iteration
time.sleep(sleep_time)
- if sleep_time < _LmiInstanceMethod.POLLING_ADAPT_MAX_WAITING_TIME:
+ if sleep_time < _LmiInstanceMethod._POLLING_ADAPT_MAX_WAITING_TIME:
sleep_time *= 2
(refreshed, _, errorstr) = job_inst.refresh()
if not refreshed:
@@ -920,6 +1038,7 @@ class _LmiInstanceMethod(BaseObject):
rval = 0,
rparams = {"out" : job_inst.JobOutParameters}
)
+
def __call__(self, *args, **kwargs):
"""
Perform a method call.
@@ -965,7 +1084,7 @@ class _LmiInstanceMethod(BaseObject):
use_display_hook = True)
# Check if we can perform synchronous method call
job = call_rparams["job"] if "job" in call_rparams else None
- can_perform_sync_call = job.classname in _LmiInstanceMethod.INDICATION_FILTER_CLASSNAMES if job else False
+ can_perform_sync_call = job.classname in _LmiInstanceMethod._INDICATION_FILTER_CLASSNAMES if job else False
if self._sync_method and job and can_perform_sync_call:
# Synchronous method calls
job_inst = call_rparams['job'].to_instance()
@@ -1019,6 +1138,20 @@ class _LmiInstanceMethod(BaseObject):
rparams = call_rparams,
errorstr = call_errorstr)
+ def __getattr__(self, name):
+ """
+ Returns either a class member, or a constant value.
+ """
+ if name in self.__dict__:
+ return self.__dict__[name]
+ if name.endswith("Values"):
+ parameter_name = name[:-6]
+ if parameter_name in self._method.parameters:
+ return _LmiConstantValuesParamProp(self._method.parameters[parameter_name])
+ elif parameter_name == self._method.name:
+ return _LmiContantValuesMethodReturnType(self._method)
+ raise AttributeError(name)
+
@property
def return_type(self):
return self._method.return_type
@@ -1026,6 +1159,12 @@ class _LmiInstanceMethod(BaseObject):
def tomof(self):
_LmiMofFormatter(self._method.tomof()).fancy_format(self._client.interactive)
+ def valuemap_parameters(self):
+ """
+ Returns a list of constant names.
+ """
+ return self._valuemap_parameters
+
def parameters(self):
return self._method.parameters
diff --git a/cli-tools/lmishell b/cli-tools/lmishell
index bbbe0df..b9e16db 100755
--- a/cli-tools/lmishell
+++ b/cli-tools/lmishell
@@ -31,6 +31,8 @@ from lmi.lmi_client_shell import _LmiNamespaceRoot
from lmi.lmi_client_shell import _LmiClass
from lmi.lmi_client_shell import _LmiInstance
from lmi.lmi_client_shell import _LmiInstanceName
+from lmi.lmi_client_shell import _LmiInstanceMethod
+from lmi.lmi_client_shell import _LmiConstantValues
from lmi.lmi_client_shell import _lmi_start_indication_listener
from lmi.lmi_client_shell import _lmi_stop_indication_listener
from lmi.lmi_client_shell import _lmi_print_indication_listeners
@@ -116,6 +118,15 @@ class _LmiShellOptions(object):
sys.stdout.write(" -i, --interact inspect interactively after running a script\n")
class _LmiCompleter(rlcompleter.Completer):
+ LMI_CLASSES = (
+ _LmiNamespace,
+ _LmiClass,
+ _LmiInstance,
+ _LmiInstanceName,
+ _LmiConstantValues,
+ LmiReturnValue
+ )
+
def __init__(self, namespace = None):
rlcompleter.Completer.__init__(self, namespace)
self._last_complete = []
@@ -133,8 +144,7 @@ class _LmiCompleter(rlcompleter.Completer):
return result
def _callable_postfix(self, val, word):
- if hasattr(val, '__call__') \
- and not isinstance(val, (_LmiNamespace, _LmiClass, _LmiInstance, _LmiInstanceName, LmiReturnValue)):
+ if hasattr(val, '__call__') and not isinstance(val, _LmiCompleter.LMI_CLASSES):
word = word + "("
return word
@@ -160,6 +170,10 @@ class _LmiCompleter(rlcompleter.Completer):
for c in expr.classes():
if c.lower().startswith(to_complete.lower()):
self._last_complete.append(cmd + "." + c)
+ elif isinstance(expr, _LmiClass):
+ for v in expr.valuemap_properties():
+ if v.lower().startswith(to_complete.lower()):
+ self._last_complete.append(cmd + "." + v + "Values")
elif isinstance(expr, _LmiInstance):
expr_methods = expr.methods()
if expr_methods:
@@ -181,6 +195,14 @@ class _LmiCompleter(rlcompleter.Completer):
for p in expr.key_properties():
if p.lower().startswith(to_complete.lower()):
self._last_complete.append(cmd + "." + p)
+ elif isinstance(expr, _LmiInstanceMethod):
+ for p in expr.valuemap_parameters():
+ if p.lower().startswith(to_complete.lower()):
+ self._last_complete.append(cmd + "." + p + "Values")
+ elif isinstance(expr, _LmiConstantValues):
+ for v in expr.values():
+ if v.lower().startswith(to_complete.lower()):
+ self._last_complete.append(cmd + "." + v)
elif isinstance(expr, LmiReturnValue):
for p in expr.properties():
if p.lower().startswith(to_complete.lower()):
10 years, 10 months
[cura-tools] master: introduce _LmiClass::fetch() (b0365c7)
by phatina@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/cura-tools.git
On branch : master
>---------------------------------------------------------------
commit b0365c7f5efbc339307c9029dc162b3ac5edf9b4
Author: Peter Hatina <phatina(a)redhat.com>
Date: Tue Jul 16 12:50:29 2013 +0200
introduce _LmiClass::fetch()
>---------------------------------------------------------------
cli-tools/lmi/lmi_client_shell.py | 16 ++++++++++++----
1 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/cli-tools/lmi/lmi_client_shell.py b/cli-tools/lmi/lmi_client_shell.py
index f31ded2..8c25cec 100644
--- a/cli-tools/lmi/lmi_client_shell.py
+++ b/cli-tools/lmi/lmi_client_shell.py
@@ -490,10 +490,7 @@ class _LmiNamespaceRoot(_LmiNamespace):
def lmi_class_fetch_lazy(fn):
def wrapped(self, *args, **kwargs):
if not self._cim_class:
- # FIXME: this is wrong, throw an exception?
- if self._namespace:
- (self._cim_class, _, _) = self._client._get_class(
- self._cim_classname, self._namespace.name, LocalOnly = False)
+ self.fetch()
return fn(self, *args, **kwargs)
return wrapped
@@ -530,6 +527,17 @@ class _LmiClass(BaseObject):
def doc(self):
_LmiXmlFormatter(self._cim_class.tocimxml()).fancy_format(self._client.interactive)
+ def fetch(self):
+ """
+ Calls GetClass() method. Manually fetches a CIMClass.
+ """
+ if self._cim_class or not self._namespace:
+ # We already have CIMClass, or we do not know the namespace, from which
+ # the class should be fetched.
+ return
+ (self._cim_class, _, _) = self._client._get_class(self._cim_classname,
+ self._namespace.name, LocalOnly = False)
+
# NOTE: usage with Key=something, Value=something is deprecated
# NOTE: inst_filter is either None or dict
@lmi_return_val_if_fail("_namespace", [])
10 years, 10 months
[cura-tools] master: introduce _LmiClass::new_instance_name() (b8dc577)
by phatina@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/cura-tools.git
On branch : master
>---------------------------------------------------------------
commit b8dc5772e50af7336d0250aa1e25b55dd8684771
Author: Peter Hatina <phatina(a)redhat.com>
Date: Tue Jul 16 09:56:42 2013 +0200
introduce _LmiClass::new_instance_name()
>---------------------------------------------------------------
cli-tools/lmi/lmi_client_shell.py | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/cli-tools/lmi/lmi_client_shell.py b/cli-tools/lmi/lmi_client_shell.py
index acfbc6f..f31ded2 100644
--- a/cli-tools/lmi/lmi_client_shell.py
+++ b/cli-tools/lmi/lmi_client_shell.py
@@ -543,6 +543,14 @@ class _LmiClass(BaseObject):
result.append(_LmiInstanceName(self._client, i))
return result
+ def new_instance_name(self, keybindings, **kwargs):
+ """
+ Create new _LmiInstanceName object by passing all the keys/values of the object.
+ """
+ cim_inst_name = pywbem.CIMInstanceName(self.classname, namespace = self.namespace,
+ keybindings = keybindings, **kwargs)
+ return _LmiInstanceName(self._client, cim_inst_name)
+
# NOTE: usage with Key=something, Value=something is deprecated
# NOTE: inst_filter is either None or dict
def first_instance_name(self, inst_filter = None, **kwargs):
10 years, 10 months
[cura-tools] master: introduce synchronous method calls (3100815)
by phatina@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/cura-tools.git
On branch : master
>---------------------------------------------------------------
commit 31008156949aa36cee715091d06044fdd82bda84
Author: Peter Hatina <phatina(a)redhat.com>
Date: Tue Jul 16 09:07:48 2013 +0200
introduce synchronous method calls
>---------------------------------------------------------------
cli-tools/lmi/__init__.py | 41 +++++
cli-tools/lmi/lmi_client_shell.py | 298 ++++++++++++++++++++++++++++++++++++-
cli-tools/lmi/lmi_listener.py | 128 +++++++++++++++-
cli-tools/lmishell | 25 ++-
4 files changed, 470 insertions(+), 22 deletions(-)
diff --git a/cli-tools/lmi/__init__.py b/cli-tools/lmi/__init__.py
index e69de29..5de5e73 100644
--- a/cli-tools/lmi/__init__.py
+++ b/cli-tools/lmi/__init__.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+# Copyright (C) 2012-2013 Peter Hatina <phatina(a)redhat.com>
+#
+# 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 2
+# 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, see <http://www.gnu.org/licenses/>.
+
+"""
+LmiShell
+"""
+
+from lmi_listener import LmiHandlerNamePatternError
+from lmi_listener import LmiIndicationListener
+from lmi_listener import PassByRef
+from lmi_client_base import LmiReturnValueError
+from lmi_client_base import LmiFilterError
+from lmi_client_base import LmiReturnValue
+from lmi_client_base import LmiBaseClient
+from lmi_client_shell import LmiDeletedObjectError
+from lmi_client_shell import LmiUnknownParameterError
+from lmi_client_shell import LmiUnknownPropertyError
+from lmi_client_shell import LmiMethodCallError
+from lmi_client_shell import LmiSynchroMethodCallError
+from lmi_client_shell import LmiNoPagerError
+from lmi_client_shell import LmiIndicationError
+from lmi_client_shell import LmiIndicationListenerError
+from lmi_client_shell import LmiConnection
+from lmi_client_shell import _LmiNamespace
+from lmi_client_shell import _LmiNamespaceRoot
+from lmi_client_shell import _LmiClass
+from lmi_client_shell import _LmiInstanceName
+from lmi_client_shell import _LmiInstance
diff --git a/cli-tools/lmi/lmi_client_shell.py b/cli-tools/lmi/lmi_client_shell.py
index 9a6788c..acfbc6f 100644
--- a/cli-tools/lmi/lmi_client_shell.py
+++ b/cli-tools/lmi/lmi_client_shell.py
@@ -17,18 +17,22 @@ import os
import abc
import new
import sys
+import time
import atexit
import pywbem
import textwrap
import tempfile
+import threading
+import socket
import subprocess
from xml.dom.minicompat import NodeList
from urlparse import urlparse
from lmi_client_base import LmiBaseClient
from lmi_client_base import LmiReturnValue
+from lmi_listener import LmiHandlerNamePatternError
from lmi_listener import LmiIndicationListener
+from lmi_listener import PassByRef
-_lmi_indications = {}
_lmi_indication_listeners = {}
class LmiDeletedObjectError(Exception):
@@ -46,6 +50,12 @@ class LmiUnknownPropertyError(Exception):
class LmiMethodCallError(Exception):
"""Raised, when an error occurs within method call"""
+class LmiSynchroMethodCallError(Exception):
+ """Raised, when an error occurs within synchronized method call"""
+
+class LmiSynchroMethodCallFilterError(Exception):
+ """Raised, when the shell can not find necessary static filter for synchronous method call"""
+
class LmiNoPagerError(Exception):
"""Raised, when tere is no default pager like less or more"""
@@ -593,6 +603,7 @@ class _LmiClass(BaseObject):
return self._cim_classname
@property
+ @lmi_return_val_if_fail("_namespace", "Unknown")
def namespace(self):
return self._namespace.name
@@ -636,6 +647,10 @@ class _LmiInstanceName(BaseObject):
return getattr(self, prop_name)
@property
+ def classname(self):
+ return self._cim_instance_name.classname
+
+ @property
def path(self):
return self._cim_instance_name
@@ -655,11 +670,43 @@ def possibly_deleted(fn):
return wrapped
class _LmiInstanceMethod(BaseObject):
- def __init__(self, client, lmi_instance, method):
+ # 15 seconds sleep timeout for main waiting thread
+ COND_WAIT_TIME = 15
+ # Wake count of main thread, when the GetInstance is performed to check,
+ # if the job object is present. Prevents infinite waiting for indication
+ # delivery. Maximum waiting time, before the GetInstance for job object
+ # will be called is: COND_WAIT_TIME * COND_WAIT_WAKE_CNT
+ COND_WAIT_WAKE_CNT = 8
+ # Default tcp port, where the indications will be delivered.
+ # TODO: create a configuration option for the port
+ INDICATION_DESTINATION_PORT = 10240
+ # Job classes, which can be used for synchro method calls
+ # TODO: create a configuration option for the static filters' classnames
+ INDICATION_FILTER_CLASSNAMES = (
+ "LMI_StorageJob",
+ "LMI_SoftwareInstallationJob",
+ "LMI_NetworkJob"
+ )
+ # When performing a synchronous method call and using the polling method to
+ # get a job object status, the sleep time between 2 polls doubles if it is
+ # less than POLLING_ADAPT_MAX_WAITING_TIME.
+ POLLING_ADAPT_MAX_WAITING_TIME = 128
+
+ def __init__(self, client, lmi_instance, method, sync_method):
super(self.__class__, self).__init__()
self._client = client
self._lmi_instance = lmi_instance
self._method = lmi_instance._lmi_class._cim_class.methods[method]
+ self._sync_method = sync_method
+
+ @staticmethod
+ def is_job_finished(job):
+ """
+ Checks if a job object is in finished state. The range comes from the standard.
+ Arguments:
+ job -- LmiInstance
+ """
+ return job.JobState in range(7, 11)
def __transform_return_values(self, rparams):
lmi_rparams = pywbem.NocaseDict()
@@ -692,7 +739,182 @@ class _LmiInstanceMethod(BaseObject):
lmi_rparams[key] = value
return lmi_rparams
+ def __handle_synchro_method_call_indication(self, job_inst):
+ """
+ Handle synchronous call for asynchronous methods returning a job object.
+ This method uses static filters installed by each OpenLMI provider, which is
+ capable of using jobs.
+
+ Static filters' names need to be in format "LMI:<job_class_name>:Changed"
+
+ Arguments:
+ job -- LmiInstance (passed by ref)
+ job_out_params -- job output parameters (passed by ref)
+ job_exception -- exception object, if any (passed by ref)
+ """
+ def handle_job(ind, cond, cond_variable, job_out_params, job_exception):
+ cond.acquire()
+ try:
+ exp_obj = ind.exported_objects()[0]
+ src_inst = exp_obj["SourceInstance"]
+ if src_inst["JobState"] in (7, 8, 9, 10):
+ job_out_params.value = src_inst["JobOutParameters"]
+ # Job has just finished
+ cond_variable.value = True
+ except Exception, e:
+ # Notify main thread, we are not able to work with such objects
+ cond_variable.value = True
+ job_exception.value = e.message
+ finally:
+ # XXX: Let's be defensive, always notify+release main thread
+ cond.notify()
+ cond.release()
+
+ # Start indication listener
+ cond = threading.Condition()
+ job_finished = PassByRef(False)
+ job_out_params = PassByRef(None)
+ job_exception = PassByRef(None)
+ # There needs to be a pattern of at least 8 "X" in a row at the end of the indication_name
+ indication_name = "synchro-method-call-XXXXXXXX"
+ listener = LmiIndicationListener("0.0.0.0", _LmiInstanceMethod.INDICATION_DESTINATION_PORT)
+ indication_name = listener.add_handler(indication_name, handle_job, cond, job_finished,
+ job_out_params, job_exception)
+ if not listener.start():
+ raise LmiIndicationListenerError("Can not start indication listener")
+
+ # Search for necessary static filter
+ filter_name = "LMI:%s:Changed" % job_inst.classname
+ (cim_filters, _, err) = self._client._get_instances("CIM_IndicationFilter", "root/PG_InterOp",
+ { "Name" : filter_name })
+ if not cim_filters:
+ listener.stop()
+ errorstr = "Can not find proper CIM_IndicationFilter for this method call"
+ raise LmiSynchroMethodCallFilterError(errorstr)
+ cim_filter = cim_filters[0]
+
+ # Create handler object
+ netloc = urlparse(self._client.hostname).netloc
+ if not netloc:
+ listener.stop()
+ errorstr = "Can not determine netloc from client's hostname"
+ raise LmiSynchroMethodCallError(errorstr)
+ netloc = netloc.split(":")[0]
+ # NOTE: This will work only on a local area network. Complicated networks may require
+ # additional configuration to make this work. See LmiInstanceMethod() and PreferPolling.
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ try:
+ s.connect((netloc, _LmiInstanceMethod.INDICATION_DESTINATION_PORT))
+ except socket.gaierror, e:
+ listener.stop()
+ errorstr = "Can not determine IP address of this machine"
+ raise LmiSynchroMethodCallError(errorstr)
+ destination = s.getsockname()[0]
+ s.close()
+ # NOTE: For now, we are using unsecure HTTP as a transport protocol.
+ # TODO: Switch to HTTPS
+ cim_handler_props = {
+ "Name" : indication_name,
+ "Destination" : "http://%s:%d/%s" % (destination,
+ _LmiInstanceMethod.INDICATION_DESTINATION_PORT, indication_name),
+ }
+ (cim_handler, _, err) = self._client._create_instance("CIM_IndicationHandlerCIMXML",
+ "root/PG_InterOp", cim_handler_props)
+ if not cim_handler:
+ listener.stop()
+ errorstr = "Can not create CIM_IndicationHandlerCIMXML object"
+ raise LmiSynchroMethodCallError(errorstr)
+
+ # Create indication subscription object
+ cim_subscription_props = {
+ "Filter" : cim_filter.path,
+ "Handler" : cim_handler.path
+ }
+ (cim_subscription, _, err) = self._client._create_instance("CIM_IndicationSubscription",
+ "root/PG_InterOp", cim_subscription_props)
+ if not cim_subscription:
+ self._client._delete_instance(cim_handler)
+ listener.stop()
+ errorstr = "Can not create CIM_IndicationSubscription object"
+ raise LmiSynchroMethodCallError(errorstr)
+
+ # Check, if the job is not already in finished state,
+ # while we were subscribing for the indications
+ job_inst.refresh()
+ job_finished.value = _LmiInstanceMethod.is_job_finished(job_inst)
+ if job_finished.value:
+ job_out_params.value = job_inst.JobOutParameters
+
+ # Wait for the job to finish
+ interrupt = False
+ try:
+ wake_cnt = 0
+ cond.acquire()
+ while True:
+ if interrupt or job_finished.value:
+ break
+ cond.wait(_LmiInstanceMethod.COND_WAIT_TIME)
+ wake_cnt += 1
+ # XXX: threading.Condition.wait() does not inform about timeout or being awaken by
+ # notify call. There is a counting to 4 sleep cycles before we actually check for
+ # job status manually. This number can be increased, so we rely more on indications,
+ # rather then on manual polling.
+ if not job_finished.value and wake_cnt >= _LmiInstanceMethod.COND_WAIT_WAKE_CNT:
+ wake_cnt = 0
+ (refreshed, _, errorstr) = job_inst.refresh()
+ if not refreshed:
+ job_exception.value = errorstr
+ break
+ if _LmiInstanceMethod.is_job_finished(job_inst):
+ job_out_params.value = job_inst.JobOutParameters
+ listener.stop()
+ break
+ except KeyboardInterrupt:
+ interrupt = True
+ finally:
+ cond.release()
+
+ # Cleanup
+ listener.stop()
+ self._client._delete_instance(cim_subscription.path)
+ self._client._delete_instance(cim_handler.path)
+ if job_exception.value:
+ raise LmiSynchroMethodCallError(job_exception.value)
+ # TODO: adjust return value from the job object
+ return LmiReturnValue(
+ rval = 0,
+ rparams = {"out" : job_out_params.value}
+ )
+
+ def __handle_synchro_method_call_polling(self, job_inst):
+ try:
+ sleep_time = 1
+ while not _LmiInstanceMethod.is_job_finished(job_inst):
+ # Sleep, a bit longer in every iteration
+ time.sleep(sleep_time)
+ if sleep_time < _LmiInstanceMethod.POLLING_ADAPT_MAX_WAITING_TIME:
+ sleep_time *= 2
+ (refreshed, _, errorstr) = job_inst.refresh()
+ if not refreshed:
+ raise LmiSynchroMethodCallError(errorstr)
+ except pywbem.cim_operations.CIMError, e:
+ raise LmiSynchroMethodCallError(e.message)
+ # TODO: adjust return value from the job object
+ return LmiReturnValue(
+ rval = 0,
+ rparams = {"out" : job_inst.JobOutParameters}
+ )
def __call__(self, *args, **kwargs):
+ """
+ Perform a method call.
+
+ If performing a synchronous method call, passing PreferPolling can be used
+ to select which method should be used -- either subscribing to an indication
+ or polling method.
+ """
+ synchro_method_polling = False
+ if "PreferPolling" in kwargs:
+ synchro_method_polling = kwargs.pop("PreferPolling")
for (param, value) in kwargs.iteritems():
if param in self._method.parameters:
t = self._method.parameters[param].type
@@ -720,7 +942,53 @@ class _LmiInstanceMethod(BaseObject):
# NOTE: this is wrong! What should we do?
if LmiBaseClient._get_use_exceptions():
raise LmiMethodCallError("Could not perform CIM -> LMI object transformation")
- return LmiReturnValue(rval = rval, errorstr = errorstr)
+ return LmiReturnValue(
+ rval = rval,
+ errorstr = errorstr,
+ hostname = self._client.hostname,
+ use_display_hook = True)
+ # Check if we can perform synchronous method call
+ job = call_rparams["job"] if "job" in call_rparams else None
+ can_perform_sync_call = job.classname in _LmiInstanceMethod.INDICATION_FILTER_CLASSNAMES if job else False
+ if self._sync_method and job and can_perform_sync_call:
+ # Synchronous method calls
+ job_inst = call_rparams['job'].to_instance()
+ # At first, try to wait for the call to finish by subscribing to an indication
+ handled_by_indication = False
+ if not synchro_method_polling:
+ try:
+ (rval, call_rparams, _) = self.__handle_synchro_method_call_indication(job_inst)
+ handled_by_indication = True
+ except LmiSynchroMethodCallError, e:
+ if LmiBaseClient._get_use_exceptions():
+ raise e
+ return LmiReturnValue(
+ rval = -1,
+ errorstr = e.message,
+ hostname = self._client.hostname,
+ use_display_hook = True
+ )
+ # Fall through, try to handle the synchro call by polling
+ except LmiHandlerNamePatternError, e:
+ handled_by_indication = False
+ except LmiSynchroMethodCallFilterError, e:
+ handled_by_indication = False
+ except LmiIndicationListenerError, e:
+ handled_by_indication = False
+ if not handled_by_indication:
+ # Executed, when LmiListener can not be started
+ try:
+ (rval, call_rparams, _) = self.__handle_synchro_method_call_polling(job_inst)
+ except LmiSynchroMethodCallError, e:
+ if LmiBaseClient._get_use_exceptions():
+ raise e
+ return LmiReturnValue(
+ rval = -1,
+ errorstr = e.message,
+ hostname = self._client.hostname,
+ use_display_hook = True
+ )
+ call_rparams = self.__transform_return_values(call_rparams)
if not self._lmi_instance.refresh():
# NOTE: this is wrong! What should we do?
errorstr = "Could not update an LMI object after a method call"
@@ -763,9 +1031,13 @@ class _LmiInstance(BaseObject):
def __getattr__(self, name):
if name in self.__dict__:
return self.__dict__[name]
+ sync_method = False
+ if name.startswith("Sync"):
+ sync_method = True
+ name = name[4:]
methods = self.methods()
if methods and name in methods:
- return _LmiInstanceMethod(self._client, self, name)
+ return _LmiInstanceMethod(self._client, self, name, sync_method)
elif name in self._cim_instance:
member = self._cim_instance[name]
if isinstance(member, pywbem.CIMInstanceName):
@@ -1103,7 +1375,17 @@ class LmiConnection(object):
try:
cim_filter_provided = "Filter" in kwargs
if cim_filter_provided:
- cim_filter = kwargs["Filter"]._cim_instance
+ filt = kwargs["Filter"]
+ cim_filter = None
+ if isinstance(filt, _LmiInstance):
+ cim_filter = filt._cim_instance
+ elif isinstance(filt, pywbem.CIMInstance):
+ cim_filter = filt
+ else:
+ errorstr = "Filter argument accepts instances of CIMInstance or _LmiInstance"
+ if LmiBaseClient._get_use_exceptions():
+ raise LmiIndicationError(errorstr)
+ return _return_value(False, errorstr)
else:
cim_filter_props = {
"CreationClassName" : kwargs["FilterCreationClassName"],
@@ -1129,7 +1411,7 @@ class LmiConnection(object):
"CreationClassName" : kwargs["HandlerCreationClassName"],
"SystemCreationClassName" : kwargs["HandlerSystemCreationClassName"],
"SystemName" : self._client.hostname,
- "Destination" : kwargs["Destination"],
+ "Destination" : kwargs["Destination"] + "/" + kwargs["Name"],
"Name" : kwargs["Name"] + "-handler"
}
(cim_handler, _, errorstr) = self._client._create_instance(
@@ -1162,10 +1444,12 @@ class LmiConnection(object):
(cim_handler, not cim_handler_provided),
cim_subscription)
except KeyError, e:
- errorstr = "Not all necessary parameters provided"
+ errorstr = "Not all necessary parameters provided, missing: %s" % e
if LmiBaseClient._get_use_exceptions():
raise LmiIndicationError(errorstr)
return _return_value(False, errorstr)
+ # XXX: Should we auto-delete all the indications?
+ atexit.register(lambda: self.unsubscribe_indication(kwargs["Name"]))
return _return_value(True)
def unsubscribe_indication(self, name):
diff --git a/cli-tools/lmi/lmi_listener.py b/cli-tools/lmi/lmi_listener.py
index 6670b20..e62fa85 100644
--- a/cli-tools/lmi/lmi_listener.py
+++ b/cli-tools/lmi/lmi_listener.py
@@ -18,31 +18,121 @@ import sys
import ssl
import time
import pywbem
+import random
import socket
+import string
import threading
from SocketServer import BaseRequestHandler
from SocketServer import ThreadingMixIn
from BaseHTTPServer import HTTPServer
from BaseHTTPServer import BaseHTTPRequestHandler
-class LmiIndicationHandler(BaseHTTPRequestHandler):
+class LmiHandlerNamePatternError(Exception):
+ """
+ Raised when the pattern string does not contain minimum
+ replacable characters.
+ """
+
+class PassByRef(object):
+ def __init__(self, val):
+ self._val = { 0 : val }
+
+ @property
+ def value(self):
+ return self._val[0]
+
+ @value.setter
+ def value(self, new_val):
+ self._val[0] = new_val
+
+class LmiIndication(object):
+ def __init__(self, export_methods):
+ self._export_methods = export_methods
+
+ def export_method_names(self):
+ return self._export_methods.keys()
+
+ def exported_objects(self):
+ result = []
+ for m in self._export_methods.values():
+ for obj in m.values():
+ result.append(obj)
+ return result
+
+class LmiIndicationHandlerCallback(object):
+ def __init__(self, callback, *args, **kwargs):
+ self.callback = callback
+ self.args = args
+ self.kwargs = kwargs
+
+class LmiIndicationHandler(ThreadingMixIn, BaseHTTPRequestHandler):
def do_POST(self):
- self.server._handler(self.rfile.read())
+ msg = self.rfile.read()
+ tt = pywbem.parse_cim(pywbem.xml_to_tupletree(msg))
+ message = tt[2]
+ export_methods = {}
+ if message[0].upper() != "MESSAGE":
+ return
+ message_params = message[2]
+ if not message_params:
+ return
+ for param in message_params:
+ if param[0].upper() != "SIMPLEEXPREQ":
+ continue
+ export_method_call = param[2]
+ export_method_name = export_method_call[1]["NAME"]
+ exp_params = export_method_call[2]
+ export_method_params = {}
+ for method_param in exp_params:
+ export_method_params[method_param[0]] = method_param[1]
+ export_methods[export_method_name] = export_method_params
+ ind = LmiIndication(export_methods)
+ path = self.path[1:]
+ if path in self.server._handlers:
+ cb = self.server._handlers[path]
+ cb.callback(ind, *cb.args, **cb.kwargs)
class LmiIndicationServer(ThreadingMixIn, HTTPServer):
- pass
+ daemon_threads = True
class LmiIndicationListener(object):
- def __init__(self, hostname, port, handler, certfile = None, keyfile = None):
- self._handler = handler
+ # Minimum replacable "X" characters in handler pattern string.
+ HANDLER_MINIMUM_REPL_CHARS_COUNT = 8
+
+ def __init__(self, hostname, port, certfile = None, keyfile = None):
+ self._handlers = {}
self._hostname = hostname
self._port = port
self._certfile = certfile
self._keyfile = keyfile
+ def __create_handler_name(self, handler_name_pattern):
+ """
+ Creates unique handler name by replacing "X" characters for random characters
+ at the end of the handler_name_pattern.
+
+ Arguments:
+ handler_name_pattern -- string containing replacable characters at the end
+ """
+ x_cnt = 0
+ pattern_len = len(handler_name_pattern)
+ while True:
+ while handler_name_pattern[pattern_len - 1 - x_cnt] == "X":
+ x_cnt += 1
+ if x_cnt < LmiIndicationListener.HANDLER_MINIMUM_REPL_CHARS_COUNT:
+ raise LmiHandlerNamePatternError("Not enough replacable characters provided")
+ unique_str = handler_name_pattern[:pattern_len - x_cnt] + \
+ "".join(random.choice(string.ascii_uppercase + string.digits) for x in range(x_cnt))
+ if not unique_str in self._handlers:
+ break
+ return unique_str
+
def start(self):
- self._server = LmiIndicationServer((self._hostname, self._port), LmiIndicationHandler)
- self._server._handler = self._handler
+ try:
+ self._server = LmiIndicationServer((self._hostname, self._port), LmiIndicationHandler)
+ except socket.error, e:
+ return False
+ self._server._handlers = self._handlers
self._server_thread = threading.Thread(target = self._server.serve_forever)
self._server_thread.daemon = True
if self._certfile:
@@ -52,6 +142,30 @@ class LmiIndicationListener(object):
keyfile = self._keyfile,
server_side = True)
self._server_thread.start()
+ return True
def stop(self):
self._server.shutdown()
+ self._server_thread.join()
+
+ @property
+ def is_alive(self):
+ return self._server_thread.is_alive()
+
+ @property
+ def hostname(self):
+ return self._hostname
+
+ @property
+ def port(self):
+ return self._port
+
+ def add_handler(self, handler_name_pattern, handler, *args, **kwargs):
+ handler_name = self.__create_handler_name(handler_name_pattern + "X" * 16)
+ self._handlers[handler_name] = LmiIndicationHandlerCallback(handler, *args, **kwargs)
+ return handler_name
+
+ def remove_handler(self, name):
+ if not name in self._handlers:
+ return
+ self._handlers.pop(name)
diff --git a/cli-tools/lmishell b/cli-tools/lmishell
index a12233e..bbbe0df 100755
--- a/cli-tools/lmishell
+++ b/cli-tools/lmishell
@@ -23,9 +23,9 @@ import readline
import urlparse
import rlcompleter
import __builtin__
-from lmi.lmi_client_base import LmiReturnValue
-from lmi.lmi_client_base import LmiBaseClient
-from lmi.lmi_client_shell import LmiConnection
+from lmi import LmiReturnValue
+from lmi import LmiBaseClient
+from lmi import LmiConnection
from lmi.lmi_client_shell import _LmiNamespace
from lmi.lmi_client_shell import _LmiNamespaceRoot
from lmi.lmi_client_shell import _LmiClass
@@ -45,8 +45,7 @@ def _lmi_displayhook(o):
__builtin__._ = None
if isinstance(o, LmiReturnValue) and o.use_display_hook:
o.use_display_hook = False
- if (type(o.rval) == int and o.rval == 0) or (type(o.rval) == bool and o.rval == True) \
- or (o.rval == 4096 and o.instance_classname == "LMI_PowerManagementService"):
+ if (type(o.rval) == int and o.rval in (0, 4096)) or (type(o.rval) == bool and o.rval == True):
result_str = "ok"
else:
result_str = "fail"
@@ -162,9 +161,19 @@ class _LmiCompleter(rlcompleter.Completer):
if c.lower().startswith(to_complete.lower()):
self._last_complete.append(cmd + "." + c)
elif isinstance(expr, _LmiInstance):
- for m in expr.methods():
- if m.lower().startswith(to_complete.lower()):
- self._last_complete.append(cmd + "." + m + "(")
+ expr_methods = expr.methods()
+ if expr_methods:
+ if to_complete and to_complete.lower() in "syn":
+ self._last_complete.append(cmd + ".Sync")
+ else:
+ sync_prefix = ""
+ if to_complete.lower().startswith("sync"):
+ sync_prefix = "Sync"
+ to_complete = to_complete[4:]
+ for m in expr_methods:
+ if m.lower().startswith(to_complete.lower()):
+ self._last_complete.append(cmd + "." + sync_prefix + m + "(")
+ to_complete = sync_prefix + to_complete
for p in expr.properties():
if p.lower().startswith(to_complete.lower()):
self._last_complete.append(cmd + "." + p)
10 years, 10 months
[cura-tools] master: use LmiReturnValue in _LmiInstance::refresh() (87ae6ff)
by phatina@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/cura-tools.git
On branch : master
>---------------------------------------------------------------
commit 87ae6ff4ee5456fd858fb1a9ff04e1d57f56e158
Author: Peter Hatina <phatina(a)redhat.com>
Date: Fri Jul 12 11:17:25 2013 +0200
use LmiReturnValue in _LmiInstance::refresh()
>---------------------------------------------------------------
cli-tools/lmi/lmi_client_shell.py | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/cli-tools/lmi/lmi_client_shell.py b/cli-tools/lmi/lmi_client_shell.py
index a71f3ce..9a6788c 100644
--- a/cli-tools/lmi/lmi_client_shell.py
+++ b/cli-tools/lmi/lmi_client_shell.py
@@ -811,11 +811,11 @@ class _LmiInstance(BaseObject):
@possibly_deleted
def refresh(self):
- (new_cim_instance, _, _) = self._client._get_instance(self.path, LocalOnly = False)
+ (new_cim_instance, _, errorstr) = self._client._get_instance(self.path, LocalOnly = False)
if not new_cim_instance:
- return False
+ return LmiReturnValue(rval = False, errorstr = errorstr)
self._cim_instance = new_cim_instance
- return True
+ return LmiReturnValue(rval = True)
@possibly_deleted
@lmi_return_val_if_fail("_cim_instance.path", [])
10 years, 10 months
[cura-tools] master: cast property from CIMInstanceName to _LmiInstanceName (223bb6a)
by phatina@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/cura-tools.git
On branch : master
>---------------------------------------------------------------
commit 223bb6aafc1603ec0f9427a53a8737f77f1d5432
Author: Peter Hatina <phatina(a)redhat.com>
Date: Fri Jul 12 11:16:31 2013 +0200
cast property from CIMInstanceName to _LmiInstanceName
>---------------------------------------------------------------
cli-tools/lmi/lmi_client_shell.py | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/cli-tools/lmi/lmi_client_shell.py b/cli-tools/lmi/lmi_client_shell.py
index 8c7321d..a71f3ce 100644
--- a/cli-tools/lmi/lmi_client_shell.py
+++ b/cli-tools/lmi/lmi_client_shell.py
@@ -767,7 +767,10 @@ class _LmiInstance(BaseObject):
if methods and name in methods:
return _LmiInstanceMethod(self._client, self, name)
elif name in self._cim_instance:
- return self._cim_instance[name]
+ member = self._cim_instance[name]
+ if isinstance(member, pywbem.CIMInstanceName):
+ member = _LmiInstanceName(self._client, member)
+ return member
raise AttributeError(name)
@possibly_deleted
10 years, 10 months
[cura-tools] master: Add configuration file for ReviewBoard (d0dc5cd)
by Stephen Gallagher
Repository : http://git.fedorahosted.org/cgit/cura-tools.git
On branch : master
>---------------------------------------------------------------
commit d0dc5cd9a944d395bbd68ae137416db7282e9153
Author: Stephen Gallagher <sgallagh(a)redhat.com>
Date: Wed May 8 10:04:18 2013 -0400
Add configuration file for ReviewBoard
>---------------------------------------------------------------
.reviewboardrc | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/.reviewboardrc b/.reviewboardrc
new file mode 100644
index 0000000..aa60c44
--- /dev/null
+++ b/.reviewboardrc
@@ -0,0 +1,2 @@
+REVIEWBOARD_URL = 'https://reviewboard-openlmi.rhcloud.com'
+REPOSITORY = 'openlmi-tools'
10 years, 11 months