Merge tag '1.0.13' into debian/kilo
python-ceilometerclient 1.0.13 Includes the following changes: * Corrected the errors in sample-show and sample-create * Allow create/update gnocchi alarm rule * Don't enforce a kind of alarm rules * Update get_client() parameters with correct variable names * Add Sample API support * add --no-traits for event-list * Add severity field to alarm CLI * Remove trailing space before , in the help string of --time-constraint * Fix improper parameter setup for cacert and client certs * event-list should sort by timestamp, not id * Allow all pep8 checks * Fix H105 pep8 error * Triple double-quoted strings should be used for docstrings * Support ceilometer-url and os-endpoint * sync to latest oslo-incubator code * Add apiclient to openstack-common.conf * Add client property for common.base.Manager * Allow graceful shutdown on Ctrl+C * Make methods static where it's possible * Fix old-style classes declaration * Remove redundant parentheses (except openstack.common) * Enable --os-insecure CLI option * sync with oslo and use oslo.i18n * Workflow documentation is now in infra-manual * Support os-endpoint-type * Alarm TimeConstraint display incorrect * Add `requests` to requirements * Fix timeout argument not treated as integer * Refactor tests/test_shell.py * Add --slowest option for testr * Fix wrong initialization of AuthPlugin for keystone v3 * Add CONTRIBUTING.rst
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -6,6 +6,8 @@ build
 | 
			
		||||
cover
 | 
			
		||||
.testrepository
 | 
			
		||||
.venv
 | 
			
		||||
doc/build
 | 
			
		||||
doc/source/ref
 | 
			
		||||
subunit.log
 | 
			
		||||
AUTHORS
 | 
			
		||||
ChangeLog
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								CONTRIBUTING.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								CONTRIBUTING.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
If you would like to contribute to the development of OpenStack,
 | 
			
		||||
you must follow the steps documented at:
 | 
			
		||||
 | 
			
		||||
   http://docs.openstack.org/infra/manual/developers.html#development-workflow
 | 
			
		||||
 | 
			
		||||
Once those steps have been completed, changes to OpenStack
 | 
			
		||||
should be submitted for review via the Gerrit tool, following
 | 
			
		||||
the workflow documented at:
 | 
			
		||||
 | 
			
		||||
   http://docs.openstack.org/infra/manual/developers.html#development-workflow
 | 
			
		||||
 | 
			
		||||
Pull requests submitted through GitHub will be ignored.
 | 
			
		||||
 | 
			
		||||
Bugs should be filed on Launchpad, not GitHub:
 | 
			
		||||
 | 
			
		||||
   https://bugs.launchpad.net/python-ceilometerclient
 | 
			
		||||
@@ -6,7 +6,7 @@ provides a Python API (the ``ceilometerclient`` module) and a command-line tool
 | 
			
		||||
(``ceilometer``).
 | 
			
		||||
 | 
			
		||||
Development takes place via the usual OpenStack processes as outlined in the
 | 
			
		||||
`OpenStack wiki <http://wiki.openstack.org/HowToContribute>`_.  The master
 | 
			
		||||
repository is on `GitHub <http://github.com/openstack/python-ceilometerclient>`_.
 | 
			
		||||
`developer guide <http://docs.openstack.org/infra/manual/developers.html>`_.  The master
 | 
			
		||||
repository is in `Git <http://git.openstack.org/cgit/openstack/python-ceilometerclient>`_.
 | 
			
		||||
 | 
			
		||||
See release notes and more at `<http://docs.openstack.org/developer/python-ceilometerclient/>`_.
 | 
			
		||||
 
 | 
			
		||||
@@ -10,68 +10,272 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from keystoneclient.v2_0 import client as ksclient
 | 
			
		||||
import six
 | 
			
		||||
from keystoneclient.auth.identity import v2 as v2_auth
 | 
			
		||||
from keystoneclient.auth.identity import v3 as v3_auth
 | 
			
		||||
from keystoneclient import discover
 | 
			
		||||
from keystoneclient import exceptions as ks_exc
 | 
			
		||||
from keystoneclient import session
 | 
			
		||||
from oslo.utils import strutils
 | 
			
		||||
import six.moves.urllib.parse as urlparse
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.common import utils
 | 
			
		||||
from ceilometerclient import exc
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import auth
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import exceptions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_ksclient(**kwargs):
 | 
			
		||||
    """Get an endpoint and auth token from Keystone.
 | 
			
		||||
def _discover_auth_versions(session, auth_url):
 | 
			
		||||
    # discover the API versions the server is supporting based on the
 | 
			
		||||
    # given URL
 | 
			
		||||
    v2_auth_url = None
 | 
			
		||||
    v3_auth_url = None
 | 
			
		||||
    try:
 | 
			
		||||
        ks_discover = discover.Discover(session=session, auth_url=auth_url)
 | 
			
		||||
        v2_auth_url = ks_discover.url_for('2.0')
 | 
			
		||||
        v3_auth_url = ks_discover.url_for('3.0')
 | 
			
		||||
    except ks_exc.DiscoveryFailure:
 | 
			
		||||
        raise
 | 
			
		||||
    except exceptions.ClientException:
 | 
			
		||||
        # Identity service may not support discovery. In that case,
 | 
			
		||||
        # try to determine version from auth_url
 | 
			
		||||
        url_parts = urlparse.urlparse(auth_url)
 | 
			
		||||
        (scheme, netloc, path, params, query, fragment) = url_parts
 | 
			
		||||
        path = path.lower()
 | 
			
		||||
        if path.startswith('/v3'):
 | 
			
		||||
            v3_auth_url = auth_url
 | 
			
		||||
        elif path.startswith('/v2'):
 | 
			
		||||
            v2_auth_url = auth_url
 | 
			
		||||
        else:
 | 
			
		||||
            raise exc.CommandError('Unable to determine the Keystone '
 | 
			
		||||
                                   'version to authenticate with '
 | 
			
		||||
                                   'using the given auth_url.')
 | 
			
		||||
    return v2_auth_url, v3_auth_url
 | 
			
		||||
 | 
			
		||||
    :param kwargs: keyword args containing credentials:
 | 
			
		||||
            * username: name of user
 | 
			
		||||
            * password: user's password
 | 
			
		||||
            * auth_url: endpoint to authenticate against
 | 
			
		||||
            * cacert: path of CA TLS certificate
 | 
			
		||||
            * insecure: allow insecure SSL (no cert verification)
 | 
			
		||||
            * tenant_{name|id}: name or ID of tenant
 | 
			
		||||
 | 
			
		||||
def _get_keystone_session(**kwargs):
 | 
			
		||||
    # TODO(fabgia): the heavy lifting here should be really done by Keystone.
 | 
			
		||||
    # Unfortunately Keystone does not support a richer method to perform
 | 
			
		||||
    # discovery and return a single viable URL. A bug against Keystone has
 | 
			
		||||
    # been filed: https://bugs.launchpad.net/python-keystoneclient/+bug/1330677
 | 
			
		||||
 | 
			
		||||
    # first create a Keystone session
 | 
			
		||||
    cacert = kwargs.pop('cacert', None)
 | 
			
		||||
    cert = kwargs.pop('cert', None)
 | 
			
		||||
    key = kwargs.pop('key', None)
 | 
			
		||||
    insecure = kwargs.pop('insecure', False)
 | 
			
		||||
    auth_url = kwargs.pop('auth_url', None)
 | 
			
		||||
    project_id = kwargs.pop('project_id', None)
 | 
			
		||||
    project_name = kwargs.pop('project_name', None)
 | 
			
		||||
 | 
			
		||||
    if insecure:
 | 
			
		||||
        verify = False
 | 
			
		||||
    else:
 | 
			
		||||
        verify = cacert or True
 | 
			
		||||
 | 
			
		||||
    if cert and key:
 | 
			
		||||
        # passing cert and key together is deprecated in favour of the
 | 
			
		||||
        # requests lib form of having the cert and key as a tuple
 | 
			
		||||
        cert = (cert, key)
 | 
			
		||||
 | 
			
		||||
    # create the keystone client session
 | 
			
		||||
    ks_session = session.Session(verify=verify, cert=cert)
 | 
			
		||||
    v2_auth_url, v3_auth_url = _discover_auth_versions(ks_session, auth_url)
 | 
			
		||||
 | 
			
		||||
    username = kwargs.pop('username', None)
 | 
			
		||||
    user_id = kwargs.pop('user_id', None)
 | 
			
		||||
    user_domain_name = kwargs.pop('user_domain_name', None)
 | 
			
		||||
    user_domain_id = kwargs.pop('user_domain_id', None)
 | 
			
		||||
    project_domain_name = kwargs.pop('project_domain_name', None)
 | 
			
		||||
    project_domain_id = kwargs.pop('project_domain_id', None)
 | 
			
		||||
    auth = None
 | 
			
		||||
 | 
			
		||||
    use_domain = (user_domain_id or user_domain_name or
 | 
			
		||||
                  project_domain_id or project_domain_name)
 | 
			
		||||
    use_v3 = v3_auth_url and (use_domain or (not v2_auth_url))
 | 
			
		||||
    use_v2 = v2_auth_url and not use_domain
 | 
			
		||||
 | 
			
		||||
    if use_v3:
 | 
			
		||||
        # the auth_url as v3 specified
 | 
			
		||||
        # e.g. http://no.where:5000/v3
 | 
			
		||||
        # Keystone will return only v3 as viable option
 | 
			
		||||
        auth = v3_auth.Password(
 | 
			
		||||
            v3_auth_url,
 | 
			
		||||
            username=username,
 | 
			
		||||
            password=kwargs.pop('password', None),
 | 
			
		||||
            user_id=user_id,
 | 
			
		||||
            user_domain_name=user_domain_name,
 | 
			
		||||
            user_domain_id=user_domain_id,
 | 
			
		||||
            project_domain_name=project_domain_name,
 | 
			
		||||
            project_domain_id=project_domain_id)
 | 
			
		||||
    elif use_v2:
 | 
			
		||||
        # the auth_url as v2 specified
 | 
			
		||||
        # e.g. http://no.where:5000/v2.0
 | 
			
		||||
        # Keystone will return only v2 as viable option
 | 
			
		||||
        auth = v2_auth.Password(
 | 
			
		||||
            v2_auth_url,
 | 
			
		||||
            username,
 | 
			
		||||
            kwargs.pop('password', None),
 | 
			
		||||
            tenant_id=project_id,
 | 
			
		||||
            tenant_name=project_name)
 | 
			
		||||
    else:
 | 
			
		||||
        raise exc.CommandError('Unable to determine the Keystone version '
 | 
			
		||||
                               'to authenticate with using the given '
 | 
			
		||||
                               'auth_url.')
 | 
			
		||||
 | 
			
		||||
    ks_session.auth = auth
 | 
			
		||||
    return ks_session
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_endpoint(ks_session, **kwargs):
 | 
			
		||||
    """Get an endpoint using the provided keystone session."""
 | 
			
		||||
 | 
			
		||||
    # set service specific endpoint types
 | 
			
		||||
    endpoint_type = kwargs.get('endpoint_type') or 'publicURL'
 | 
			
		||||
    service_type = kwargs.get('service_type') or 'metering'
 | 
			
		||||
 | 
			
		||||
    endpoint = ks_session.get_endpoint(service_type=service_type,
 | 
			
		||||
                                       interface=endpoint_type,
 | 
			
		||||
                                       region_name=kwargs.get('region_name'))
 | 
			
		||||
 | 
			
		||||
    return endpoint
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AuthPlugin(auth.BaseAuthPlugin):
 | 
			
		||||
    opt_names = ['tenant_id', 'region_name', 'auth_token',
 | 
			
		||||
                 'service_type', 'endpoint_type', 'cacert',
 | 
			
		||||
                 'auth_url', 'insecure', 'cert_file', 'key_file',
 | 
			
		||||
                 'cert', 'key', 'tenant_name', 'project_name',
 | 
			
		||||
                 'project_id', 'user_domain_id', 'user_domain_name',
 | 
			
		||||
                 'password', 'username', 'endpoint']
 | 
			
		||||
 | 
			
		||||
    def __init__(self, auth_system=None, **kwargs):
 | 
			
		||||
        self.opt_names.extend(self.common_opt_names)
 | 
			
		||||
        super(AuthPlugin, self).__init__(auth_system, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def _do_authenticate(self, http_client):
 | 
			
		||||
        token = self.opts.get('token') or self.opts.get('auth_token')
 | 
			
		||||
        endpoint = self.opts.get('endpoint')
 | 
			
		||||
        if not (token and endpoint):
 | 
			
		||||
            project_id = (self.opts.get('project_id') or
 | 
			
		||||
                          self.opts.get('tenant_id'))
 | 
			
		||||
            project_name = (self.opts.get('project_name') or
 | 
			
		||||
                            self.opts.get('tenant_name'))
 | 
			
		||||
            ks_kwargs = {
 | 
			
		||||
                'username': self.opts.get('username'),
 | 
			
		||||
                'password': self.opts.get('password'),
 | 
			
		||||
                'user_id': self.opts.get('user_id'),
 | 
			
		||||
                'user_domain_id': self.opts.get('user_domain_id'),
 | 
			
		||||
                'user_domain_name': self.opts.get('user_domain_name'),
 | 
			
		||||
                'project_id': project_id,
 | 
			
		||||
                'project_name': project_name,
 | 
			
		||||
                'project_domain_name': self.opts.get('project_domain_name'),
 | 
			
		||||
                'project_domain_id': self.opts.get('project_domain_id'),
 | 
			
		||||
                'auth_url': self.opts.get('auth_url'),
 | 
			
		||||
                'cacert': self.opts.get('cacert'),
 | 
			
		||||
                'cert': self.opts.get('cert'),
 | 
			
		||||
                'key': self.opts.get('key'),
 | 
			
		||||
                'insecure': strutils.bool_from_string(
 | 
			
		||||
                    self.opts.get('insecure')),
 | 
			
		||||
                'endpoint_type': self.opts.get('endpoint_type'),
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            # retrieve session
 | 
			
		||||
            ks_session = _get_keystone_session(**ks_kwargs)
 | 
			
		||||
            token = lambda: ks_session.get_token()
 | 
			
		||||
            endpoint = (self.opts.get('endpoint') or
 | 
			
		||||
                        _get_endpoint(ks_session, **ks_kwargs))
 | 
			
		||||
        self.opts['token'] = token
 | 
			
		||||
        self.opts['endpoint'] = endpoint
 | 
			
		||||
 | 
			
		||||
    def token_and_endpoint(self, endpoint_type, service_type):
 | 
			
		||||
        token = self.opts.get('token')
 | 
			
		||||
        if callable(token):
 | 
			
		||||
            token = token()
 | 
			
		||||
        return token, self.opts.get('endpoint')
 | 
			
		||||
 | 
			
		||||
    def sufficient_options(self):
 | 
			
		||||
        """Check if all required options are present.
 | 
			
		||||
 | 
			
		||||
        :raises: AuthPluginOptionsMissing
 | 
			
		||||
        """
 | 
			
		||||
    return ksclient.Client(username=kwargs.get('username'),
 | 
			
		||||
                           password=kwargs.get('password'),
 | 
			
		||||
                           tenant_id=kwargs.get('tenant_id'),
 | 
			
		||||
                           tenant_name=kwargs.get('tenant_name'),
 | 
			
		||||
                           auth_url=kwargs.get('auth_url'),
 | 
			
		||||
                           region_name=kwargs.get('region_name'),
 | 
			
		||||
                           cacert=kwargs.get('cacert'),
 | 
			
		||||
                           insecure=kwargs.get('insecure'))
 | 
			
		||||
        has_token = self.opts.get('token') or self.opts.get('auth_token')
 | 
			
		||||
        no_auth = has_token and self.opts.get('endpoint')
 | 
			
		||||
        has_tenant = self.opts.get('tenant_id') or self.opts.get('tenant_name')
 | 
			
		||||
        has_credential = (self.opts.get('username') and has_tenant
 | 
			
		||||
                          and self.opts.get('password')
 | 
			
		||||
                          and self.opts.get('auth_url'))
 | 
			
		||||
        missing = not (no_auth or has_credential)
 | 
			
		||||
        if missing:
 | 
			
		||||
            missing_opts = []
 | 
			
		||||
            opts = ['token', 'endpoint', 'username', 'password', 'auth_url',
 | 
			
		||||
                    'tenant_id', 'tenant_name']
 | 
			
		||||
            for opt in opts:
 | 
			
		||||
                if not self.opts.get(opt):
 | 
			
		||||
                    missing_opts.append(opt)
 | 
			
		||||
            raise exceptions.AuthPluginOptionsMissing(missing_opts)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_endpoint(client, **kwargs):
 | 
			
		||||
    """Get an endpoint using the provided keystone client."""
 | 
			
		||||
    return client.service_catalog.url_for(
 | 
			
		||||
        service_type=kwargs.get('service_type') or 'metering',
 | 
			
		||||
        endpoint_type=kwargs.get('endpoint_type') or 'publicURL')
 | 
			
		||||
def Client(version, *args, **kwargs):
 | 
			
		||||
    module = utils.import_versioned_module(version, 'client')
 | 
			
		||||
    client_class = getattr(module, 'Client')
 | 
			
		||||
    kwargs['token'] = kwargs.get('token') or kwargs.get('auth_token')
 | 
			
		||||
    return client_class(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_client(api_version, **kwargs):
 | 
			
		||||
    """Get an authtenticated client, based on the credentials
 | 
			
		||||
       in the keyword args.
 | 
			
		||||
def _adjust_params(kwargs):
 | 
			
		||||
    timeout = kwargs.get('timeout')
 | 
			
		||||
    if timeout is not None:
 | 
			
		||||
        timeout = int(timeout)
 | 
			
		||||
        if timeout <= 0:
 | 
			
		||||
            timeout = None
 | 
			
		||||
 | 
			
		||||
    insecure = strutils.bool_from_string(kwargs.get('insecure'))
 | 
			
		||||
    verify = kwargs.get('verify')
 | 
			
		||||
    if verify is None:
 | 
			
		||||
        if insecure:
 | 
			
		||||
            verify = False
 | 
			
		||||
        else:
 | 
			
		||||
            verify = kwargs.get('cacert') or True
 | 
			
		||||
 | 
			
		||||
    cert = kwargs.get('cert_file')
 | 
			
		||||
    key = kwargs.get('key_file')
 | 
			
		||||
    if cert and key:
 | 
			
		||||
        cert = cert, key
 | 
			
		||||
    return {'verify': verify, 'cert': cert, 'timeout': timeout}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_client(version, **kwargs):
 | 
			
		||||
    """Get an authenticated client, based on the credentials in the kwargs.
 | 
			
		||||
 | 
			
		||||
    :param api_version: the API version to use ('1' or '2')
 | 
			
		||||
    :param kwargs: keyword args containing credentials, either:
 | 
			
		||||
            * os_auth_token: pre-existing token to re-use
 | 
			
		||||
            * ceilometer_url: ceilometer API endpoint
 | 
			
		||||
 | 
			
		||||
            * os_auth_token: (DEPRECATED) pre-existing token to re-use,
 | 
			
		||||
                             use os_token instead
 | 
			
		||||
            * os_token: pre-existing token to re-use
 | 
			
		||||
            * ceilometer_url: (DEPRECATED) Ceilometer API endpoint,
 | 
			
		||||
                              use os_endpoint instead
 | 
			
		||||
            * os_endpoint: Ceilometer API endpoint
 | 
			
		||||
            or:
 | 
			
		||||
            * os_username: name of user
 | 
			
		||||
            * os_password: user's password
 | 
			
		||||
            * os_user_id: user's id
 | 
			
		||||
            * os_user_domain_id: the domain id of the user
 | 
			
		||||
            * os_user_domain_name: the domain name of the user
 | 
			
		||||
            * os_project_id: the user project id
 | 
			
		||||
            * os_tenant_id: V2 alternative to os_project_id
 | 
			
		||||
            * os_project_name: the user project name
 | 
			
		||||
            * os_tenant_name: V2 alternative to os_project_name
 | 
			
		||||
            * os_project_domain_name: domain name for the user project
 | 
			
		||||
            * os_project_domain_id: domain id for the user project
 | 
			
		||||
            * os_auth_url: endpoint to authenticate against
 | 
			
		||||
            * os_cacert: path of CA TLS certificate
 | 
			
		||||
            * os_cert|os_cacert: path of CA TLS certificate
 | 
			
		||||
            * os_key: SSL private key
 | 
			
		||||
            * insecure: allow insecure SSL (no cert verification)
 | 
			
		||||
            * os_tenant_{name|id}: name or ID of tenant
 | 
			
		||||
    """
 | 
			
		||||
    token = kwargs.get('os_auth_token')
 | 
			
		||||
    if token:
 | 
			
		||||
        token = (token if six.callable(token) else lambda: token)
 | 
			
		||||
    endpoint = kwargs.get('os_endpoint') or kwargs.get('ceilometer_url')
 | 
			
		||||
 | 
			
		||||
    if token and kwargs.get('ceilometer_url'):
 | 
			
		||||
        endpoint = kwargs.get('ceilometer_url')
 | 
			
		||||
    elif (kwargs.get('os_username') and
 | 
			
		||||
          kwargs.get('os_password') and
 | 
			
		||||
          kwargs.get('os_auth_url') and
 | 
			
		||||
          (kwargs.get('os_tenant_id') or kwargs.get('os_tenant_name'))):
 | 
			
		||||
 | 
			
		||||
        ks_kwargs = {
 | 
			
		||||
    cli_kwargs = {
 | 
			
		||||
        'username': kwargs.get('os_username'),
 | 
			
		||||
        'password': kwargs.get('os_password'),
 | 
			
		||||
        'tenant_id': kwargs.get('os_tenant_id'),
 | 
			
		||||
@@ -81,27 +285,36 @@ def get_client(api_version, **kwargs):
 | 
			
		||||
        'service_type': kwargs.get('os_service_type'),
 | 
			
		||||
        'endpoint_type': kwargs.get('os_endpoint_type'),
 | 
			
		||||
        'cacert': kwargs.get('os_cacert'),
 | 
			
		||||
            'insecure': kwargs.get('insecure'),
 | 
			
		||||
        }
 | 
			
		||||
        _ksclient = _get_ksclient(**ks_kwargs)
 | 
			
		||||
        token = token or (lambda: _ksclient.auth_token)
 | 
			
		||||
 | 
			
		||||
        endpoint = kwargs.get('ceilometer_url') or \
 | 
			
		||||
            _get_endpoint(_ksclient, **ks_kwargs)
 | 
			
		||||
 | 
			
		||||
    cli_kwargs = {
 | 
			
		||||
        'token': token,
 | 
			
		||||
        'insecure': kwargs.get('insecure'),
 | 
			
		||||
        'timeout': kwargs.get('timeout'),
 | 
			
		||||
        'cacert': kwargs.get('os_cacert'),
 | 
			
		||||
        'cert_file': kwargs.get('cert_file'),
 | 
			
		||||
        'key_file': kwargs.get('key_file'),
 | 
			
		||||
        'cert_file': kwargs.get('os_cert'),
 | 
			
		||||
        'key_file': kwargs.get('os_key'),
 | 
			
		||||
        'token': kwargs.get('os_token') or kwargs.get('os_auth_token'),
 | 
			
		||||
        'user_domain_name': kwargs.get('os_user_domain_name'),
 | 
			
		||||
        'user_domain_id': kwargs.get('os_user_domain_id'),
 | 
			
		||||
        'project_domain_name': kwargs.get('os_project_domain_name'),
 | 
			
		||||
        'project_domain_id': kwargs.get('os_project_domain_id'),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Client(api_version, endpoint, **cli_kwargs)
 | 
			
		||||
    cli_kwargs.update(kwargs)
 | 
			
		||||
    cli_kwargs.update(_adjust_params(cli_kwargs))
 | 
			
		||||
 | 
			
		||||
    return Client(version, endpoint, **cli_kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def Client(version, *args, **kwargs):
 | 
			
		||||
    module = utils.import_versioned_module(version, 'client')
 | 
			
		||||
    client_class = getattr(module, 'Client')
 | 
			
		||||
    return client_class(*args, **kwargs)
 | 
			
		||||
def get_auth_plugin(endpoint, **kwargs):
 | 
			
		||||
    auth_plugin = AuthPlugin(
 | 
			
		||||
        auth_url=kwargs.get('auth_url'),
 | 
			
		||||
        service_type=kwargs.get('service_type'),
 | 
			
		||||
        token=kwargs.get('token'),
 | 
			
		||||
        endpoint_type=kwargs.get('endpoint_type'),
 | 
			
		||||
        cacert=kwargs.get('cacert'),
 | 
			
		||||
        tenant_id=kwargs.get('project_id') or kwargs.get('tenant_id'),
 | 
			
		||||
        endpoint=endpoint,
 | 
			
		||||
        username=kwargs.get('username'),
 | 
			
		||||
        password=kwargs.get('password'),
 | 
			
		||||
        tenant_name=kwargs.get('tenant_name'),
 | 
			
		||||
        user_domain_name=kwargs.get('user_domain_name'),
 | 
			
		||||
        user_domain_id=kwargs.get('user_domain_id'),
 | 
			
		||||
        project_domain_name=kwargs.get('project_domain_name'),
 | 
			
		||||
        project_domain_id=kwargs.get('project_domain_id')
 | 
			
		||||
    )
 | 
			
		||||
    return auth_plugin
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ Base utilities to build API operation managers and objects on top of.
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
 | 
			
		||||
from ceilometerclient import exc
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import base
 | 
			
		||||
 | 
			
		||||
# Python 2.4 compat
 | 
			
		||||
@@ -30,7 +31,9 @@ except NameError:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getid(obj):
 | 
			
		||||
    """Abstracts the common pattern of allowing both an object or an
 | 
			
		||||
    """Extracts object ID.
 | 
			
		||||
 | 
			
		||||
    Abstracts the common pattern of allowing both an object or an
 | 
			
		||||
    object's ID (UUID) as a parameter when dealing with relationships.
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
@@ -40,22 +43,32 @@ def getid(obj):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Manager(object):
 | 
			
		||||
    """Managers interact with a particular type of API
 | 
			
		||||
    (samples, meters, alarms, etc.) and provide CRUD operations for them.
 | 
			
		||||
    """Managers interact with a particular type of API.
 | 
			
		||||
 | 
			
		||||
    It works with samples, meters, alarms, etc. and provide CRUD operations for
 | 
			
		||||
    them.
 | 
			
		||||
    """
 | 
			
		||||
    resource_class = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, api):
 | 
			
		||||
        self.api = api
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def client(self):
 | 
			
		||||
        """Compatible with latest oslo-incubator.apiclient code."""
 | 
			
		||||
        return self.api
 | 
			
		||||
 | 
			
		||||
    def _create(self, url, body):
 | 
			
		||||
        resp, body = self.api.json_request('POST', url, body=body)
 | 
			
		||||
        body = self.api.post(url, json=body).json()
 | 
			
		||||
        if body:
 | 
			
		||||
            return self.resource_class(self, body)
 | 
			
		||||
 | 
			
		||||
    def _list(self, url, response_key=None, obj_class=None, body=None,
 | 
			
		||||
              expect_single=False):
 | 
			
		||||
        resp, body = self.api.json_request('GET', url)
 | 
			
		||||
        resp = self.api.get(url)
 | 
			
		||||
        if not resp.content:
 | 
			
		||||
            raise exc.HTTPNotFound
 | 
			
		||||
        body = resp.json()
 | 
			
		||||
 | 
			
		||||
        if obj_class is None:
 | 
			
		||||
            obj_class = self.resource_class
 | 
			
		||||
@@ -72,18 +85,20 @@ class Manager(object):
 | 
			
		||||
        return [obj_class(self, res, loaded=True) for res in data if res]
 | 
			
		||||
 | 
			
		||||
    def _update(self, url, body, response_key=None):
 | 
			
		||||
        resp, body = self.api.json_request('PUT', url, body=body)
 | 
			
		||||
        body = self.api.put(url, json=body).json()
 | 
			
		||||
        # PUT requests may not return a body
 | 
			
		||||
        if body:
 | 
			
		||||
            return self.resource_class(self, body)
 | 
			
		||||
 | 
			
		||||
    def _delete(self, url):
 | 
			
		||||
        self.api.raw_request('DELETE', url)
 | 
			
		||||
        self.api.delete(url)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Resource(base.Resource):
 | 
			
		||||
    """A resource represents a particular instance of an object (tenant, user,
 | 
			
		||||
    etc). This is pretty much just a bag for attributes.
 | 
			
		||||
    """A resource represents a particular instance of an object.
 | 
			
		||||
 | 
			
		||||
    Resource might be tenant, user, etc.
 | 
			
		||||
    This is pretty much just a bag for attributes.
 | 
			
		||||
 | 
			
		||||
    :param manager: Manager object
 | 
			
		||||
    :param info: dictionary representing resource attributes
 | 
			
		||||
 
 | 
			
		||||
@@ -1,301 +0,0 @@
 | 
			
		||||
# Copyright 2012 OpenStack Foundation
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import socket
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import ssl
 | 
			
		||||
except ImportError:
 | 
			
		||||
    #TODO(bcwaldon): Handle this failure more gracefully
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import json
 | 
			
		||||
except ImportError:
 | 
			
		||||
    import simplejson as json
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
from six.moves import http_client as httplib  # noqa
 | 
			
		||||
from six.moves.urllib import parse
 | 
			
		||||
 | 
			
		||||
from ceilometerclient import exc
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
USER_AGENT = 'python-ceilometerclient'
 | 
			
		||||
CHUNKSIZE = 1024 * 64  # 64kB
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPClient(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, endpoint, **kwargs):
 | 
			
		||||
        self.endpoint = endpoint
 | 
			
		||||
        self.auth_token = kwargs.get('token')
 | 
			
		||||
        self.connection_params = self.get_connection_params(endpoint, **kwargs)
 | 
			
		||||
        self.proxy_url = self.get_proxy_url()
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_connection_params(endpoint, **kwargs):
 | 
			
		||||
        parts = parse.urlparse(endpoint)
 | 
			
		||||
 | 
			
		||||
        _args = (parts.hostname, parts.port, parts.path)
 | 
			
		||||
        _kwargs = {'timeout': (float(kwargs.get('timeout'))
 | 
			
		||||
                               if kwargs.get('timeout') else 600)}
 | 
			
		||||
 | 
			
		||||
        if parts.scheme == 'https':
 | 
			
		||||
            _class = VerifiedHTTPSConnection
 | 
			
		||||
            _kwargs['cacert'] = kwargs.get('cacert', None)
 | 
			
		||||
            _kwargs['cert_file'] = kwargs.get('cert_file', None)
 | 
			
		||||
            _kwargs['key_file'] = kwargs.get('key_file', None)
 | 
			
		||||
            _kwargs['insecure'] = kwargs.get('insecure', False)
 | 
			
		||||
        elif parts.scheme == 'http':
 | 
			
		||||
            _class = httplib.HTTPConnection
 | 
			
		||||
        else:
 | 
			
		||||
            msg = 'Unsupported scheme: %s' % parts.scheme
 | 
			
		||||
            raise exc.InvalidEndpoint(msg)
 | 
			
		||||
 | 
			
		||||
        return (_class, _args, _kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_connection(self):
 | 
			
		||||
        _class = self.connection_params[0]
 | 
			
		||||
        try:
 | 
			
		||||
            if self.proxy_url:
 | 
			
		||||
                proxy_parts = parse.urlparse(self.proxy_url)
 | 
			
		||||
                return _class(proxy_parts.hostname, proxy_parts.port,
 | 
			
		||||
                              **self.connection_params[2])
 | 
			
		||||
            else:
 | 
			
		||||
                return _class(*self.connection_params[1][0:2],
 | 
			
		||||
                              **self.connection_params[2])
 | 
			
		||||
        except httplib.InvalidURL:
 | 
			
		||||
            raise exc.InvalidEndpoint()
 | 
			
		||||
 | 
			
		||||
    def log_curl_request(self, method, url, kwargs):
 | 
			
		||||
        curl = ['curl -i -X %s' % method]
 | 
			
		||||
 | 
			
		||||
        for (key, value) in kwargs['headers'].items():
 | 
			
		||||
            header = '-H \'%s: %s\'' % (key, value)
 | 
			
		||||
            curl.append(header)
 | 
			
		||||
 | 
			
		||||
        conn_params_fmt = [
 | 
			
		||||
            ('key_file', '--key %s'),
 | 
			
		||||
            ('cert_file', '--cert %s'),
 | 
			
		||||
            ('cacert', '--cacert %s'),
 | 
			
		||||
        ]
 | 
			
		||||
        for (key, fmt) in conn_params_fmt:
 | 
			
		||||
            value = self.connection_params[2].get(key)
 | 
			
		||||
            if value:
 | 
			
		||||
                curl.append(fmt % value)
 | 
			
		||||
 | 
			
		||||
        if self.connection_params[2].get('insecure'):
 | 
			
		||||
            curl.append('-k')
 | 
			
		||||
 | 
			
		||||
        if 'body' in kwargs:
 | 
			
		||||
            curl.append('-d \'%s\'' % kwargs['body'])
 | 
			
		||||
 | 
			
		||||
        curl.append('%s/%s' % (self.endpoint.rstrip('/'), url.lstrip('/')))
 | 
			
		||||
        LOG.debug(' '.join(curl))
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def log_http_response(resp, body=None):
 | 
			
		||||
        status = (resp.version / 10.0, resp.status, resp.reason)
 | 
			
		||||
        dump = ['\nHTTP/%.1f %s %s' % status]
 | 
			
		||||
        dump.extend(['%s: %s' % (k, v) for k, v in resp.getheaders()])
 | 
			
		||||
        dump.append('')
 | 
			
		||||
        if body:
 | 
			
		||||
            dump.extend([body, ''])
 | 
			
		||||
        LOG.debug('\n'.join(dump))
 | 
			
		||||
 | 
			
		||||
    def _make_connection_url(self, url):
 | 
			
		||||
        (_class, _args, _kwargs) = self.connection_params
 | 
			
		||||
        base_url = _args[2]
 | 
			
		||||
        return '%s/%s' % (base_url.rstrip('/'), url.lstrip('/'))
 | 
			
		||||
 | 
			
		||||
    def _http_request(self, url, method, **kwargs):
 | 
			
		||||
        """Send an http request with the specified characteristics.
 | 
			
		||||
 | 
			
		||||
        Wrapper around httplib.HTTP(S)Connection.request to handle tasks such
 | 
			
		||||
        as setting headers and error handling.
 | 
			
		||||
        """
 | 
			
		||||
        # Copy the kwargs so we can reuse the original in case of redirects
 | 
			
		||||
        kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
 | 
			
		||||
        kwargs['headers'].setdefault('User-Agent', USER_AGENT)
 | 
			
		||||
        auth_token = self.auth_token()
 | 
			
		||||
        if auth_token:
 | 
			
		||||
            kwargs['headers'].setdefault('X-Auth-Token', auth_token)
 | 
			
		||||
 | 
			
		||||
        self.log_curl_request(method, url, kwargs)
 | 
			
		||||
        conn = self.get_connection()
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            if self.proxy_url:
 | 
			
		||||
                conn_url = (self.endpoint.rstrip('/') +
 | 
			
		||||
                            self._make_connection_url(url))
 | 
			
		||||
            else:
 | 
			
		||||
                conn_url = self._make_connection_url(url)
 | 
			
		||||
            conn.request(method, conn_url, **kwargs)
 | 
			
		||||
            resp = conn.getresponse()
 | 
			
		||||
        except socket.gaierror as e:
 | 
			
		||||
            message = ("Error finding address for %(url)s: %(e)s"
 | 
			
		||||
                       % dict(url=url, e=e))
 | 
			
		||||
            raise exc.InvalidEndpoint(message=message)
 | 
			
		||||
        except (socket.error, socket.timeout) as e:
 | 
			
		||||
            endpoint = self.endpoint
 | 
			
		||||
            message = ("Error communicating with %(endpoint)s %(e)s"
 | 
			
		||||
                       % dict(endpoint=endpoint, e=e))
 | 
			
		||||
            raise exc.CommunicationError(message=message)
 | 
			
		||||
 | 
			
		||||
        body_iter = ResponseBodyIterator(resp)
 | 
			
		||||
 | 
			
		||||
        # Read body into string if it isn't obviously image data
 | 
			
		||||
        if resp.getheader('content-type', None) != 'application/octet-stream':
 | 
			
		||||
            body_str = ''.join([chunk for chunk in body_iter])
 | 
			
		||||
            self.log_http_response(resp, body_str)
 | 
			
		||||
            body_iter = six.StringIO(body_str)
 | 
			
		||||
        else:
 | 
			
		||||
            self.log_http_response(resp)
 | 
			
		||||
 | 
			
		||||
        if 400 <= resp.status < 600:
 | 
			
		||||
            LOG.warn("Request returned failure status.")
 | 
			
		||||
            raise exc.from_response(resp, ''.join(body_iter))
 | 
			
		||||
        elif resp.status in (301, 302, 305):
 | 
			
		||||
            # Redirected. Reissue the request to the new location.
 | 
			
		||||
            return self._http_request(resp['location'], method, **kwargs)
 | 
			
		||||
        elif resp.status == 300:
 | 
			
		||||
            raise exc.from_response(resp)
 | 
			
		||||
 | 
			
		||||
        return resp, body_iter
 | 
			
		||||
 | 
			
		||||
    def json_request(self, method, url, **kwargs):
 | 
			
		||||
        kwargs.setdefault('headers', {})
 | 
			
		||||
        kwargs['headers'].setdefault('Content-Type', 'application/json')
 | 
			
		||||
        kwargs['headers'].setdefault('Accept', 'application/json')
 | 
			
		||||
 | 
			
		||||
        if 'body' in kwargs:
 | 
			
		||||
            kwargs['body'] = json.dumps(kwargs['body'])
 | 
			
		||||
 | 
			
		||||
        resp, body_iter = self._http_request(url, method, **kwargs)
 | 
			
		||||
        content_type = resp.getheader('content-type', None)
 | 
			
		||||
 | 
			
		||||
        if resp.status == 204 or resp.status == 205 or content_type is None:
 | 
			
		||||
            return resp, list()
 | 
			
		||||
 | 
			
		||||
        if 'application/json' in content_type:
 | 
			
		||||
            body = ''.join([chunk for chunk in body_iter])
 | 
			
		||||
            try:
 | 
			
		||||
                body = json.loads(body)
 | 
			
		||||
            except ValueError:
 | 
			
		||||
                LOG.error('Could not decode response body as JSON')
 | 
			
		||||
        else:
 | 
			
		||||
            body = None
 | 
			
		||||
 | 
			
		||||
        return resp, body
 | 
			
		||||
 | 
			
		||||
    def raw_request(self, method, url, **kwargs):
 | 
			
		||||
        kwargs.setdefault('headers', {})
 | 
			
		||||
        kwargs['headers'].setdefault('Content-Type',
 | 
			
		||||
                                     'application/octet-stream')
 | 
			
		||||
        return self._http_request(url, method, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_proxy_url(self):
 | 
			
		||||
        scheme = parse.urlparse(self.endpoint).scheme
 | 
			
		||||
        if scheme == 'https':
 | 
			
		||||
            return os.environ.get('https_proxy')
 | 
			
		||||
        elif scheme == 'http':
 | 
			
		||||
            return os.environ.get('http_proxy')
 | 
			
		||||
        msg = 'Unsupported scheme: %s' % scheme
 | 
			
		||||
        raise exc.InvalidEndpoint(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VerifiedHTTPSConnection(httplib.HTTPSConnection):
 | 
			
		||||
    """httplib-compatibile connection using client-side SSL authentication
 | 
			
		||||
 | 
			
		||||
    :see http://code.activestate.com/recipes/
 | 
			
		||||
            577548-https-httplib-client-connection-with-certificate-v/
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, host, port, key_file=None, cert_file=None,
 | 
			
		||||
                 cacert=None, timeout=None, insecure=False):
 | 
			
		||||
        httplib.HTTPSConnection.__init__(self, host, port, key_file=key_file,
 | 
			
		||||
                                         cert_file=cert_file)
 | 
			
		||||
        self.key_file = key_file
 | 
			
		||||
        self.cert_file = cert_file
 | 
			
		||||
        if cacert is not None:
 | 
			
		||||
            self.cacert = cacert
 | 
			
		||||
        else:
 | 
			
		||||
            self.cacert = self.get_system_ca_file()
 | 
			
		||||
        self.timeout = timeout
 | 
			
		||||
        self.insecure = insecure
 | 
			
		||||
 | 
			
		||||
    def connect(self):
 | 
			
		||||
        """Connect to a host on a given (SSL) port.
 | 
			
		||||
        If cacert is pointing somewhere, use it to check Server Certificate.
 | 
			
		||||
 | 
			
		||||
        Redefined/copied and extended from httplib.py:1105 (Python 2.6.x).
 | 
			
		||||
        This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter to
 | 
			
		||||
        ssl.wrap_socket(), which forces SSL to check server certificate against
 | 
			
		||||
        our client certificate.
 | 
			
		||||
        """
 | 
			
		||||
        sock = socket.create_connection((self.host, self.port), self.timeout)
 | 
			
		||||
 | 
			
		||||
        if self._tunnel_host:
 | 
			
		||||
            self.sock = sock
 | 
			
		||||
            self._tunnel()
 | 
			
		||||
 | 
			
		||||
        if self.insecure is True:
 | 
			
		||||
            kwargs = {'cert_reqs': ssl.CERT_NONE}
 | 
			
		||||
        else:
 | 
			
		||||
            kwargs = {'cert_reqs': ssl.CERT_REQUIRED, 'ca_certs': self.cacert}
 | 
			
		||||
 | 
			
		||||
        if self.cert_file:
 | 
			
		||||
            kwargs['certfile'] = self.cert_file
 | 
			
		||||
            if self.key_file:
 | 
			
		||||
                kwargs['keyfile'] = self.key_file
 | 
			
		||||
 | 
			
		||||
        self.sock = ssl.wrap_socket(sock, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_system_ca_file():
 | 
			
		||||
        """Return path to system default CA file."""
 | 
			
		||||
        # Standard CA file locations for Debian/Ubuntu, RedHat/Fedora,
 | 
			
		||||
        # Suse, FreeBSD/OpenBSD
 | 
			
		||||
        ca_path = ['/etc/ssl/certs/ca-certificates.crt',
 | 
			
		||||
                   '/etc/pki/tls/certs/ca-bundle.crt',
 | 
			
		||||
                   '/etc/ssl/ca-bundle.pem',
 | 
			
		||||
                   '/etc/ssl/cert.pem']
 | 
			
		||||
        for ca in ca_path:
 | 
			
		||||
            if os.path.exists(ca):
 | 
			
		||||
                return ca
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResponseBodyIterator(object):
 | 
			
		||||
    """A class that acts as an iterator over an HTTP response."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, resp):
 | 
			
		||||
        self.resp = resp
 | 
			
		||||
 | 
			
		||||
    def __iter__(self):
 | 
			
		||||
        while True:
 | 
			
		||||
            yield self.next()
 | 
			
		||||
 | 
			
		||||
    def next(self):
 | 
			
		||||
        chunk = self.resp.read(CHUNKSIZE)
 | 
			
		||||
        if chunk:
 | 
			
		||||
            return chunk
 | 
			
		||||
        else:
 | 
			
		||||
            raise StopIteration()
 | 
			
		||||
@@ -14,17 +14,19 @@
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import textwrap
 | 
			
		||||
import uuid
 | 
			
		||||
 | 
			
		||||
from oslo.serialization import jsonutils
 | 
			
		||||
from oslo.utils import encodeutils
 | 
			
		||||
from oslo.utils import importutils
 | 
			
		||||
import prettytable
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from ceilometerclient import exc
 | 
			
		||||
from ceilometerclient.openstack.common import cliutils
 | 
			
		||||
from ceilometerclient.openstack.common import importutils
 | 
			
		||||
from ceilometerclient.openstack.common import strutils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Decorator for cli-args
 | 
			
		||||
@@ -83,15 +85,12 @@ def format_nested_list_of_dict(l, column_names):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def print_dict(d, dict_property="Property", wrap=0):
 | 
			
		||||
    pt = prettytable.PrettyTable([dict_property, 'Value'],
 | 
			
		||||
                                 caching=False, print_empty=False)
 | 
			
		||||
    pt = prettytable.PrettyTable([dict_property, 'Value'], print_empty=False)
 | 
			
		||||
    pt.align = 'l'
 | 
			
		||||
    for k, v in sorted(six.iteritems(d)):
 | 
			
		||||
        # convert dict to str to check length
 | 
			
		||||
        if isinstance(v, dict):
 | 
			
		||||
            v = str(v)
 | 
			
		||||
        if isinstance(v, six.string_types):
 | 
			
		||||
            v = strutils.safe_encode(v)
 | 
			
		||||
            v = jsonutils.dumps(v)
 | 
			
		||||
        # if value has a newline, add in multiple rows
 | 
			
		||||
        # e.g. fault with stacktrace
 | 
			
		||||
        if v and isinstance(v, six.string_types) and r'\n' in v:
 | 
			
		||||
@@ -106,7 +105,11 @@ def print_dict(d, dict_property="Property", wrap=0):
 | 
			
		||||
            if wrap > 0:
 | 
			
		||||
                v = textwrap.fill(str(v), wrap)
 | 
			
		||||
            pt.add_row([k, v])
 | 
			
		||||
    print(pt.get_string())
 | 
			
		||||
    encoded = encodeutils.safe_encode(pt.get_string())
 | 
			
		||||
    # FIXME(gordc): https://bugs.launchpad.net/oslo-incubator/+bug/1370710
 | 
			
		||||
    if six.PY3:
 | 
			
		||||
        encoded = encoded.decode()
 | 
			
		||||
    print(encoded)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_resource(manager, name_or_id):
 | 
			
		||||
@@ -115,20 +118,20 @@ def find_resource(manager, name_or_id):
 | 
			
		||||
    try:
 | 
			
		||||
        if isinstance(name_or_id, int) or name_or_id.isdigit():
 | 
			
		||||
            return manager.get(int(name_or_id))
 | 
			
		||||
    except exc.NotFound:
 | 
			
		||||
    except exc.HTTPNotFound:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    # now try to get entity as uuid
 | 
			
		||||
    try:
 | 
			
		||||
        uuid.UUID(str(name_or_id))
 | 
			
		||||
        return manager.get(name_or_id)
 | 
			
		||||
    except (ValueError, exc.NotFound):
 | 
			
		||||
    except (ValueError, exc.HTTPNotFound):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    # finally try to find entity by name
 | 
			
		||||
    try:
 | 
			
		||||
        return manager.find(name=name_or_id)
 | 
			
		||||
    except exc.NotFound:
 | 
			
		||||
    except exc.HTTPNotFound:
 | 
			
		||||
        msg = "No %s with a name or ID of '%s' exists." % \
 | 
			
		||||
              (manager.resource_class.__name__.lower(), name_or_id)
 | 
			
		||||
        raise exc.CommandError(msg)
 | 
			
		||||
@@ -155,8 +158,7 @@ def args_array_to_dict(kwargs, key_to_convert):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def args_array_to_list_of_dicts(kwargs, key_to_convert):
 | 
			
		||||
    """Converts ['a=1;b=2','c=3;d=4'] to [{a:1,b:2},{c:3,d:4}]
 | 
			
		||||
    """
 | 
			
		||||
    """Converts ['a=1;b=2','c=3;d=4'] to [{a:1,b:2},{c:3,d:4}]."""
 | 
			
		||||
    values_to_convert = kwargs.get(key_to_convert)
 | 
			
		||||
    if values_to_convert:
 | 
			
		||||
        try:
 | 
			
		||||
 
 | 
			
		||||
@@ -35,11 +35,7 @@ class CommunicationError(BaseException):
 | 
			
		||||
    """Unable to communicate with server."""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ClientException(Exception):
 | 
			
		||||
    """DEPRECATED."""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPException(ClientException):
 | 
			
		||||
class HTTPException(BaseException):
 | 
			
		||||
    """Base exception for all HTTP-derived exceptions."""
 | 
			
		||||
    code = 'N/A'
 | 
			
		||||
 | 
			
		||||
@@ -47,6 +43,14 @@ class HTTPException(ClientException):
 | 
			
		||||
        self.details = details
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        try:
 | 
			
		||||
            data = json.loads(self.details)
 | 
			
		||||
            message = data.get("error_message", {}).get("faultstring")
 | 
			
		||||
            if message:
 | 
			
		||||
                return "%s (HTTP %s) ERROR %s" % (
 | 
			
		||||
                    self.__class__.__name__, self.code, message)
 | 
			
		||||
        except (ValueError, TypeError, AttributeError):
 | 
			
		||||
            pass
 | 
			
		||||
        return "%s (HTTP %s)" % (self.__class__.__name__, self.code)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -60,74 +64,34 @@ class HTTPMultipleChoices(HTTPException):
 | 
			
		||||
                                    self.details)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BadRequest(HTTPException):
 | 
			
		||||
    """DEPRECATED."""
 | 
			
		||||
class HTTPBadRequest(HTTPException):
 | 
			
		||||
    code = 400
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPBadRequest(BadRequest):
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        try:
 | 
			
		||||
            data = json.loads(self.details)
 | 
			
		||||
            message = data.get("error_message", {}).get("faultstring")
 | 
			
		||||
            if message:
 | 
			
		||||
                return "%s (HTTP %s) ERROR %s" % (
 | 
			
		||||
                    self.__class__.__name__, self.code, message)
 | 
			
		||||
        except (ValueError, TypeError, AttributeError):
 | 
			
		||||
            pass
 | 
			
		||||
        return super(HTTPBadRequest, self).__str__()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Unauthorized(HTTPException):
 | 
			
		||||
    """DEPRECATED."""
 | 
			
		||||
class HTTPUnauthorized(HTTPException):
 | 
			
		||||
    code = 401
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPUnauthorized(Unauthorized):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Forbidden(HTTPException):
 | 
			
		||||
    """DEPRECATED."""
 | 
			
		||||
class HTTPForbidden(HTTPException):
 | 
			
		||||
    code = 403
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPForbidden(Forbidden):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NotFound(HTTPException):
 | 
			
		||||
    """DEPRECATED."""
 | 
			
		||||
class HTTPNotFound(HTTPException):
 | 
			
		||||
    code = 404
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPNotFound(NotFound):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPMethodNotAllowed(HTTPException):
 | 
			
		||||
    code = 405
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Conflict(HTTPException):
 | 
			
		||||
    """DEPRECATED."""
 | 
			
		||||
class HTTPConflict(HTTPException):
 | 
			
		||||
    code = 409
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPConflict(Conflict):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OverLimit(HTTPException):
 | 
			
		||||
    """DEPRECATED."""
 | 
			
		||||
class HTTPOverLimit(HTTPException):
 | 
			
		||||
    code = 413
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPOverLimit(OverLimit):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPInternalServerError(HTTPException):
 | 
			
		||||
    code = 500
 | 
			
		||||
 | 
			
		||||
@@ -140,15 +104,10 @@ class HTTPBadGateway(HTTPException):
 | 
			
		||||
    code = 502
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ServiceUnavailable(HTTPException):
 | 
			
		||||
    """DEPRECATED."""
 | 
			
		||||
class HTTPServiceUnavailable(HTTPException):
 | 
			
		||||
    code = 503
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPServiceUnavailable(ServiceUnavailable):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# NOTE(bcwaldon): Build a mapping of HTTP codes to corresponding exception
 | 
			
		||||
# classes
 | 
			
		||||
_code_map = {}
 | 
			
		||||
@@ -162,13 +121,3 @@ def from_response(response, details=None):
 | 
			
		||||
    """Return an instance of an HTTPException based on httplib response."""
 | 
			
		||||
    cls = _code_map.get(response.status, HTTPException)
 | 
			
		||||
    return cls(details)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NoTokenLookupException(Exception):
 | 
			
		||||
    """DEPRECATED."""
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EndpointNotFound(Exception):
 | 
			
		||||
    """DEPRECATED."""
 | 
			
		||||
    pass
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								ceilometerclient/openstack/common/_i18n.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								ceilometerclient/openstack/common/_i18n.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
"""oslo.i18n integration module.
 | 
			
		||||
 | 
			
		||||
See http://docs.openstack.org/developer/oslo.i18n/usage.html
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import oslo.i18n
 | 
			
		||||
 | 
			
		||||
    # NOTE(dhellmann): This reference to o-s-l-o will be replaced by the
 | 
			
		||||
    # application name when this module is synced into the separate
 | 
			
		||||
    # repository. It is OK to have more than one translation function
 | 
			
		||||
    # using the same domain, since there will still only be one message
 | 
			
		||||
    # catalog.
 | 
			
		||||
    _translators = oslo.i18n.TranslatorFactory(domain='ceilometerclient')
 | 
			
		||||
 | 
			
		||||
    # The primary translation function using the well-known name "_"
 | 
			
		||||
    _ = _translators.primary
 | 
			
		||||
 | 
			
		||||
    # Translators for log levels.
 | 
			
		||||
    #
 | 
			
		||||
    # The abbreviated names are meant to reflect the usual use of a short
 | 
			
		||||
    # name like '_'. The "L" is for "log" and the other letter comes from
 | 
			
		||||
    # the level.
 | 
			
		||||
    _LI = _translators.log_info
 | 
			
		||||
    _LW = _translators.log_warning
 | 
			
		||||
    _LE = _translators.log_error
 | 
			
		||||
    _LC = _translators.log_critical
 | 
			
		||||
except ImportError:
 | 
			
		||||
    # NOTE(dims): Support for cases where a project wants to use
 | 
			
		||||
    # code from oslo-incubator, but is not ready to be internationalized
 | 
			
		||||
    # (like tempest)
 | 
			
		||||
    _ = _LI = _LW = _LE = _LC = lambda x: x
 | 
			
		||||
@@ -17,9 +17,21 @@
 | 
			
		||||
# E0202: An attribute inherited from %s hide this method
 | 
			
		||||
# pylint: disable=E0202
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
#
 | 
			
		||||
# THIS MODULE IS DEPRECATED
 | 
			
		||||
#
 | 
			
		||||
# Please refer to
 | 
			
		||||
# https://etherpad.openstack.org/p/kilo-ceilometerclient-library-proposals for
 | 
			
		||||
# the discussion leading to this deprecation.
 | 
			
		||||
#
 | 
			
		||||
# We recommend checking out the python-openstacksdk project
 | 
			
		||||
# (https://launchpad.net/python-openstacksdk) instead.
 | 
			
		||||
#
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
import abc
 | 
			
		||||
import argparse
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
@@ -28,9 +40,6 @@ from stevedore import extension
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import exceptions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_discovered_plugins = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -80,7 +89,7 @@ def load_plugin_from_args(args):
 | 
			
		||||
    alphabetical order.
 | 
			
		||||
 | 
			
		||||
    :type args: argparse.Namespace
 | 
			
		||||
    :raises: AuthorizationFailure
 | 
			
		||||
    :raises: AuthPluginOptionsMissing
 | 
			
		||||
    """
 | 
			
		||||
    auth_system = args.os_auth_system
 | 
			
		||||
    if auth_system:
 | 
			
		||||
 
 | 
			
		||||
@@ -20,16 +20,32 @@
 | 
			
		||||
Base utilities to build API operation managers and objects on top of.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
#
 | 
			
		||||
# THIS MODULE IS DEPRECATED
 | 
			
		||||
#
 | 
			
		||||
# Please refer to
 | 
			
		||||
# https://etherpad.openstack.org/p/kilo-ceilometerclient-library-proposals for
 | 
			
		||||
# the discussion leading to this deprecation.
 | 
			
		||||
#
 | 
			
		||||
# We recommend checking out the python-openstacksdk project
 | 
			
		||||
# (https://launchpad.net/python-openstacksdk) instead.
 | 
			
		||||
#
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# E1102: %s is not callable
 | 
			
		||||
# pylint: disable=E1102
 | 
			
		||||
 | 
			
		||||
import abc
 | 
			
		||||
import copy
 | 
			
		||||
 | 
			
		||||
from oslo.utils import strutils
 | 
			
		||||
import six
 | 
			
		||||
from six.moves.urllib import parse
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common._i18n import _
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import exceptions
 | 
			
		||||
from ceilometerclient.openstack.common import strutils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getid(obj):
 | 
			
		||||
@@ -73,8 +89,8 @@ class HookableMixin(object):
 | 
			
		||||
 | 
			
		||||
        :param cls: class that registers hooks
 | 
			
		||||
        :param hook_type: hook type, e.g., '__pre_parse_args__'
 | 
			
		||||
        :param **args: args to be passed to every hook function
 | 
			
		||||
        :param **kwargs: kwargs to be passed to every hook function
 | 
			
		||||
        :param args: args to be passed to every hook function
 | 
			
		||||
        :param kwargs: kwargs to be passed to every hook function
 | 
			
		||||
        """
 | 
			
		||||
        hook_funcs = cls._hooks_map.get(hook_type) or []
 | 
			
		||||
        for hook_func in hook_funcs:
 | 
			
		||||
@@ -97,12 +113,13 @@ class BaseManager(HookableMixin):
 | 
			
		||||
        super(BaseManager, self).__init__()
 | 
			
		||||
        self.client = client
 | 
			
		||||
 | 
			
		||||
    def _list(self, url, response_key, obj_class=None, json=None):
 | 
			
		||||
    def _list(self, url, response_key=None, obj_class=None, json=None):
 | 
			
		||||
        """List the collection.
 | 
			
		||||
 | 
			
		||||
        :param url: a partial URL, e.g., '/servers'
 | 
			
		||||
        :param response_key: the key to be looked up in response dictionary,
 | 
			
		||||
            e.g., 'servers'
 | 
			
		||||
            e.g., 'servers'. If response_key is None - all response body
 | 
			
		||||
            will be used.
 | 
			
		||||
        :param obj_class: class for constructing the returned objects
 | 
			
		||||
            (self.resource_class will be used by default)
 | 
			
		||||
        :param json: data that will be encoded as JSON and passed in POST
 | 
			
		||||
@@ -116,7 +133,7 @@ class BaseManager(HookableMixin):
 | 
			
		||||
        if obj_class is None:
 | 
			
		||||
            obj_class = self.resource_class
 | 
			
		||||
 | 
			
		||||
        data = body[response_key]
 | 
			
		||||
        data = body[response_key] if response_key is not None else body
 | 
			
		||||
        # NOTE(ja): keystone returns values as list as {'values': [ ... ]}
 | 
			
		||||
        #           unlike other services which just return the list...
 | 
			
		||||
        try:
 | 
			
		||||
@@ -126,15 +143,17 @@ class BaseManager(HookableMixin):
 | 
			
		||||
 | 
			
		||||
        return [obj_class(self, res, loaded=True) for res in data if res]
 | 
			
		||||
 | 
			
		||||
    def _get(self, url, response_key):
 | 
			
		||||
    def _get(self, url, response_key=None):
 | 
			
		||||
        """Get an object from collection.
 | 
			
		||||
 | 
			
		||||
        :param url: a partial URL, e.g., '/servers'
 | 
			
		||||
        :param response_key: the key to be looked up in response dictionary,
 | 
			
		||||
            e.g., 'server'
 | 
			
		||||
            e.g., 'server'. If response_key is None - all response body
 | 
			
		||||
            will be used.
 | 
			
		||||
        """
 | 
			
		||||
        body = self.client.get(url).json()
 | 
			
		||||
        return self.resource_class(self, body[response_key], loaded=True)
 | 
			
		||||
        data = body[response_key] if response_key is not None else body
 | 
			
		||||
        return self.resource_class(self, data, loaded=True)
 | 
			
		||||
 | 
			
		||||
    def _head(self, url):
 | 
			
		||||
        """Retrieve request headers for an object.
 | 
			
		||||
@@ -144,21 +163,23 @@ class BaseManager(HookableMixin):
 | 
			
		||||
        resp = self.client.head(url)
 | 
			
		||||
        return resp.status_code == 204
 | 
			
		||||
 | 
			
		||||
    def _post(self, url, json, response_key, return_raw=False):
 | 
			
		||||
    def _post(self, url, json, response_key=None, return_raw=False):
 | 
			
		||||
        """Create an object.
 | 
			
		||||
 | 
			
		||||
        :param url: a partial URL, e.g., '/servers'
 | 
			
		||||
        :param json: data that will be encoded as JSON and passed in POST
 | 
			
		||||
            request (GET will be sent by default)
 | 
			
		||||
        :param response_key: the key to be looked up in response dictionary,
 | 
			
		||||
            e.g., 'servers'
 | 
			
		||||
            e.g., 'server'. If response_key is None - all response body
 | 
			
		||||
            will be used.
 | 
			
		||||
        :param return_raw: flag to force returning raw JSON instead of
 | 
			
		||||
            Python object of self.resource_class
 | 
			
		||||
        """
 | 
			
		||||
        body = self.client.post(url, json=json).json()
 | 
			
		||||
        data = body[response_key] if response_key is not None else body
 | 
			
		||||
        if return_raw:
 | 
			
		||||
            return body[response_key]
 | 
			
		||||
        return self.resource_class(self, body[response_key])
 | 
			
		||||
            return data
 | 
			
		||||
        return self.resource_class(self, data)
 | 
			
		||||
 | 
			
		||||
    def _put(self, url, json=None, response_key=None):
 | 
			
		||||
        """Update an object with PUT method.
 | 
			
		||||
@@ -167,7 +188,8 @@ class BaseManager(HookableMixin):
 | 
			
		||||
        :param json: data that will be encoded as JSON and passed in POST
 | 
			
		||||
            request (GET will be sent by default)
 | 
			
		||||
        :param response_key: the key to be looked up in response dictionary,
 | 
			
		||||
            e.g., 'servers'
 | 
			
		||||
            e.g., 'servers'. If response_key is None - all response body
 | 
			
		||||
            will be used.
 | 
			
		||||
        """
 | 
			
		||||
        resp = self.client.put(url, json=json)
 | 
			
		||||
        # PUT requests may not return a body
 | 
			
		||||
@@ -185,7 +207,8 @@ class BaseManager(HookableMixin):
 | 
			
		||||
        :param json: data that will be encoded as JSON and passed in POST
 | 
			
		||||
            request (GET will be sent by default)
 | 
			
		||||
        :param response_key: the key to be looked up in response dictionary,
 | 
			
		||||
            e.g., 'servers'
 | 
			
		||||
            e.g., 'servers'. If response_key is None - all response body
 | 
			
		||||
            will be used.
 | 
			
		||||
        """
 | 
			
		||||
        body = self.client.patch(url, json=json).json()
 | 
			
		||||
        if response_key is not None:
 | 
			
		||||
@@ -218,7 +241,10 @@ class ManagerWithFind(BaseManager):
 | 
			
		||||
        matches = self.findall(**kwargs)
 | 
			
		||||
        num_matches = len(matches)
 | 
			
		||||
        if num_matches == 0:
 | 
			
		||||
            msg = "No %s matching %s." % (self.resource_class.__name__, kwargs)
 | 
			
		||||
            msg = _("No %(name)s matching %(args)s.") % {
 | 
			
		||||
                'name': self.resource_class.__name__,
 | 
			
		||||
                'args': kwargs
 | 
			
		||||
            }
 | 
			
		||||
            raise exceptions.NotFound(msg)
 | 
			
		||||
        elif num_matches > 1:
 | 
			
		||||
            raise exceptions.NoUniqueMatch()
 | 
			
		||||
@@ -372,7 +398,10 @@ class CrudManager(BaseManager):
 | 
			
		||||
        num = len(rl)
 | 
			
		||||
 | 
			
		||||
        if num == 0:
 | 
			
		||||
            msg = "No %s matching %s." % (self.resource_class.__name__, kwargs)
 | 
			
		||||
            msg = _("No %(name)s matching %(args)s.") % {
 | 
			
		||||
                'name': self.resource_class.__name__,
 | 
			
		||||
                'args': kwargs
 | 
			
		||||
            }
 | 
			
		||||
            raise exceptions.NotFound(404, msg)
 | 
			
		||||
        elif num > 1:
 | 
			
		||||
            raise exceptions.NoUniqueMatch
 | 
			
		||||
@@ -440,8 +469,10 @@ class Resource(object):
 | 
			
		||||
    def human_id(self):
 | 
			
		||||
        """Human-readable ID which can be used for bash completion.
 | 
			
		||||
        """
 | 
			
		||||
        if self.NAME_ATTR in self.__dict__ and self.HUMAN_ID:
 | 
			
		||||
            return strutils.to_slug(getattr(self, self.NAME_ATTR))
 | 
			
		||||
        if self.HUMAN_ID:
 | 
			
		||||
            name = getattr(self, self.NAME_ATTR, None)
 | 
			
		||||
            if name is not None:
 | 
			
		||||
                return strutils.to_slug(name)
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def _add_details(self, info):
 | 
			
		||||
@@ -465,6 +496,11 @@ class Resource(object):
 | 
			
		||||
            return self.__dict__[k]
 | 
			
		||||
 | 
			
		||||
    def get(self):
 | 
			
		||||
        """Support for lazy loading details.
 | 
			
		||||
 | 
			
		||||
        Some clients, such as novaclient have the option to lazy load the
 | 
			
		||||
        details, details which can be loaded with this function.
 | 
			
		||||
        """
 | 
			
		||||
        # set_loaded() first ... so if we have to bail, we know we tried.
 | 
			
		||||
        self.set_loaded(True)
 | 
			
		||||
        if not hasattr(self.manager, 'get'):
 | 
			
		||||
@@ -473,6 +509,8 @@ class Resource(object):
 | 
			
		||||
        new = self.manager.get(self.id)
 | 
			
		||||
        if new:
 | 
			
		||||
            self._add_details(new._info)
 | 
			
		||||
            self._add_details(
 | 
			
		||||
                {'x_request_id': self.manager.client.last_request_id})
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        if not isinstance(other, Resource):
 | 
			
		||||
@@ -489,3 +527,6 @@ class Resource(object):
 | 
			
		||||
 | 
			
		||||
    def set_loaded(self, val):
 | 
			
		||||
        self._loaded = val
 | 
			
		||||
 | 
			
		||||
    def to_dict(self):
 | 
			
		||||
        return copy.deepcopy(self._info)
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ OpenStack Client interface. Handles the REST calls and responses.
 | 
			
		||||
# E0202: An attribute inherited from %s hide this method
 | 
			
		||||
# pylint: disable=E0202
 | 
			
		||||
 | 
			
		||||
import hashlib
 | 
			
		||||
import logging
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
@@ -33,19 +34,22 @@ try:
 | 
			
		||||
except ImportError:
 | 
			
		||||
    import json
 | 
			
		||||
 | 
			
		||||
from oslo.utils import encodeutils
 | 
			
		||||
from oslo.utils import importutils
 | 
			
		||||
import requests
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common._i18n import _
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import exceptions
 | 
			
		||||
from ceilometerclient.openstack.common import importutils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_logger = logging.getLogger(__name__)
 | 
			
		||||
SENSITIVE_HEADERS = ('X-Auth-Token', 'X-Subject-Token',)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPClient(object):
 | 
			
		||||
    """This client handles sending HTTP requests to OpenStack servers.
 | 
			
		||||
 | 
			
		||||
    Features:
 | 
			
		||||
 | 
			
		||||
    - share authentication information between several clients to different
 | 
			
		||||
      services (e.g., for compute and image clients);
 | 
			
		||||
    - reissue authentication request for expired tokens;
 | 
			
		||||
@@ -96,19 +100,32 @@ class HTTPClient(object):
 | 
			
		||||
        self.http = http or requests.Session()
 | 
			
		||||
 | 
			
		||||
        self.cached_token = None
 | 
			
		||||
        self.last_request_id = None
 | 
			
		||||
 | 
			
		||||
    def _safe_header(self, name, value):
 | 
			
		||||
        if name in SENSITIVE_HEADERS:
 | 
			
		||||
            # because in python3 byte string handling is ... ug
 | 
			
		||||
            v = value.encode('utf-8')
 | 
			
		||||
            h = hashlib.sha1(v)
 | 
			
		||||
            d = h.hexdigest()
 | 
			
		||||
            return encodeutils.safe_decode(name), "{SHA1}%s" % d
 | 
			
		||||
        else:
 | 
			
		||||
            return (encodeutils.safe_decode(name),
 | 
			
		||||
                    encodeutils.safe_decode(value))
 | 
			
		||||
 | 
			
		||||
    def _http_log_req(self, method, url, kwargs):
 | 
			
		||||
        if not self.debug:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        string_parts = [
 | 
			
		||||
            "curl -i",
 | 
			
		||||
            "curl -g -i",
 | 
			
		||||
            "-X '%s'" % method,
 | 
			
		||||
            "'%s'" % url,
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        for element in kwargs['headers']:
 | 
			
		||||
            header = "-H '%s: %s'" % (element, kwargs['headers'][element])
 | 
			
		||||
            header = ("-H '%s: %s'" %
 | 
			
		||||
                      self._safe_header(element, kwargs['headers'][element]))
 | 
			
		||||
            string_parts.append(header)
 | 
			
		||||
 | 
			
		||||
        _logger.debug("REQ: %s" % " ".join(string_parts))
 | 
			
		||||
@@ -151,10 +168,10 @@ class HTTPClient(object):
 | 
			
		||||
        :param method: method of HTTP request
 | 
			
		||||
        :param url: URL of HTTP request
 | 
			
		||||
        :param kwargs: any other parameter that can be passed to
 | 
			
		||||
'            requests.Session.request (such as `headers`) or `json`
 | 
			
		||||
             requests.Session.request (such as `headers`) or `json`
 | 
			
		||||
             that will be encoded as JSON and used as `data` argument
 | 
			
		||||
        """
 | 
			
		||||
        kwargs.setdefault("headers", kwargs.get("headers", {}))
 | 
			
		||||
        kwargs.setdefault("headers", {})
 | 
			
		||||
        kwargs["headers"]["User-Agent"] = self.user_agent
 | 
			
		||||
        if self.original_ip:
 | 
			
		||||
            kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % (
 | 
			
		||||
@@ -175,6 +192,8 @@ class HTTPClient(object):
 | 
			
		||||
                               start_time, time.time()))
 | 
			
		||||
        self._http_log_resp(resp)
 | 
			
		||||
 | 
			
		||||
        self.last_request_id = resp.headers.get('x-openstack-request-id')
 | 
			
		||||
 | 
			
		||||
        if resp.status_code >= 400:
 | 
			
		||||
            _logger.debug(
 | 
			
		||||
                "Request returned failure status: %s",
 | 
			
		||||
@@ -206,7 +225,7 @@ class HTTPClient(object):
 | 
			
		||||
        :param method: method of HTTP request
 | 
			
		||||
        :param url: URL of HTTP request
 | 
			
		||||
        :param kwargs: any other parameter that can be passed to
 | 
			
		||||
'            `HTTPClient.request`
 | 
			
		||||
            `HTTPClient.request`
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        filter_args = {
 | 
			
		||||
@@ -228,7 +247,7 @@ class HTTPClient(object):
 | 
			
		||||
                    **filter_args)
 | 
			
		||||
                if not (token and endpoint):
 | 
			
		||||
                    raise exceptions.AuthorizationFailure(
 | 
			
		||||
                        "Cannot find endpoint or token for request")
 | 
			
		||||
                        _("Cannot find endpoint or token for request"))
 | 
			
		||||
 | 
			
		||||
        old_token_endpoint = (token, endpoint)
 | 
			
		||||
        kwargs.setdefault("headers", {})["X-Auth-Token"] = token
 | 
			
		||||
@@ -245,6 +264,10 @@ class HTTPClient(object):
 | 
			
		||||
                raise
 | 
			
		||||
            self.cached_token = None
 | 
			
		||||
            client.cached_endpoint = None
 | 
			
		||||
            if self.auth_plugin.opts.get('token'):
 | 
			
		||||
                self.auth_plugin.opts['token'] = None
 | 
			
		||||
            if self.auth_plugin.opts.get('endpoint'):
 | 
			
		||||
                self.auth_plugin.opts['endpoint'] = None
 | 
			
		||||
            self.authenticate()
 | 
			
		||||
            try:
 | 
			
		||||
                token, endpoint = self.auth_plugin.token_and_endpoint(
 | 
			
		||||
@@ -321,6 +344,10 @@ class BaseClient(object):
 | 
			
		||||
        return self.http_client.client_request(
 | 
			
		||||
            self, method, url, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def last_request_id(self):
 | 
			
		||||
        return self.http_client.last_request_id
 | 
			
		||||
 | 
			
		||||
    def head(self, url, **kwargs):
 | 
			
		||||
        return self.client_request("HEAD", url, **kwargs)
 | 
			
		||||
 | 
			
		||||
@@ -351,8 +378,11 @@ class BaseClient(object):
 | 
			
		||||
        try:
 | 
			
		||||
            client_path = version_map[str(version)]
 | 
			
		||||
        except (KeyError, ValueError):
 | 
			
		||||
            msg = "Invalid %s client version '%s'. must be one of: %s" % (
 | 
			
		||||
                  (api_name, version, ', '.join(version_map.keys())))
 | 
			
		||||
            msg = _("Invalid %(api_name)s client version '%(version)s'. "
 | 
			
		||||
                    "Must be one of: %(version_map)s") % {
 | 
			
		||||
                        'api_name': api_name,
 | 
			
		||||
                        'version': version,
 | 
			
		||||
                        'version_map': ', '.join(version_map.keys())}
 | 
			
		||||
            raise exceptions.UnsupportedVersion(msg)
 | 
			
		||||
 | 
			
		||||
        return importutils.import_class(client_path)
 | 
			
		||||
 
 | 
			
		||||
@@ -20,11 +20,26 @@
 | 
			
		||||
Exception definitions.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
#
 | 
			
		||||
# THIS MODULE IS DEPRECATED
 | 
			
		||||
#
 | 
			
		||||
# Please refer to
 | 
			
		||||
# https://etherpad.openstack.org/p/kilo-ceilometerclient-library-proposals for
 | 
			
		||||
# the discussion leading to this deprecation.
 | 
			
		||||
#
 | 
			
		||||
# We recommend checking out the python-openstacksdk project
 | 
			
		||||
# (https://launchpad.net/python-openstacksdk) instead.
 | 
			
		||||
#
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
import inspect
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common._i18n import _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ClientException(Exception):
 | 
			
		||||
    """The base exception class for all exceptions this library raises.
 | 
			
		||||
@@ -32,14 +47,6 @@ class ClientException(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MissingArgs(ClientException):
 | 
			
		||||
    """Supplied arguments are not sufficient for calling a function."""
 | 
			
		||||
    def __init__(self, missing):
 | 
			
		||||
        self.missing = missing
 | 
			
		||||
        msg = "Missing argument(s): %s" % ", ".join(missing)
 | 
			
		||||
        super(MissingArgs, self).__init__(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ValidationError(ClientException):
 | 
			
		||||
    """Error in validation on API client side."""
 | 
			
		||||
    pass
 | 
			
		||||
@@ -60,25 +67,30 @@ class AuthorizationFailure(ClientException):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConnectionRefused(ClientException):
 | 
			
		||||
class ConnectionError(ClientException):
 | 
			
		||||
    """Cannot connect to API service."""
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConnectionRefused(ConnectionError):
 | 
			
		||||
    """Connection refused while trying to connect to API service."""
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AuthPluginOptionsMissing(AuthorizationFailure):
 | 
			
		||||
    """Auth plugin misses some options."""
 | 
			
		||||
    def __init__(self, opt_names):
 | 
			
		||||
        super(AuthPluginOptionsMissing, self).__init__(
 | 
			
		||||
            "Authentication failed. Missing options: %s" %
 | 
			
		||||
            _("Authentication failed. Missing options: %s") %
 | 
			
		||||
            ", ".join(opt_names))
 | 
			
		||||
        self.opt_names = opt_names
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AuthSystemNotFound(AuthorizationFailure):
 | 
			
		||||
    """User has specified a AuthSystem that is not installed."""
 | 
			
		||||
    """User has specified an AuthSystem that is not installed."""
 | 
			
		||||
    def __init__(self, auth_system):
 | 
			
		||||
        super(AuthSystemNotFound, self).__init__(
 | 
			
		||||
            "AuthSystemNotFound: %s" % repr(auth_system))
 | 
			
		||||
            _("AuthSystemNotFound: %s") % repr(auth_system))
 | 
			
		||||
        self.auth_system = auth_system
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -101,7 +113,7 @@ class AmbiguousEndpoints(EndpointException):
 | 
			
		||||
    """Found more than one matching endpoint in Service Catalog."""
 | 
			
		||||
    def __init__(self, endpoints=None):
 | 
			
		||||
        super(AmbiguousEndpoints, self).__init__(
 | 
			
		||||
            "AmbiguousEndpoints: %s" % repr(endpoints))
 | 
			
		||||
            _("AmbiguousEndpoints: %s") % repr(endpoints))
 | 
			
		||||
        self.endpoints = endpoints
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -109,7 +121,7 @@ class HttpError(ClientException):
 | 
			
		||||
    """The base exception class for all HTTP exceptions.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 0
 | 
			
		||||
    message = "HTTP Error"
 | 
			
		||||
    message = _("HTTP Error")
 | 
			
		||||
 | 
			
		||||
    def __init__(self, message=None, details=None,
 | 
			
		||||
                 response=None, request_id=None,
 | 
			
		||||
@@ -127,12 +139,17 @@ class HttpError(ClientException):
 | 
			
		||||
        super(HttpError, self).__init__(formatted_string)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPRedirection(HttpError):
 | 
			
		||||
    """HTTP Redirection."""
 | 
			
		||||
    message = _("HTTP Redirection")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPClientError(HttpError):
 | 
			
		||||
    """Client-side HTTP error.
 | 
			
		||||
 | 
			
		||||
    Exception for cases in which the client seems to have erred.
 | 
			
		||||
    """
 | 
			
		||||
    message = "HTTP Client Error"
 | 
			
		||||
    message = _("HTTP Client Error")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HttpServerError(HttpError):
 | 
			
		||||
@@ -141,7 +158,17 @@ class HttpServerError(HttpError):
 | 
			
		||||
    Exception for cases in which the server is aware that it has
 | 
			
		||||
    erred or is incapable of performing the request.
 | 
			
		||||
    """
 | 
			
		||||
    message = "HTTP Server Error"
 | 
			
		||||
    message = _("HTTP Server Error")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MultipleChoices(HTTPRedirection):
 | 
			
		||||
    """HTTP 300 - Multiple Choices.
 | 
			
		||||
 | 
			
		||||
    Indicates multiple options for the resource that the client may follow.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    http_status = 300
 | 
			
		||||
    message = _("Multiple Choices")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BadRequest(HTTPClientError):
 | 
			
		||||
@@ -150,7 +177,7 @@ class BadRequest(HTTPClientError):
 | 
			
		||||
    The request cannot be fulfilled due to bad syntax.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 400
 | 
			
		||||
    message = "Bad Request"
 | 
			
		||||
    message = _("Bad Request")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Unauthorized(HTTPClientError):
 | 
			
		||||
@@ -160,7 +187,7 @@ class Unauthorized(HTTPClientError):
 | 
			
		||||
    is required and has failed or has not yet been provided.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 401
 | 
			
		||||
    message = "Unauthorized"
 | 
			
		||||
    message = _("Unauthorized")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PaymentRequired(HTTPClientError):
 | 
			
		||||
@@ -169,7 +196,7 @@ class PaymentRequired(HTTPClientError):
 | 
			
		||||
    Reserved for future use.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 402
 | 
			
		||||
    message = "Payment Required"
 | 
			
		||||
    message = _("Payment Required")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Forbidden(HTTPClientError):
 | 
			
		||||
@@ -179,7 +206,7 @@ class Forbidden(HTTPClientError):
 | 
			
		||||
    to it.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 403
 | 
			
		||||
    message = "Forbidden"
 | 
			
		||||
    message = _("Forbidden")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NotFound(HTTPClientError):
 | 
			
		||||
@@ -189,7 +216,7 @@ class NotFound(HTTPClientError):
 | 
			
		||||
    in the future.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 404
 | 
			
		||||
    message = "Not Found"
 | 
			
		||||
    message = _("Not Found")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MethodNotAllowed(HTTPClientError):
 | 
			
		||||
@@ -199,7 +226,7 @@ class MethodNotAllowed(HTTPClientError):
 | 
			
		||||
    by that resource.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 405
 | 
			
		||||
    message = "Method Not Allowed"
 | 
			
		||||
    message = _("Method Not Allowed")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NotAcceptable(HTTPClientError):
 | 
			
		||||
@@ -209,7 +236,7 @@ class NotAcceptable(HTTPClientError):
 | 
			
		||||
    acceptable according to the Accept headers sent in the request.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 406
 | 
			
		||||
    message = "Not Acceptable"
 | 
			
		||||
    message = _("Not Acceptable")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProxyAuthenticationRequired(HTTPClientError):
 | 
			
		||||
@@ -218,7 +245,7 @@ class ProxyAuthenticationRequired(HTTPClientError):
 | 
			
		||||
    The client must first authenticate itself with the proxy.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 407
 | 
			
		||||
    message = "Proxy Authentication Required"
 | 
			
		||||
    message = _("Proxy Authentication Required")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RequestTimeout(HTTPClientError):
 | 
			
		||||
@@ -227,7 +254,7 @@ class RequestTimeout(HTTPClientError):
 | 
			
		||||
    The server timed out waiting for the request.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 408
 | 
			
		||||
    message = "Request Timeout"
 | 
			
		||||
    message = _("Request Timeout")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Conflict(HTTPClientError):
 | 
			
		||||
@@ -237,7 +264,7 @@ class Conflict(HTTPClientError):
 | 
			
		||||
    in the request, such as an edit conflict.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 409
 | 
			
		||||
    message = "Conflict"
 | 
			
		||||
    message = _("Conflict")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Gone(HTTPClientError):
 | 
			
		||||
@@ -247,7 +274,7 @@ class Gone(HTTPClientError):
 | 
			
		||||
    not be available again.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 410
 | 
			
		||||
    message = "Gone"
 | 
			
		||||
    message = _("Gone")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LengthRequired(HTTPClientError):
 | 
			
		||||
@@ -257,7 +284,7 @@ class LengthRequired(HTTPClientError):
 | 
			
		||||
    required by the requested resource.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 411
 | 
			
		||||
    message = "Length Required"
 | 
			
		||||
    message = _("Length Required")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PreconditionFailed(HTTPClientError):
 | 
			
		||||
@@ -267,7 +294,7 @@ class PreconditionFailed(HTTPClientError):
 | 
			
		||||
    put on the request.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 412
 | 
			
		||||
    message = "Precondition Failed"
 | 
			
		||||
    message = _("Precondition Failed")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RequestEntityTooLarge(HTTPClientError):
 | 
			
		||||
@@ -276,7 +303,7 @@ class RequestEntityTooLarge(HTTPClientError):
 | 
			
		||||
    The request is larger than the server is willing or able to process.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 413
 | 
			
		||||
    message = "Request Entity Too Large"
 | 
			
		||||
    message = _("Request Entity Too Large")
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        try:
 | 
			
		||||
@@ -293,7 +320,7 @@ class RequestUriTooLong(HTTPClientError):
 | 
			
		||||
    The URI provided was too long for the server to process.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 414
 | 
			
		||||
    message = "Request-URI Too Long"
 | 
			
		||||
    message = _("Request-URI Too Long")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UnsupportedMediaType(HTTPClientError):
 | 
			
		||||
@@ -303,7 +330,7 @@ class UnsupportedMediaType(HTTPClientError):
 | 
			
		||||
    not support.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 415
 | 
			
		||||
    message = "Unsupported Media Type"
 | 
			
		||||
    message = _("Unsupported Media Type")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RequestedRangeNotSatisfiable(HTTPClientError):
 | 
			
		||||
@@ -313,7 +340,7 @@ class RequestedRangeNotSatisfiable(HTTPClientError):
 | 
			
		||||
    supply that portion.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 416
 | 
			
		||||
    message = "Requested Range Not Satisfiable"
 | 
			
		||||
    message = _("Requested Range Not Satisfiable")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ExpectationFailed(HTTPClientError):
 | 
			
		||||
@@ -322,7 +349,7 @@ class ExpectationFailed(HTTPClientError):
 | 
			
		||||
    The server cannot meet the requirements of the Expect request-header field.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 417
 | 
			
		||||
    message = "Expectation Failed"
 | 
			
		||||
    message = _("Expectation Failed")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UnprocessableEntity(HTTPClientError):
 | 
			
		||||
@@ -332,7 +359,7 @@ class UnprocessableEntity(HTTPClientError):
 | 
			
		||||
    errors.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 422
 | 
			
		||||
    message = "Unprocessable Entity"
 | 
			
		||||
    message = _("Unprocessable Entity")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InternalServerError(HttpServerError):
 | 
			
		||||
@@ -341,7 +368,7 @@ class InternalServerError(HttpServerError):
 | 
			
		||||
    A generic error message, given when no more specific message is suitable.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 500
 | 
			
		||||
    message = "Internal Server Error"
 | 
			
		||||
    message = _("Internal Server Error")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# NotImplemented is a python keyword.
 | 
			
		||||
@@ -352,7 +379,7 @@ class HttpNotImplemented(HttpServerError):
 | 
			
		||||
    the ability to fulfill the request.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 501
 | 
			
		||||
    message = "Not Implemented"
 | 
			
		||||
    message = _("Not Implemented")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BadGateway(HttpServerError):
 | 
			
		||||
@@ -362,7 +389,7 @@ class BadGateway(HttpServerError):
 | 
			
		||||
    response from the upstream server.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 502
 | 
			
		||||
    message = "Bad Gateway"
 | 
			
		||||
    message = _("Bad Gateway")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ServiceUnavailable(HttpServerError):
 | 
			
		||||
@@ -371,7 +398,7 @@ class ServiceUnavailable(HttpServerError):
 | 
			
		||||
    The server is currently unavailable.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 503
 | 
			
		||||
    message = "Service Unavailable"
 | 
			
		||||
    message = _("Service Unavailable")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GatewayTimeout(HttpServerError):
 | 
			
		||||
@@ -381,7 +408,7 @@ class GatewayTimeout(HttpServerError):
 | 
			
		||||
    response from the upstream server.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 504
 | 
			
		||||
    message = "Gateway Timeout"
 | 
			
		||||
    message = _("Gateway Timeout")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HttpVersionNotSupported(HttpServerError):
 | 
			
		||||
@@ -390,7 +417,7 @@ class HttpVersionNotSupported(HttpServerError):
 | 
			
		||||
    The server does not support the HTTP protocol version used in the request.
 | 
			
		||||
    """
 | 
			
		||||
    http_status = 505
 | 
			
		||||
    message = "HTTP Version Not Supported"
 | 
			
		||||
    message = _("HTTP Version Not Supported")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# _code_map contains all the classes that have http_status attribute.
 | 
			
		||||
@@ -408,12 +435,17 @@ def from_response(response, method, url):
 | 
			
		||||
    :param method: HTTP method used for request
 | 
			
		||||
    :param url: URL used for request
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    req_id = response.headers.get("x-openstack-request-id")
 | 
			
		||||
    # NOTE(hdd) true for older versions of nova and cinder
 | 
			
		||||
    if not req_id:
 | 
			
		||||
        req_id = response.headers.get("x-compute-request-id")
 | 
			
		||||
    kwargs = {
 | 
			
		||||
        "http_status": response.status_code,
 | 
			
		||||
        "response": response,
 | 
			
		||||
        "method": method,
 | 
			
		||||
        "url": url,
 | 
			
		||||
        "request_id": response.headers.get("x-compute-request-id"),
 | 
			
		||||
        "request_id": req_id,
 | 
			
		||||
    }
 | 
			
		||||
    if "retry-after" in response.headers:
 | 
			
		||||
        kwargs["retry_after"] = response.headers["retry-after"]
 | 
			
		||||
@@ -425,10 +457,13 @@ def from_response(response, method, url):
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            pass
 | 
			
		||||
        else:
 | 
			
		||||
            if hasattr(body, "keys"):
 | 
			
		||||
                error = body[body.keys()[0]]
 | 
			
		||||
                kwargs["message"] = error.get("message", None)
 | 
			
		||||
                kwargs["details"] = error.get("details", None)
 | 
			
		||||
            if isinstance(body, dict):
 | 
			
		||||
                error = body.get(list(body)[0])
 | 
			
		||||
                if isinstance(error, dict):
 | 
			
		||||
                    kwargs["message"] = (error.get("message") or
 | 
			
		||||
                                         error.get("faultstring"))
 | 
			
		||||
                    kwargs["details"] = (error.get("details") or
 | 
			
		||||
                                         six.text_type(body))
 | 
			
		||||
    elif content_type.startswith("text/"):
 | 
			
		||||
        kwargs["details"] = response.text
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,19 @@ wrong the tests might raise AssertionError. I've indicated in comments the
 | 
			
		||||
places where actual behavior differs from the spec.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
#
 | 
			
		||||
# THIS MODULE IS DEPRECATED
 | 
			
		||||
#
 | 
			
		||||
# Please refer to
 | 
			
		||||
# https://etherpad.openstack.org/p/kilo-ceilometerclient-library-proposals for
 | 
			
		||||
# the discussion leading to this deprecation.
 | 
			
		||||
#
 | 
			
		||||
# We recommend checking out the python-openstacksdk project
 | 
			
		||||
# (https://launchpad.net/python-openstacksdk) instead.
 | 
			
		||||
#
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
# W0102: Dangerous default value %s as argument
 | 
			
		||||
# pylint: disable=W0102
 | 
			
		||||
 | 
			
		||||
@@ -33,7 +46,9 @@ from six.moves.urllib import parse
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def assert_has_keys(dct, required=[], optional=[]):
 | 
			
		||||
def assert_has_keys(dct, required=None, optional=None):
 | 
			
		||||
    required = required or []
 | 
			
		||||
    optional = optional or []
 | 
			
		||||
    for k in required:
 | 
			
		||||
        try:
 | 
			
		||||
            assert k in dct
 | 
			
		||||
@@ -79,7 +94,7 @@ class FakeHTTPClient(client.HTTPClient):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        self.callstack = []
 | 
			
		||||
        self.fixtures = kwargs.pop("fixtures", None) or {}
 | 
			
		||||
        if not args and not "auth_plugin" in kwargs:
 | 
			
		||||
        if not args and "auth_plugin" not in kwargs:
 | 
			
		||||
            args = (None, )
 | 
			
		||||
        super(FakeHTTPClient, self).__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
@@ -166,6 +181,8 @@ class FakeHTTPClient(client.HTTPClient):
 | 
			
		||||
        else:
 | 
			
		||||
            status, body = resp
 | 
			
		||||
            headers = {}
 | 
			
		||||
        self.last_request_id = headers.get('x-openstack-request-id',
 | 
			
		||||
                                           'req-test')
 | 
			
		||||
        return TestResponse({
 | 
			
		||||
            "status_code": status,
 | 
			
		||||
            "text": body,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										100
									
								
								ceilometerclient/openstack/common/apiclient/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								ceilometerclient/openstack/common/apiclient/utils.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
#
 | 
			
		||||
# THIS MODULE IS DEPRECATED
 | 
			
		||||
#
 | 
			
		||||
# Please refer to
 | 
			
		||||
# https://etherpad.openstack.org/p/kilo-ceilometerclient-library-proposals for
 | 
			
		||||
# the discussion leading to this deprecation.
 | 
			
		||||
#
 | 
			
		||||
# We recommend checking out the python-openstacksdk project
 | 
			
		||||
# (https://launchpad.net/python-openstacksdk) instead.
 | 
			
		||||
#
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
from oslo.utils import encodeutils
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common._i18n import _
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import exceptions
 | 
			
		||||
from ceilometerclient.openstack.common import uuidutils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_resource(manager, name_or_id, **find_args):
 | 
			
		||||
    """Look for resource in a given manager.
 | 
			
		||||
 | 
			
		||||
    Used as a helper for the _find_* methods.
 | 
			
		||||
    Example:
 | 
			
		||||
 | 
			
		||||
    .. code-block:: python
 | 
			
		||||
 | 
			
		||||
        def _find_hypervisor(cs, hypervisor):
 | 
			
		||||
            #Get a hypervisor by name or ID.
 | 
			
		||||
            return cliutils.find_resource(cs.hypervisors, hypervisor)
 | 
			
		||||
    """
 | 
			
		||||
    # first try to get entity as integer id
 | 
			
		||||
    try:
 | 
			
		||||
        return manager.get(int(name_or_id))
 | 
			
		||||
    except (TypeError, ValueError, exceptions.NotFound):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    # now try to get entity as uuid
 | 
			
		||||
    try:
 | 
			
		||||
        if six.PY2:
 | 
			
		||||
            tmp_id = encodeutils.safe_encode(name_or_id)
 | 
			
		||||
        else:
 | 
			
		||||
            tmp_id = encodeutils.safe_decode(name_or_id)
 | 
			
		||||
 | 
			
		||||
        if uuidutils.is_uuid_like(tmp_id):
 | 
			
		||||
            return manager.get(tmp_id)
 | 
			
		||||
    except (TypeError, ValueError, exceptions.NotFound):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    # for str id which is not uuid
 | 
			
		||||
    if getattr(manager, 'is_alphanum_id_allowed', False):
 | 
			
		||||
        try:
 | 
			
		||||
            return manager.get(name_or_id)
 | 
			
		||||
        except exceptions.NotFound:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        try:
 | 
			
		||||
            return manager.find(human_id=name_or_id, **find_args)
 | 
			
		||||
        except exceptions.NotFound:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        # finally try to find entity by name
 | 
			
		||||
        try:
 | 
			
		||||
            resource = getattr(manager, 'resource_class', None)
 | 
			
		||||
            name_attr = resource.NAME_ATTR if resource else 'name'
 | 
			
		||||
            kwargs = {name_attr: name_or_id}
 | 
			
		||||
            kwargs.update(find_args)
 | 
			
		||||
            return manager.find(**kwargs)
 | 
			
		||||
        except exceptions.NotFound:
 | 
			
		||||
            msg = _("No %(name)s with a name or "
 | 
			
		||||
                    "ID of '%(name_or_id)s' exists.") % \
 | 
			
		||||
                {
 | 
			
		||||
                    "name": manager.resource_class.__name__.lower(),
 | 
			
		||||
                    "name_or_id": name_or_id
 | 
			
		||||
                }
 | 
			
		||||
            raise exceptions.CommandError(msg)
 | 
			
		||||
    except exceptions.NoUniqueMatch:
 | 
			
		||||
        msg = _("Multiple %(name)s matches found for "
 | 
			
		||||
                "'%(name_or_id)s', use an ID to be more specific.") % \
 | 
			
		||||
            {
 | 
			
		||||
                "name": manager.resource_class.__name__.lower(),
 | 
			
		||||
                "name_or_id": name_or_id
 | 
			
		||||
            }
 | 
			
		||||
        raise exceptions.CommandError(msg)
 | 
			
		||||
@@ -16,18 +16,29 @@
 | 
			
		||||
# W0621: Redefining name %s from outer scope
 | 
			
		||||
# pylint: disable=W0603,W0621
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import getpass
 | 
			
		||||
import inspect
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import textwrap
 | 
			
		||||
 | 
			
		||||
from oslo.utils import encodeutils
 | 
			
		||||
from oslo.utils import strutils
 | 
			
		||||
import prettytable
 | 
			
		||||
import six
 | 
			
		||||
from six import moves
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import exceptions
 | 
			
		||||
from ceilometerclient.openstack.common import strutils
 | 
			
		||||
from ceilometerclient.openstack.common._i18n import _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MissingArgs(Exception):
 | 
			
		||||
    """Supplied arguments are not sufficient for calling a function."""
 | 
			
		||||
    def __init__(self, missing):
 | 
			
		||||
        self.missing = missing
 | 
			
		||||
        msg = _("Missing arguments: %s") % ", ".join(missing)
 | 
			
		||||
        super(MissingArgs, self).__init__(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_args(fn, *args, **kwargs):
 | 
			
		||||
@@ -52,7 +63,7 @@ def validate_args(fn, *args, **kwargs):
 | 
			
		||||
    required_args = argspec.args[:len(argspec.args) - num_defaults]
 | 
			
		||||
 | 
			
		||||
    def isbound(method):
 | 
			
		||||
        return getattr(method, 'im_self', None) is not None
 | 
			
		||||
        return getattr(method, '__self__', None) is not None
 | 
			
		||||
 | 
			
		||||
    if isbound(fn):
 | 
			
		||||
        required_args.pop(0)
 | 
			
		||||
@@ -60,7 +71,7 @@ def validate_args(fn, *args, **kwargs):
 | 
			
		||||
    missing = [arg for arg in required_args if arg not in kwargs]
 | 
			
		||||
    missing = missing[len(args):]
 | 
			
		||||
    if missing:
 | 
			
		||||
        raise exceptions.MissingArgs(missing)
 | 
			
		||||
        raise MissingArgs(missing)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def arg(*args, **kwargs):
 | 
			
		||||
@@ -84,7 +95,7 @@ def env(*args, **kwargs):
 | 
			
		||||
    If all are empty, defaults to '' or keyword arg `default`.
 | 
			
		||||
    """
 | 
			
		||||
    for arg in args:
 | 
			
		||||
        value = os.environ.get(arg, None)
 | 
			
		||||
        value = os.environ.get(arg)
 | 
			
		||||
        if value:
 | 
			
		||||
            return value
 | 
			
		||||
    return kwargs.get('default', '')
 | 
			
		||||
@@ -128,7 +139,7 @@ def isunauthenticated(func):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def print_list(objs, fields, formatters=None, sortby_index=0,
 | 
			
		||||
               mixed_case_fields=None):
 | 
			
		||||
               mixed_case_fields=None, field_labels=None):
 | 
			
		||||
    """Print a list or objects as a table, one row per object.
 | 
			
		||||
 | 
			
		||||
    :param objs: iterable of :class:`Resource`
 | 
			
		||||
@@ -137,14 +148,22 @@ def print_list(objs, fields, formatters=None, sortby_index=0,
 | 
			
		||||
    :param sortby_index: index of the field for sorting table rows
 | 
			
		||||
    :param mixed_case_fields: fields corresponding to object attributes that
 | 
			
		||||
        have mixed case names (e.g., 'serverId')
 | 
			
		||||
    :param field_labels: Labels to use in the heading of the table, default to
 | 
			
		||||
        fields.
 | 
			
		||||
    """
 | 
			
		||||
    formatters = formatters or {}
 | 
			
		||||
    mixed_case_fields = mixed_case_fields or []
 | 
			
		||||
    field_labels = field_labels or fields
 | 
			
		||||
    if len(field_labels) != len(fields):
 | 
			
		||||
        raise ValueError(_("Field labels list %(labels)s has different number "
 | 
			
		||||
                           "of elements than fields list %(fields)s"),
 | 
			
		||||
                         {'labels': field_labels, 'fields': fields})
 | 
			
		||||
 | 
			
		||||
    if sortby_index is None:
 | 
			
		||||
        kwargs = {}
 | 
			
		||||
    else:
 | 
			
		||||
        kwargs = {'sortby': fields[sortby_index]}
 | 
			
		||||
    pt = prettytable.PrettyTable(fields, caching=False)
 | 
			
		||||
        kwargs = {'sortby': field_labels[sortby_index]}
 | 
			
		||||
    pt = prettytable.PrettyTable(field_labels)
 | 
			
		||||
    pt.align = 'l'
 | 
			
		||||
 | 
			
		||||
    for o in objs:
 | 
			
		||||
@@ -161,7 +180,10 @@ def print_list(objs, fields, formatters=None, sortby_index=0,
 | 
			
		||||
                row.append(data)
 | 
			
		||||
        pt.add_row(row)
 | 
			
		||||
 | 
			
		||||
    print(strutils.safe_encode(pt.get_string(**kwargs)))
 | 
			
		||||
    if six.PY3:
 | 
			
		||||
        print(encodeutils.safe_encode(pt.get_string(**kwargs)).decode())
 | 
			
		||||
    else:
 | 
			
		||||
        print(encodeutils.safe_encode(pt.get_string(**kwargs)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def print_dict(dct, dict_property="Property", wrap=0):
 | 
			
		||||
@@ -171,14 +193,14 @@ def print_dict(dct, dict_property="Property", wrap=0):
 | 
			
		||||
    :param dict_property: name of the first column
 | 
			
		||||
    :param wrap: wrapping for the second column
 | 
			
		||||
    """
 | 
			
		||||
    pt = prettytable.PrettyTable([dict_property, 'Value'], caching=False)
 | 
			
		||||
    pt = prettytable.PrettyTable([dict_property, 'Value'])
 | 
			
		||||
    pt.align = 'l'
 | 
			
		||||
    for k, v in six.iteritems(dct):
 | 
			
		||||
        # convert dict to str to check length
 | 
			
		||||
        if isinstance(v, dict):
 | 
			
		||||
            v = str(v)
 | 
			
		||||
            v = six.text_type(v)
 | 
			
		||||
        if wrap > 0:
 | 
			
		||||
            v = textwrap.fill(str(v), wrap)
 | 
			
		||||
            v = textwrap.fill(six.text_type(v), wrap)
 | 
			
		||||
        # if value has a newline, add in multiple rows
 | 
			
		||||
        # e.g. fault with stacktrace
 | 
			
		||||
        if v and isinstance(v, six.string_types) and r'\n' in v:
 | 
			
		||||
@@ -189,7 +211,11 @@ def print_dict(dct, dict_property="Property", wrap=0):
 | 
			
		||||
                col1 = ''
 | 
			
		||||
        else:
 | 
			
		||||
            pt.add_row([k, v])
 | 
			
		||||
    print(strutils.safe_encode(pt.get_string()))
 | 
			
		||||
 | 
			
		||||
    if six.PY3:
 | 
			
		||||
        print(encodeutils.safe_encode(pt.get_string()).decode())
 | 
			
		||||
    else:
 | 
			
		||||
        print(encodeutils.safe_encode(pt.get_string()))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_password(max_password_prompts=3):
 | 
			
		||||
@@ -199,7 +225,7 @@ def get_password(max_password_prompts=3):
 | 
			
		||||
    if hasattr(sys.stdin, "isatty") and sys.stdin.isatty():
 | 
			
		||||
        # Check for Ctrl-D
 | 
			
		||||
        try:
 | 
			
		||||
            for _ in moves.range(max_password_prompts):
 | 
			
		||||
            for __ in moves.range(max_password_prompts):
 | 
			
		||||
                pw1 = getpass.getpass("OS Password: ")
 | 
			
		||||
                if verify:
 | 
			
		||||
                    pw2 = getpass.getpass("Please verify: ")
 | 
			
		||||
@@ -211,3 +237,35 @@ def get_password(max_password_prompts=3):
 | 
			
		||||
        except EOFError:
 | 
			
		||||
            pass
 | 
			
		||||
    return pw
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def service_type(stype):
 | 
			
		||||
    """Adds 'service_type' attribute to decorated function.
 | 
			
		||||
 | 
			
		||||
    Usage:
 | 
			
		||||
 | 
			
		||||
    .. code-block:: python
 | 
			
		||||
 | 
			
		||||
       @service_type('volume')
 | 
			
		||||
       def mymethod(f):
 | 
			
		||||
       ...
 | 
			
		||||
    """
 | 
			
		||||
    def inner(f):
 | 
			
		||||
        f.service_type = stype
 | 
			
		||||
        return f
 | 
			
		||||
    return inner
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_service_type(f):
 | 
			
		||||
    """Retrieves service type from function."""
 | 
			
		||||
    return getattr(f, 'service_type', None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pretty_choice_list(l):
 | 
			
		||||
    return ', '.join("'%s'" % i for i in l)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def exit(msg=''):
 | 
			
		||||
    if msg:
 | 
			
		||||
        print (msg, file=sys.stderr)
 | 
			
		||||
    sys.exit(1)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,371 +0,0 @@
 | 
			
		||||
# Copyright 2012 Red Hat, Inc.
 | 
			
		||||
# Copyright 2013 IBM Corp.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
gettext for openstack-common modules.
 | 
			
		||||
 | 
			
		||||
Usual usage in an openstack.common module:
 | 
			
		||||
 | 
			
		||||
    from ceilometerclient.openstack.common.gettextutils import _
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
import gettext
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
try:
 | 
			
		||||
    import UserString as _userString
 | 
			
		||||
except ImportError:
 | 
			
		||||
    import collections as _userString
 | 
			
		||||
 | 
			
		||||
from babel import localedata
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
_localedir = os.environ.get('ceilometerclient'.upper() + '_LOCALEDIR')
 | 
			
		||||
_t = gettext.translation('ceilometerclient', localedir=_localedir, fallback=True)
 | 
			
		||||
 | 
			
		||||
_AVAILABLE_LANGUAGES = {}
 | 
			
		||||
USE_LAZY = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def enable_lazy():
 | 
			
		||||
    """Convenience function for configuring _() to use lazy gettext
 | 
			
		||||
 | 
			
		||||
    Call this at the start of execution to enable the gettextutils._
 | 
			
		||||
    function to use lazy gettext functionality. This is useful if
 | 
			
		||||
    your project is importing _ directly instead of using the
 | 
			
		||||
    gettextutils.install() way of importing the _ function.
 | 
			
		||||
    """
 | 
			
		||||
    global USE_LAZY
 | 
			
		||||
    USE_LAZY = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _(msg):
 | 
			
		||||
    if USE_LAZY:
 | 
			
		||||
        return Message(msg, 'ceilometerclient')
 | 
			
		||||
    else:
 | 
			
		||||
        if six.PY3:
 | 
			
		||||
            return _t.gettext(msg)
 | 
			
		||||
        return _t.ugettext(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def install(domain, lazy=False):
 | 
			
		||||
    """Install a _() function using the given translation domain.
 | 
			
		||||
 | 
			
		||||
    Given a translation domain, install a _() function using gettext's
 | 
			
		||||
    install() function.
 | 
			
		||||
 | 
			
		||||
    The main difference from gettext.install() is that we allow
 | 
			
		||||
    overriding the default localedir (e.g. /usr/share/locale) using
 | 
			
		||||
    a translation-domain-specific environment variable (e.g.
 | 
			
		||||
    NOVA_LOCALEDIR).
 | 
			
		||||
 | 
			
		||||
    :param domain: the translation domain
 | 
			
		||||
    :param lazy: indicates whether or not to install the lazy _() function.
 | 
			
		||||
                 The lazy _() introduces a way to do deferred translation
 | 
			
		||||
                 of messages by installing a _ that builds Message objects,
 | 
			
		||||
                 instead of strings, which can then be lazily translated into
 | 
			
		||||
                 any available locale.
 | 
			
		||||
    """
 | 
			
		||||
    if lazy:
 | 
			
		||||
        # NOTE(mrodden): Lazy gettext functionality.
 | 
			
		||||
        #
 | 
			
		||||
        # The following introduces a deferred way to do translations on
 | 
			
		||||
        # messages in OpenStack. We override the standard _() function
 | 
			
		||||
        # and % (format string) operation to build Message objects that can
 | 
			
		||||
        # later be translated when we have more information.
 | 
			
		||||
        #
 | 
			
		||||
        # Also included below is an example LocaleHandler that translates
 | 
			
		||||
        # Messages to an associated locale, effectively allowing many logs,
 | 
			
		||||
        # each with their own locale.
 | 
			
		||||
 | 
			
		||||
        def _lazy_gettext(msg):
 | 
			
		||||
            """Create and return a Message object.
 | 
			
		||||
 | 
			
		||||
            Lazy gettext function for a given domain, it is a factory method
 | 
			
		||||
            for a project/module to get a lazy gettext function for its own
 | 
			
		||||
            translation domain (i.e. nova, glance, cinder, etc.)
 | 
			
		||||
 | 
			
		||||
            Message encapsulates a string so that we can translate
 | 
			
		||||
            it later when needed.
 | 
			
		||||
            """
 | 
			
		||||
            return Message(msg, domain)
 | 
			
		||||
 | 
			
		||||
        from six import moves
 | 
			
		||||
        moves.builtins.__dict__['_'] = _lazy_gettext
 | 
			
		||||
    else:
 | 
			
		||||
        localedir = '%s_LOCALEDIR' % domain.upper()
 | 
			
		||||
        if six.PY3:
 | 
			
		||||
            gettext.install(domain,
 | 
			
		||||
                            localedir=os.environ.get(localedir))
 | 
			
		||||
        else:
 | 
			
		||||
            gettext.install(domain,
 | 
			
		||||
                            localedir=os.environ.get(localedir),
 | 
			
		||||
                            unicode=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Message(_userString.UserString, object):
 | 
			
		||||
    """Class used to encapsulate translatable messages."""
 | 
			
		||||
    def __init__(self, msg, domain):
 | 
			
		||||
        # _msg is the gettext msgid and should never change
 | 
			
		||||
        self._msg = msg
 | 
			
		||||
        self._left_extra_msg = ''
 | 
			
		||||
        self._right_extra_msg = ''
 | 
			
		||||
        self._locale = None
 | 
			
		||||
        self.params = None
 | 
			
		||||
        self.domain = domain
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def data(self):
 | 
			
		||||
        # NOTE(mrodden): this should always resolve to a unicode string
 | 
			
		||||
        # that best represents the state of the message currently
 | 
			
		||||
 | 
			
		||||
        localedir = os.environ.get(self.domain.upper() + '_LOCALEDIR')
 | 
			
		||||
        if self.locale:
 | 
			
		||||
            lang = gettext.translation(self.domain,
 | 
			
		||||
                                       localedir=localedir,
 | 
			
		||||
                                       languages=[self.locale],
 | 
			
		||||
                                       fallback=True)
 | 
			
		||||
        else:
 | 
			
		||||
            # use system locale for translations
 | 
			
		||||
            lang = gettext.translation(self.domain,
 | 
			
		||||
                                       localedir=localedir,
 | 
			
		||||
                                       fallback=True)
 | 
			
		||||
 | 
			
		||||
        if six.PY3:
 | 
			
		||||
            ugettext = lang.gettext
 | 
			
		||||
        else:
 | 
			
		||||
            ugettext = lang.ugettext
 | 
			
		||||
 | 
			
		||||
        full_msg = (self._left_extra_msg +
 | 
			
		||||
                    ugettext(self._msg) +
 | 
			
		||||
                    self._right_extra_msg)
 | 
			
		||||
 | 
			
		||||
        if self.params is not None:
 | 
			
		||||
            full_msg = full_msg % self.params
 | 
			
		||||
 | 
			
		||||
        return six.text_type(full_msg)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def locale(self):
 | 
			
		||||
        return self._locale
 | 
			
		||||
 | 
			
		||||
    @locale.setter
 | 
			
		||||
    def locale(self, value):
 | 
			
		||||
        self._locale = value
 | 
			
		||||
        if not self.params:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # This Message object may have been constructed with one or more
 | 
			
		||||
        # Message objects as substitution parameters, given as a single
 | 
			
		||||
        # Message, or a tuple or Map containing some, so when setting the
 | 
			
		||||
        # locale for this Message we need to set it for those Messages too.
 | 
			
		||||
        if isinstance(self.params, Message):
 | 
			
		||||
            self.params.locale = value
 | 
			
		||||
            return
 | 
			
		||||
        if isinstance(self.params, tuple):
 | 
			
		||||
            for param in self.params:
 | 
			
		||||
                if isinstance(param, Message):
 | 
			
		||||
                    param.locale = value
 | 
			
		||||
            return
 | 
			
		||||
        if isinstance(self.params, dict):
 | 
			
		||||
            for param in self.params.values():
 | 
			
		||||
                if isinstance(param, Message):
 | 
			
		||||
                    param.locale = value
 | 
			
		||||
 | 
			
		||||
    def _save_dictionary_parameter(self, dict_param):
 | 
			
		||||
        full_msg = self.data
 | 
			
		||||
        # look for %(blah) fields in string;
 | 
			
		||||
        # ignore %% and deal with the
 | 
			
		||||
        # case where % is first character on the line
 | 
			
		||||
        keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', full_msg)
 | 
			
		||||
 | 
			
		||||
        # if we don't find any %(blah) blocks but have a %s
 | 
			
		||||
        if not keys and re.findall('(?:[^%]|^)%[a-z]', full_msg):
 | 
			
		||||
            # apparently the full dictionary is the parameter
 | 
			
		||||
            params = copy.deepcopy(dict_param)
 | 
			
		||||
        else:
 | 
			
		||||
            params = {}
 | 
			
		||||
            for key in keys:
 | 
			
		||||
                try:
 | 
			
		||||
                    params[key] = copy.deepcopy(dict_param[key])
 | 
			
		||||
                except TypeError:
 | 
			
		||||
                    # cast uncopyable thing to unicode string
 | 
			
		||||
                    params[key] = six.text_type(dict_param[key])
 | 
			
		||||
 | 
			
		||||
        return params
 | 
			
		||||
 | 
			
		||||
    def _save_parameters(self, other):
 | 
			
		||||
        # we check for None later to see if
 | 
			
		||||
        # we actually have parameters to inject,
 | 
			
		||||
        # so encapsulate if our parameter is actually None
 | 
			
		||||
        if other is None:
 | 
			
		||||
            self.params = (other, )
 | 
			
		||||
        elif isinstance(other, dict):
 | 
			
		||||
            self.params = self._save_dictionary_parameter(other)
 | 
			
		||||
        else:
 | 
			
		||||
            # fallback to casting to unicode,
 | 
			
		||||
            # this will handle the problematic python code-like
 | 
			
		||||
            # objects that cannot be deep-copied
 | 
			
		||||
            try:
 | 
			
		||||
                self.params = copy.deepcopy(other)
 | 
			
		||||
            except TypeError:
 | 
			
		||||
                self.params = six.text_type(other)
 | 
			
		||||
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
    # overrides to be more string-like
 | 
			
		||||
    def __unicode__(self):
 | 
			
		||||
        return self.data
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        if six.PY3:
 | 
			
		||||
            return self.__unicode__()
 | 
			
		||||
        return self.data.encode('utf-8')
 | 
			
		||||
 | 
			
		||||
    def __getstate__(self):
 | 
			
		||||
        to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg',
 | 
			
		||||
                   'domain', 'params', '_locale']
 | 
			
		||||
        new_dict = self.__dict__.fromkeys(to_copy)
 | 
			
		||||
        for attr in to_copy:
 | 
			
		||||
            new_dict[attr] = copy.deepcopy(self.__dict__[attr])
 | 
			
		||||
 | 
			
		||||
        return new_dict
 | 
			
		||||
 | 
			
		||||
    def __setstate__(self, state):
 | 
			
		||||
        for (k, v) in state.items():
 | 
			
		||||
            setattr(self, k, v)
 | 
			
		||||
 | 
			
		||||
    # operator overloads
 | 
			
		||||
    def __add__(self, other):
 | 
			
		||||
        copied = copy.deepcopy(self)
 | 
			
		||||
        copied._right_extra_msg += other.__str__()
 | 
			
		||||
        return copied
 | 
			
		||||
 | 
			
		||||
    def __radd__(self, other):
 | 
			
		||||
        copied = copy.deepcopy(self)
 | 
			
		||||
        copied._left_extra_msg += other.__str__()
 | 
			
		||||
        return copied
 | 
			
		||||
 | 
			
		||||
    def __mod__(self, other):
 | 
			
		||||
        # do a format string to catch and raise
 | 
			
		||||
        # any possible KeyErrors from missing parameters
 | 
			
		||||
        self.data % other
 | 
			
		||||
        copied = copy.deepcopy(self)
 | 
			
		||||
        return copied._save_parameters(other)
 | 
			
		||||
 | 
			
		||||
    def __mul__(self, other):
 | 
			
		||||
        return self.data * other
 | 
			
		||||
 | 
			
		||||
    def __rmul__(self, other):
 | 
			
		||||
        return other * self.data
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, key):
 | 
			
		||||
        return self.data[key]
 | 
			
		||||
 | 
			
		||||
    def __getslice__(self, start, end):
 | 
			
		||||
        return self.data.__getslice__(start, end)
 | 
			
		||||
 | 
			
		||||
    def __getattribute__(self, name):
 | 
			
		||||
        # NOTE(mrodden): handle lossy operations that we can't deal with yet
 | 
			
		||||
        # These override the UserString implementation, since UserString
 | 
			
		||||
        # uses our __class__ attribute to try and build a new message
 | 
			
		||||
        # after running the inner data string through the operation.
 | 
			
		||||
        # At that point, we have lost the gettext message id and can just
 | 
			
		||||
        # safely resolve to a string instead.
 | 
			
		||||
        ops = ['capitalize', 'center', 'decode', 'encode',
 | 
			
		||||
               'expandtabs', 'ljust', 'lstrip', 'replace', 'rjust', 'rstrip',
 | 
			
		||||
               'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
 | 
			
		||||
        if name in ops:
 | 
			
		||||
            return getattr(self.data, name)
 | 
			
		||||
        else:
 | 
			
		||||
            return _userString.UserString.__getattribute__(self, name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_available_languages(domain):
 | 
			
		||||
    """Lists the available languages for the given translation domain.
 | 
			
		||||
 | 
			
		||||
    :param domain: the domain to get languages for
 | 
			
		||||
    """
 | 
			
		||||
    if domain in _AVAILABLE_LANGUAGES:
 | 
			
		||||
        return copy.copy(_AVAILABLE_LANGUAGES[domain])
 | 
			
		||||
 | 
			
		||||
    localedir = '%s_LOCALEDIR' % domain.upper()
 | 
			
		||||
    find = lambda x: gettext.find(domain,
 | 
			
		||||
                                  localedir=os.environ.get(localedir),
 | 
			
		||||
                                  languages=[x])
 | 
			
		||||
 | 
			
		||||
    # NOTE(mrodden): en_US should always be available (and first in case
 | 
			
		||||
    # order matters) since our in-line message strings are en_US
 | 
			
		||||
    language_list = ['en_US']
 | 
			
		||||
    # NOTE(luisg): Babel <1.0 used a function called list(), which was
 | 
			
		||||
    # renamed to locale_identifiers() in >=1.0, the requirements master list
 | 
			
		||||
    # requires >=0.9.6, uncapped, so defensively work with both. We can remove
 | 
			
		||||
    # this check when the master list updates to >=1.0, and update all projects
 | 
			
		||||
    list_identifiers = (getattr(localedata, 'list', None) or
 | 
			
		||||
                        getattr(localedata, 'locale_identifiers'))
 | 
			
		||||
    locale_identifiers = list_identifiers()
 | 
			
		||||
    for i in locale_identifiers:
 | 
			
		||||
        if find(i) is not None:
 | 
			
		||||
            language_list.append(i)
 | 
			
		||||
    _AVAILABLE_LANGUAGES[domain] = language_list
 | 
			
		||||
    return copy.copy(language_list)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_localized_message(message, user_locale):
 | 
			
		||||
    """Gets a localized version of the given message in the given locale.
 | 
			
		||||
 | 
			
		||||
    If the message is not a Message object the message is returned as-is.
 | 
			
		||||
    If the locale is None the message is translated to the default locale.
 | 
			
		||||
 | 
			
		||||
    :returns: the translated message in unicode, or the original message if
 | 
			
		||||
              it could not be translated
 | 
			
		||||
    """
 | 
			
		||||
    translated = message
 | 
			
		||||
    if isinstance(message, Message):
 | 
			
		||||
        original_locale = message.locale
 | 
			
		||||
        message.locale = user_locale
 | 
			
		||||
        translated = six.text_type(message)
 | 
			
		||||
        message.locale = original_locale
 | 
			
		||||
    return translated
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LocaleHandler(logging.Handler):
 | 
			
		||||
    """Handler that can have a locale associated to translate Messages.
 | 
			
		||||
 | 
			
		||||
    A quick example of how to utilize the Message class above.
 | 
			
		||||
    LocaleHandler takes a locale and a target logging.Handler object
 | 
			
		||||
    to forward LogRecord objects to after translating the internal Message.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, locale, target):
 | 
			
		||||
        """Initialize a LocaleHandler
 | 
			
		||||
 | 
			
		||||
        :param locale: locale to use for translating messages
 | 
			
		||||
        :param target: logging.Handler object to forward
 | 
			
		||||
                       LogRecord objects to after translation
 | 
			
		||||
        """
 | 
			
		||||
        logging.Handler.__init__(self)
 | 
			
		||||
        self.locale = locale
 | 
			
		||||
        self.target = target
 | 
			
		||||
 | 
			
		||||
    def emit(self, record):
 | 
			
		||||
        if isinstance(record.msg, Message):
 | 
			
		||||
            # set the locale and resolve to a string
 | 
			
		||||
            record.msg.locale = self.locale
 | 
			
		||||
 | 
			
		||||
        self.target.emit(record)
 | 
			
		||||
@@ -1,66 +0,0 @@
 | 
			
		||||
# Copyright 2011 OpenStack Foundation.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Import related utilities and helper functions.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import traceback
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def import_class(import_str):
 | 
			
		||||
    """Returns a class from a string including module and class."""
 | 
			
		||||
    mod_str, _sep, class_str = import_str.rpartition('.')
 | 
			
		||||
    try:
 | 
			
		||||
        __import__(mod_str)
 | 
			
		||||
        return getattr(sys.modules[mod_str], class_str)
 | 
			
		||||
    except (ValueError, AttributeError):
 | 
			
		||||
        raise ImportError('Class %s cannot be found (%s)' %
 | 
			
		||||
                          (class_str,
 | 
			
		||||
                           traceback.format_exception(*sys.exc_info())))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def import_object(import_str, *args, **kwargs):
 | 
			
		||||
    """Import a class and return an instance of it."""
 | 
			
		||||
    return import_class(import_str)(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def import_object_ns(name_space, import_str, *args, **kwargs):
 | 
			
		||||
    """Tries to import object from default namespace.
 | 
			
		||||
 | 
			
		||||
    Imports a class and return an instance of it, first by trying
 | 
			
		||||
    to find the class in a default namespace, then failing back to
 | 
			
		||||
    a full path if not found in the default namespace.
 | 
			
		||||
    """
 | 
			
		||||
    import_value = "%s.%s" % (name_space, import_str)
 | 
			
		||||
    try:
 | 
			
		||||
        return import_class(import_value)(*args, **kwargs)
 | 
			
		||||
    except ImportError:
 | 
			
		||||
        return import_class(import_str)(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def import_module(import_str):
 | 
			
		||||
    """Import a module."""
 | 
			
		||||
    __import__(import_str)
 | 
			
		||||
    return sys.modules[import_str]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def try_import(import_str, default=None):
 | 
			
		||||
    """Try to import a module and if it fails return default."""
 | 
			
		||||
    try:
 | 
			
		||||
        return import_module(import_str)
 | 
			
		||||
    except ImportError:
 | 
			
		||||
        return default
 | 
			
		||||
@@ -1,222 +0,0 @@
 | 
			
		||||
# Copyright 2011 OpenStack Foundation.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
System-level utilities and helper functions.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
import sys
 | 
			
		||||
import unicodedata
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.gettextutils import _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Used for looking up extensions of text
 | 
			
		||||
# to their 'multiplied' byte amount
 | 
			
		||||
BYTE_MULTIPLIERS = {
 | 
			
		||||
    '': 1,
 | 
			
		||||
    't': 1024 ** 4,
 | 
			
		||||
    'g': 1024 ** 3,
 | 
			
		||||
    'm': 1024 ** 2,
 | 
			
		||||
    'k': 1024,
 | 
			
		||||
}
 | 
			
		||||
BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)')
 | 
			
		||||
 | 
			
		||||
TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
 | 
			
		||||
FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
 | 
			
		||||
 | 
			
		||||
SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
 | 
			
		||||
SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def int_from_bool_as_string(subject):
 | 
			
		||||
    """Interpret a string as a boolean and return either 1 or 0.
 | 
			
		||||
 | 
			
		||||
    Any string value in:
 | 
			
		||||
 | 
			
		||||
        ('True', 'true', 'On', 'on', '1')
 | 
			
		||||
 | 
			
		||||
    is interpreted as a boolean True.
 | 
			
		||||
 | 
			
		||||
    Useful for JSON-decoded stuff and config file parsing
 | 
			
		||||
    """
 | 
			
		||||
    return bool_from_string(subject) and 1 or 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def bool_from_string(subject, strict=False):
 | 
			
		||||
    """Interpret a string as a boolean.
 | 
			
		||||
 | 
			
		||||
    A case-insensitive match is performed such that strings matching 't',
 | 
			
		||||
    'true', 'on', 'y', 'yes', or '1' are considered True and, when
 | 
			
		||||
    `strict=False`, anything else is considered False.
 | 
			
		||||
 | 
			
		||||
    Useful for JSON-decoded stuff and config file parsing.
 | 
			
		||||
 | 
			
		||||
    If `strict=True`, unrecognized values, including None, will raise a
 | 
			
		||||
    ValueError which is useful when parsing values passed in from an API call.
 | 
			
		||||
    Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
 | 
			
		||||
    """
 | 
			
		||||
    if not isinstance(subject, six.string_types):
 | 
			
		||||
        subject = str(subject)
 | 
			
		||||
 | 
			
		||||
    lowered = subject.strip().lower()
 | 
			
		||||
 | 
			
		||||
    if lowered in TRUE_STRINGS:
 | 
			
		||||
        return True
 | 
			
		||||
    elif lowered in FALSE_STRINGS:
 | 
			
		||||
        return False
 | 
			
		||||
    elif strict:
 | 
			
		||||
        acceptable = ', '.join(
 | 
			
		||||
            "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
 | 
			
		||||
        msg = _("Unrecognized value '%(val)s', acceptable values are:"
 | 
			
		||||
                " %(acceptable)s") % {'val': subject,
 | 
			
		||||
                                      'acceptable': acceptable}
 | 
			
		||||
        raise ValueError(msg)
 | 
			
		||||
    else:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def safe_decode(text, incoming=None, errors='strict'):
 | 
			
		||||
    """Decodes incoming str using `incoming` if they're not already unicode.
 | 
			
		||||
 | 
			
		||||
    :param incoming: Text's current encoding
 | 
			
		||||
    :param errors: Errors handling policy. See here for valid
 | 
			
		||||
        values http://docs.python.org/2/library/codecs.html
 | 
			
		||||
    :returns: text or a unicode `incoming` encoded
 | 
			
		||||
                representation of it.
 | 
			
		||||
    :raises TypeError: If text is not an instance of str
 | 
			
		||||
    """
 | 
			
		||||
    if not isinstance(text, six.string_types):
 | 
			
		||||
        raise TypeError("%s can't be decoded" % type(text))
 | 
			
		||||
 | 
			
		||||
    if isinstance(text, six.text_type):
 | 
			
		||||
        return text
 | 
			
		||||
 | 
			
		||||
    if not incoming:
 | 
			
		||||
        incoming = (sys.stdin.encoding or
 | 
			
		||||
                    sys.getdefaultencoding())
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        return text.decode(incoming, errors)
 | 
			
		||||
    except UnicodeDecodeError:
 | 
			
		||||
        # Note(flaper87) If we get here, it means that
 | 
			
		||||
        # sys.stdin.encoding / sys.getdefaultencoding
 | 
			
		||||
        # didn't return a suitable encoding to decode
 | 
			
		||||
        # text. This happens mostly when global LANG
 | 
			
		||||
        # var is not set correctly and there's no
 | 
			
		||||
        # default encoding. In this case, most likely
 | 
			
		||||
        # python will use ASCII or ANSI encoders as
 | 
			
		||||
        # default encodings but they won't be capable
 | 
			
		||||
        # of decoding non-ASCII characters.
 | 
			
		||||
        #
 | 
			
		||||
        # Also, UTF-8 is being used since it's an ASCII
 | 
			
		||||
        # extension.
 | 
			
		||||
        return text.decode('utf-8', errors)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def safe_encode(text, incoming=None,
 | 
			
		||||
                encoding='utf-8', errors='strict'):
 | 
			
		||||
    """Encodes incoming str/unicode using `encoding`.
 | 
			
		||||
 | 
			
		||||
    If incoming is not specified, text is expected to be encoded with
 | 
			
		||||
    current python's default encoding. (`sys.getdefaultencoding`)
 | 
			
		||||
 | 
			
		||||
    :param incoming: Text's current encoding
 | 
			
		||||
    :param encoding: Expected encoding for text (Default UTF-8)
 | 
			
		||||
    :param errors: Errors handling policy. See here for valid
 | 
			
		||||
        values http://docs.python.org/2/library/codecs.html
 | 
			
		||||
    :returns: text or a bytestring `encoding` encoded
 | 
			
		||||
                representation of it.
 | 
			
		||||
    :raises TypeError: If text is not an instance of str
 | 
			
		||||
    """
 | 
			
		||||
    if not isinstance(text, six.string_types):
 | 
			
		||||
        raise TypeError("%s can't be encoded" % type(text))
 | 
			
		||||
 | 
			
		||||
    if not incoming:
 | 
			
		||||
        incoming = (sys.stdin.encoding or
 | 
			
		||||
                    sys.getdefaultencoding())
 | 
			
		||||
 | 
			
		||||
    if isinstance(text, six.text_type):
 | 
			
		||||
        if six.PY3:
 | 
			
		||||
            return text.encode(encoding, errors).decode(incoming)
 | 
			
		||||
        else:
 | 
			
		||||
            return text.encode(encoding, errors)
 | 
			
		||||
    elif text and encoding != incoming:
 | 
			
		||||
        # Decode text before encoding it with `encoding`
 | 
			
		||||
        text = safe_decode(text, incoming, errors)
 | 
			
		||||
        if six.PY3:
 | 
			
		||||
            return text.encode(encoding, errors).decode(incoming)
 | 
			
		||||
        else:
 | 
			
		||||
            return text.encode(encoding, errors)
 | 
			
		||||
 | 
			
		||||
    return text
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_bytes(text, default=0):
 | 
			
		||||
    """Converts a string into an integer of bytes.
 | 
			
		||||
 | 
			
		||||
    Looks at the last characters of the text to determine
 | 
			
		||||
    what conversion is needed to turn the input text into a byte number.
 | 
			
		||||
    Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive)
 | 
			
		||||
 | 
			
		||||
    :param text: String input for bytes size conversion.
 | 
			
		||||
    :param default: Default return value when text is blank.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    match = BYTE_REGEX.search(text)
 | 
			
		||||
    if match:
 | 
			
		||||
        magnitude = int(match.group(1))
 | 
			
		||||
        mult_key_org = match.group(2)
 | 
			
		||||
        if not mult_key_org:
 | 
			
		||||
            return magnitude
 | 
			
		||||
    elif text:
 | 
			
		||||
        msg = _('Invalid string format: %s') % text
 | 
			
		||||
        raise TypeError(msg)
 | 
			
		||||
    else:
 | 
			
		||||
        return default
 | 
			
		||||
    mult_key = mult_key_org.lower().replace('b', '', 1)
 | 
			
		||||
    multiplier = BYTE_MULTIPLIERS.get(mult_key)
 | 
			
		||||
    if multiplier is None:
 | 
			
		||||
        msg = _('Unknown byte multiplier: %s') % mult_key_org
 | 
			
		||||
        raise TypeError(msg)
 | 
			
		||||
    return magnitude * multiplier
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_slug(value, incoming=None, errors="strict"):
 | 
			
		||||
    """Normalize string.
 | 
			
		||||
 | 
			
		||||
    Convert to lowercase, remove non-word characters, and convert spaces
 | 
			
		||||
    to hyphens.
 | 
			
		||||
 | 
			
		||||
    Inspired by Django's `slugify` filter.
 | 
			
		||||
 | 
			
		||||
    :param value: Text to slugify
 | 
			
		||||
    :param incoming: Text's current encoding
 | 
			
		||||
    :param errors: Errors handling policy. See here for valid
 | 
			
		||||
        values http://docs.python.org/2/library/codecs.html
 | 
			
		||||
    :returns: slugified unicode representation of `value`
 | 
			
		||||
    :raises TypeError: If text is not an instance of str
 | 
			
		||||
    """
 | 
			
		||||
    value = safe_decode(value, incoming, errors)
 | 
			
		||||
    # NOTE(aababilov): no need to use safe_(encode|decode) here:
 | 
			
		||||
    # encodings are always "ascii", error handling is always "ignore"
 | 
			
		||||
    # and types are always known (first: unicode; second: str)
 | 
			
		||||
    value = unicodedata.normalize("NFKD", value).encode(
 | 
			
		||||
        "ascii", "ignore").decode("ascii")
 | 
			
		||||
    value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
 | 
			
		||||
    return SLUGIFY_HYPHENATE_RE.sub("-", value)
 | 
			
		||||
							
								
								
									
										37
									
								
								ceilometerclient/openstack/common/uuidutils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								ceilometerclient/openstack/common/uuidutils.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
# Copyright (c) 2012 Intel Corporation.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
UUID related utilities and helper functions.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import uuid
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_uuid():
 | 
			
		||||
    return str(uuid.uuid4())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_uuid_like(val):
 | 
			
		||||
    """Returns validation of a value as a UUID.
 | 
			
		||||
 | 
			
		||||
    For our purposes, a UUID is a canonical form string:
 | 
			
		||||
    aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        return str(uuid.UUID(val)) == val
 | 
			
		||||
    except (TypeError, ValueError, AttributeError):
 | 
			
		||||
        return False
 | 
			
		||||
@@ -20,6 +20,7 @@ import argparse
 | 
			
		||||
import logging
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from oslo.utils import encodeutils
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
import ceilometerclient
 | 
			
		||||
@@ -27,7 +28,20 @@ from ceilometerclient import client as ceiloclient
 | 
			
		||||
from ceilometerclient.common import utils
 | 
			
		||||
from ceilometerclient import exc
 | 
			
		||||
from ceilometerclient.openstack.common import cliutils
 | 
			
		||||
from ceilometerclient.openstack.common import strutils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _positive_non_zero_int(argument_value):
 | 
			
		||||
    if argument_value is None:
 | 
			
		||||
        return None
 | 
			
		||||
    try:
 | 
			
		||||
        value = int(argument_value)
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        msg = "%s must be an integer" % argument_value
 | 
			
		||||
        raise argparse.ArgumentTypeError(msg)
 | 
			
		||||
    if value <= 0:
 | 
			
		||||
        msg = "%s must be greater than 0" % argument_value
 | 
			
		||||
        raise argparse.ArgumentTypeError(msg)
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CeilometerShell(object):
 | 
			
		||||
@@ -62,96 +76,19 @@ class CeilometerShell(object):
 | 
			
		||||
                            default=False, action="store_true",
 | 
			
		||||
                            help="Print more verbose output.")
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('-k', '--insecure',
 | 
			
		||||
                            default=False,
 | 
			
		||||
                            action='store_true',
 | 
			
		||||
                            help="Explicitly allow ceilometerclient to "
 | 
			
		||||
                            "perform \"insecure\" SSL (https) requests. "
 | 
			
		||||
                            "The server's certificate will "
 | 
			
		||||
                            "not be verified against any certificate "
 | 
			
		||||
                            "authorities. This option should be used with "
 | 
			
		||||
                            "caution.")
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--cert-file',
 | 
			
		||||
                            help='Path of certificate file to use in SSL '
 | 
			
		||||
                            'connection. This file can optionally be prepended'
 | 
			
		||||
                            ' with the private key.')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--key-file',
 | 
			
		||||
                            help='Path of client key to use in SSL connection.'
 | 
			
		||||
                            ' This option is not necessary if your key is '
 | 
			
		||||
                            'prepended to your cert file.')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os-cacert',
 | 
			
		||||
                            metavar='<ca-certificate-file>',
 | 
			
		||||
                            dest='os_cacert',
 | 
			
		||||
                            default=cliutils.env('OS_CACERT'),
 | 
			
		||||
                            help='Path of CA TLS certificate(s) used to verify'
 | 
			
		||||
                            'the remote server\'s certificate. Without this '
 | 
			
		||||
                            'option ceilometer looks for the default system '
 | 
			
		||||
                            'CA certificates.')
 | 
			
		||||
        parser.add_argument('--ca-file',
 | 
			
		||||
                            dest='os_cacert',
 | 
			
		||||
                            help='DEPRECATED! Use --os-cacert.')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--timeout',
 | 
			
		||||
                            default=600,
 | 
			
		||||
                            type=_positive_non_zero_int,
 | 
			
		||||
                            help='Number of seconds to wait for a response.')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os-username',
 | 
			
		||||
                            default=cliutils.env('OS_USERNAME'),
 | 
			
		||||
                            help='Defaults to env[OS_USERNAME].')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os_username',
 | 
			
		||||
                            help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os-password',
 | 
			
		||||
                            default=cliutils.env('OS_PASSWORD'),
 | 
			
		||||
                            help='Defaults to env[OS_PASSWORD].')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os_password',
 | 
			
		||||
                            help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os-tenant-id',
 | 
			
		||||
                            default=cliutils.env('OS_TENANT_ID'),
 | 
			
		||||
                            help='Defaults to env[OS_TENANT_ID].')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os_tenant_id',
 | 
			
		||||
                            help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os-tenant-name',
 | 
			
		||||
                            default=cliutils.env('OS_TENANT_NAME'),
 | 
			
		||||
                            help='Defaults to env[OS_TENANT_NAME].')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os_tenant_name',
 | 
			
		||||
                            help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os-auth-url',
 | 
			
		||||
                            default=cliutils.env('OS_AUTH_URL'),
 | 
			
		||||
                            help='Defaults to env[OS_AUTH_URL].')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os_auth_url',
 | 
			
		||||
                            help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os-region-name',
 | 
			
		||||
                            default=cliutils.env('OS_REGION_NAME'),
 | 
			
		||||
                            help='Defaults to env[OS_REGION_NAME].')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os_region_name',
 | 
			
		||||
                            help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os-auth-token',
 | 
			
		||||
                            default=cliutils.env('OS_AUTH_TOKEN'),
 | 
			
		||||
                            help='Defaults to env[OS_AUTH_TOKEN].')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os_auth_token',
 | 
			
		||||
                            help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--ceilometer-url',
 | 
			
		||||
        parser.add_argument('--ceilometer-url', metavar='<CEILOMETER_URL>',
 | 
			
		||||
                            dest='os_endpoint',
 | 
			
		||||
                            default=cliutils.env('CEILOMETER_URL'),
 | 
			
		||||
                            help='Defaults to env[CEILOMETER_URL].')
 | 
			
		||||
                            help=("DEPRECATED, use --os-endpoint instead. "
 | 
			
		||||
                                  "Defaults to env[CEILOMETER_URL]."))
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--ceilometer_url',
 | 
			
		||||
                            dest='os_endpoint',
 | 
			
		||||
                            help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--ceilometer-api-version',
 | 
			
		||||
@@ -163,19 +100,8 @@ class CeilometerShell(object):
 | 
			
		||||
        parser.add_argument('--ceilometer_api_version',
 | 
			
		||||
                            help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os-service-type',
 | 
			
		||||
                            default=cliutils.env('OS_SERVICE_TYPE'),
 | 
			
		||||
                            help='Defaults to env[OS_SERVICE_TYPE].')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os_service_type',
 | 
			
		||||
                            help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os-endpoint-type',
 | 
			
		||||
                            default=cliutils.env('OS_ENDPOINT_TYPE'),
 | 
			
		||||
                            help='Defaults to env[OS_ENDPOINT_TYPE].')
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os_endpoint_type',
 | 
			
		||||
                            help=argparse.SUPPRESS)
 | 
			
		||||
        self.auth_plugin.add_opts(parser)
 | 
			
		||||
        self.auth_plugin.add_common_opts(parser)
 | 
			
		||||
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
@@ -220,17 +146,22 @@ class CeilometerShell(object):
 | 
			
		||||
                subparser.add_argument(*args, **kwargs)
 | 
			
		||||
            subparser.set_defaults(func=callback)
 | 
			
		||||
 | 
			
		||||
    def _setup_logging(self, debug):
 | 
			
		||||
        format = '%(levelname)s (%(module)s:%(lineno)d) %(message)s'
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _setup_logging(debug):
 | 
			
		||||
        format = '%(levelname)s (%(module)s) %(message)s'
 | 
			
		||||
        if debug:
 | 
			
		||||
            logging.basicConfig(format=format, level=logging.DEBUG)
 | 
			
		||||
        else:
 | 
			
		||||
            logging.basicConfig(format=format, level=logging.WARN)
 | 
			
		||||
        logging.getLogger('iso8601').setLevel(logging.WARNING)
 | 
			
		||||
        logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING)
 | 
			
		||||
 | 
			
		||||
    def parse_args(self, argv):
 | 
			
		||||
        # Parse args once to find version
 | 
			
		||||
        self.auth_plugin = ceiloclient.AuthPlugin()
 | 
			
		||||
        parser = self.get_base_parser()
 | 
			
		||||
        (options, args) = parser.parse_known_args(argv)
 | 
			
		||||
        self.auth_plugin.parse_opts(options)
 | 
			
		||||
        self._setup_logging(options.debug)
 | 
			
		||||
 | 
			
		||||
        # build available subcommands based on version
 | 
			
		||||
@@ -247,6 +178,15 @@ class CeilometerShell(object):
 | 
			
		||||
        # Return parsed args
 | 
			
		||||
        return api_version, subcommand_parser.parse_args(argv)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def no_project_and_domain_set(args):
 | 
			
		||||
        if not (args.os_project_id or (args.os_project_name and
 | 
			
		||||
                (args.os_user_domain_name or args.os_user_domain_id)) or
 | 
			
		||||
                (args.os_tenant_id or args.os_tenant_name)):
 | 
			
		||||
            return True
 | 
			
		||||
        else:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def main(self, argv):
 | 
			
		||||
        parsed = self.parse_args(argv)
 | 
			
		||||
        if parsed == 0:
 | 
			
		||||
@@ -261,33 +201,50 @@ class CeilometerShell(object):
 | 
			
		||||
            self.do_bash_completion(args)
 | 
			
		||||
            return 0
 | 
			
		||||
 | 
			
		||||
        if not (args.os_auth_token and args.ceilometer_url):
 | 
			
		||||
            if not args.os_username:
 | 
			
		||||
        if not ((self.auth_plugin.opts.get('token')
 | 
			
		||||
                 or self.auth_plugin.opts.get('auth_token'))
 | 
			
		||||
                and self.auth_plugin.opts['endpoint']):
 | 
			
		||||
            if not self.auth_plugin.opts['username']:
 | 
			
		||||
                raise exc.CommandError("You must provide a username via "
 | 
			
		||||
                                       "either --os-username or via "
 | 
			
		||||
                                       "env[OS_USERNAME]")
 | 
			
		||||
 | 
			
		||||
            if not args.os_password:
 | 
			
		||||
            if not self.auth_plugin.opts['password']:
 | 
			
		||||
                raise exc.CommandError("You must provide a password via "
 | 
			
		||||
                                       "either --os-password or via "
 | 
			
		||||
                                       "env[OS_PASSWORD]")
 | 
			
		||||
 | 
			
		||||
            if not (args.os_tenant_id or args.os_tenant_name):
 | 
			
		||||
            if self.no_project_and_domain_set(args):
 | 
			
		||||
                # steer users towards Keystone V3 API
 | 
			
		||||
                raise exc.CommandError("You must provide a project_id via "
 | 
			
		||||
                                       "either --os-project-id or via "
 | 
			
		||||
                                       "env[OS_PROJECT_ID] and "
 | 
			
		||||
                                       "a domain_name via either "
 | 
			
		||||
                                       "--os-user-domain-name or via "
 | 
			
		||||
                                       "env[OS_USER_DOMAIN_NAME] or "
 | 
			
		||||
                                       "a domain_id via either "
 | 
			
		||||
                                       "--os-user-domain-id or via "
 | 
			
		||||
                                       "env[OS_USER_DOMAIN_ID]")
 | 
			
		||||
 | 
			
		||||
            if not (self.auth_plugin.opts['tenant_id']
 | 
			
		||||
                    or self.auth_plugin.opts['tenant_name']):
 | 
			
		||||
                raise exc.CommandError("You must provide a tenant_id via "
 | 
			
		||||
                                       "either --os-tenant-id or via "
 | 
			
		||||
                                       "env[OS_TENANT_ID]")
 | 
			
		||||
 | 
			
		||||
            if not args.os_auth_url:
 | 
			
		||||
            if not self.auth_plugin.opts['auth_url']:
 | 
			
		||||
                raise exc.CommandError("You must provide an auth url via "
 | 
			
		||||
                                       "either --os-auth-url or via "
 | 
			
		||||
                                       "env[OS_AUTH_URL]")
 | 
			
		||||
 | 
			
		||||
        client = ceiloclient.get_client(api_version, **(args.__dict__))
 | 
			
		||||
 | 
			
		||||
        client_kwargs = vars(args)
 | 
			
		||||
        client_kwargs.update(self.auth_plugin.opts)
 | 
			
		||||
        client_kwargs['auth_plugin'] = self.auth_plugin
 | 
			
		||||
        client = ceiloclient.get_client(api_version, **client_kwargs)
 | 
			
		||||
        # call whatever callback was selected
 | 
			
		||||
        try:
 | 
			
		||||
            args.func(client, args)
 | 
			
		||||
        except exc.Unauthorized:
 | 
			
		||||
        except exc.HTTPUnauthorized:
 | 
			
		||||
            raise exc.CommandError("Invalid OpenStack Identity credentials.")
 | 
			
		||||
 | 
			
		||||
    def do_bash_completion(self, args):
 | 
			
		||||
@@ -321,6 +278,11 @@ class CeilometerShell(object):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HelpFormatter(argparse.HelpFormatter):
 | 
			
		||||
    def __init__(self, prog, indent_increment=2, max_help_position=32,
 | 
			
		||||
                 width=None):
 | 
			
		||||
        super(HelpFormatter, self).__init__(prog, indent_increment,
 | 
			
		||||
                                            max_help_position, width)
 | 
			
		||||
 | 
			
		||||
    def start_section(self, heading):
 | 
			
		||||
        # Title-case the headings
 | 
			
		||||
        heading = '%s%s' % (heading[0].upper(), heading[1:])
 | 
			
		||||
@@ -338,8 +300,11 @@ def main(args=None):
 | 
			
		||||
        if '--debug' in args or '-d' in args:
 | 
			
		||||
            raise
 | 
			
		||||
        else:
 | 
			
		||||
            print(strutils.safe_encode(six.text_type(e)), file=sys.stderr)
 | 
			
		||||
            print(encodeutils.safe_encode(six.text_type(e)), file=sys.stderr)
 | 
			
		||||
        sys.exit(1)
 | 
			
		||||
    except KeyboardInterrupt:
 | 
			
		||||
        print("Stopping Ceilometer Client", file=sys.stderr)
 | 
			
		||||
        sys.exit(130)
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    main()
 | 
			
		||||
 
 | 
			
		||||
@@ -29,19 +29,20 @@ def fake_headers():
 | 
			
		||||
            'User-Agent': 'python-ceilometerclient'}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeServiceCatalog():
 | 
			
		||||
    def url_for(self, endpoint_type, service_type):
 | 
			
		||||
class FakeServiceCatalog(object):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def url_for(endpoint_type, service_type):
 | 
			
		||||
        return 'http://192.168.1.5:8004/v1/f14b41234'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeKeystone():
 | 
			
		||||
class FakeKeystone(object):
 | 
			
		||||
    service_catalog = FakeServiceCatalog()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, auth_token):
 | 
			
		||||
        self.auth_token = auth_token
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeHTTPResponse():
 | 
			
		||||
class FakeHTTPResponse(object):
 | 
			
		||||
 | 
			
		||||
    version = 1.1
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,44 +12,140 @@
 | 
			
		||||
 | 
			
		||||
import types
 | 
			
		||||
 | 
			
		||||
import mock
 | 
			
		||||
 | 
			
		||||
from ceilometerclient import client
 | 
			
		||||
from ceilometerclient.tests import fakes
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
from ceilometerclient.v1 import client as v1client
 | 
			
		||||
from ceilometerclient.v2 import client as v2client
 | 
			
		||||
 | 
			
		||||
FAKE_ENV = {'os_username': 'username',
 | 
			
		||||
            'os_password': 'password',
 | 
			
		||||
            'os_tenant_name': 'tenant_name',
 | 
			
		||||
            'os_auth_url': 'http://no.where',
 | 
			
		||||
            'os_auth_token': '1234',
 | 
			
		||||
            'ceilometer_url': 'http://no.where'}
 | 
			
		||||
FAKE_ENV = {
 | 
			
		||||
    'username': 'username',
 | 
			
		||||
    'password': 'password',
 | 
			
		||||
    'tenant_name': 'tenant_name',
 | 
			
		||||
    'auth_url': 'http://no.where',
 | 
			
		||||
    'ceilometer_url': 'http://no.where',
 | 
			
		||||
    'auth_plugin': 'fake_auth',
 | 
			
		||||
    'token': '1234',
 | 
			
		||||
    'user_domain_name': 'default',
 | 
			
		||||
    'project_domain_name': 'default',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ClientTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def create_client(self, api_version=2, exclude=[]):
 | 
			
		||||
        env = dict((k, v) for k, v in FAKE_ENV.items() if k not in exclude)
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def create_client(env, api_version=2, endpoint=None, exclude=[]):
 | 
			
		||||
        env = dict((k, v) for k, v in env.items()
 | 
			
		||||
                   if k not in exclude)
 | 
			
		||||
 | 
			
		||||
        return client.get_client(api_version, **env)
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(ClientTest, self).setUp()
 | 
			
		||||
 | 
			
		||||
    def test_client_version(self):
 | 
			
		||||
        c1 = self.create_client(api_version=1)
 | 
			
		||||
        c1 = self.create_client(env=FAKE_ENV, api_version=1)
 | 
			
		||||
        self.assertIsInstance(c1, v1client.Client)
 | 
			
		||||
 | 
			
		||||
        c2 = self.create_client(api_version=2)
 | 
			
		||||
        c2 = self.create_client(env=FAKE_ENV, api_version=2)
 | 
			
		||||
        self.assertIsInstance(c2, v2client.Client)
 | 
			
		||||
 | 
			
		||||
    def test_client_auth_lambda(self):
 | 
			
		||||
        FAKE_ENV['os_auth_token'] = lambda: FAKE_ENV['os_auth_token']
 | 
			
		||||
        self.assertIsInstance(FAKE_ENV['os_auth_token'],
 | 
			
		||||
        env = FAKE_ENV.copy()
 | 
			
		||||
        env['token'] = lambda: env['token']
 | 
			
		||||
        self.assertIsInstance(env['token'],
 | 
			
		||||
                              types.FunctionType)
 | 
			
		||||
        c2 = self.create_client()
 | 
			
		||||
        c2 = self.create_client(env)
 | 
			
		||||
        self.assertIsInstance(c2, v2client.Client)
 | 
			
		||||
 | 
			
		||||
    def test_client_auth_non_lambda(self):
 | 
			
		||||
        FAKE_ENV['os_auth_token'] = "1234"
 | 
			
		||||
        self.assertIsInstance(FAKE_ENV['os_auth_token'], str)
 | 
			
		||||
        c2 = self.create_client()
 | 
			
		||||
        env = FAKE_ENV.copy()
 | 
			
		||||
        env['token'] = "1234"
 | 
			
		||||
        self.assertIsInstance(env['token'], str)
 | 
			
		||||
        c2 = self.create_client(env)
 | 
			
		||||
        self.assertIsInstance(c2, v2client.Client)
 | 
			
		||||
 | 
			
		||||
    @mock.patch('keystoneclient.v2_0.client', fakes.FakeKeystone)
 | 
			
		||||
    def test_client_without_auth_plugin(self):
 | 
			
		||||
        env = FAKE_ENV.copy()
 | 
			
		||||
        del env['auth_plugin']
 | 
			
		||||
        c = self.create_client(env, api_version=2, endpoint='fake_endpoint')
 | 
			
		||||
        self.assertIsInstance(c.auth_plugin, client.AuthPlugin)
 | 
			
		||||
 | 
			
		||||
    def test_client_without_auth_plugin_keystone_v3(self):
 | 
			
		||||
        env = FAKE_ENV.copy()
 | 
			
		||||
        del env['auth_plugin']
 | 
			
		||||
        expected = {
 | 
			
		||||
            'username': 'username',
 | 
			
		||||
            'endpoint': 'http://no.where',
 | 
			
		||||
            'tenant_name': 'tenant_name',
 | 
			
		||||
            'service_type': None,
 | 
			
		||||
            'token': '1234',
 | 
			
		||||
            'endpoint_type': None,
 | 
			
		||||
            'auth_url': 'http://no.where',
 | 
			
		||||
            'tenant_id': None,
 | 
			
		||||
            'cacert': None,
 | 
			
		||||
            'password': 'password',
 | 
			
		||||
            'user_domain_name': 'default',
 | 
			
		||||
            'user_domain_id': None,
 | 
			
		||||
            'project_domain_name': 'default',
 | 
			
		||||
            'project_domain_id': None,
 | 
			
		||||
        }
 | 
			
		||||
        with mock.patch('ceilometerclient.client.AuthPlugin') as auth_plugin:
 | 
			
		||||
            self.create_client(env, api_version=2)
 | 
			
		||||
            auth_plugin.assert_called_with(**expected)
 | 
			
		||||
 | 
			
		||||
    def test_client_with_auth_plugin(self):
 | 
			
		||||
        c = self.create_client(FAKE_ENV, api_version=2)
 | 
			
		||||
        self.assertIsInstance(c.auth_plugin, str)
 | 
			
		||||
 | 
			
		||||
    def test_v2_client_timeout_invalid_value(self):
 | 
			
		||||
        env = FAKE_ENV.copy()
 | 
			
		||||
        env['timeout'] = 'abc'
 | 
			
		||||
        self.assertRaises(ValueError, self.create_client, env)
 | 
			
		||||
        env['timeout'] = '1.5'
 | 
			
		||||
        self.assertRaises(ValueError, self.create_client, env)
 | 
			
		||||
 | 
			
		||||
    def _test_v2_client_timeout_integer(self, timeout, expected_value):
 | 
			
		||||
        env = FAKE_ENV.copy()
 | 
			
		||||
        env['timeout'] = timeout
 | 
			
		||||
        expected = {
 | 
			
		||||
            'auth_plugin': 'fake_auth',
 | 
			
		||||
            'timeout': expected_value,
 | 
			
		||||
            'original_ip': None,
 | 
			
		||||
            'http': None,
 | 
			
		||||
            'region_name': None,
 | 
			
		||||
            'verify': True,
 | 
			
		||||
            'timings': None,
 | 
			
		||||
            'keyring_saver': None,
 | 
			
		||||
            'cert': None,
 | 
			
		||||
            'endpoint_type': None,
 | 
			
		||||
            'user_agent': None,
 | 
			
		||||
            'debug': None,
 | 
			
		||||
        }
 | 
			
		||||
        cls = 'ceilometerclient.openstack.common.apiclient.client.HTTPClient'
 | 
			
		||||
        with mock.patch(cls) as mocked:
 | 
			
		||||
            self.create_client(env)
 | 
			
		||||
            mocked.assert_called_with(**expected)
 | 
			
		||||
 | 
			
		||||
    def test_v2_client_timeout_zero(self):
 | 
			
		||||
        self._test_v2_client_timeout_integer(0, None)
 | 
			
		||||
 | 
			
		||||
    def test_v2_client_timeout_valid_value(self):
 | 
			
		||||
        self._test_v2_client_timeout_integer(30, 30)
 | 
			
		||||
 | 
			
		||||
    def test_v2_client_cacert_in_verify(self):
 | 
			
		||||
        env = FAKE_ENV.copy()
 | 
			
		||||
        env['cacert'] = '/path/to/cacert'
 | 
			
		||||
        client = self.create_client(env)
 | 
			
		||||
        self.assertEqual('/path/to/cacert', client.client.verify)
 | 
			
		||||
 | 
			
		||||
    def test_v2_client_certfile_and_keyfile(self):
 | 
			
		||||
        env = FAKE_ENV.copy()
 | 
			
		||||
        env['cert_file'] = '/path/to/cert'
 | 
			
		||||
        env['key_file'] = '/path/to/keycert'
 | 
			
		||||
        client = self.create_client(env)
 | 
			
		||||
        self.assertEqual(('/path/to/cert', '/path/to/keycert'),
 | 
			
		||||
                         client.client.cert)
 | 
			
		||||
 
 | 
			
		||||
@@ -16,36 +16,56 @@
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
from ceilometerclient import exc
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
 | 
			
		||||
HTTPEXCEPTIONS = {'HTTPBadRequest': exc.HTTPBadRequest,
 | 
			
		||||
                  'HTTPUnauthorized': exc.HTTPUnauthorized,
 | 
			
		||||
                  'HTTPForbidden': exc.HTTPForbidden,
 | 
			
		||||
                  'HTTPNotFound': exc.HTTPNotFound,
 | 
			
		||||
                  'HTTPMethodNotAllowed': exc.HTTPMethodNotAllowed,
 | 
			
		||||
                  'HTTPConflict': exc.HTTPConflict,
 | 
			
		||||
                  'HTTPOverLimit': exc.HTTPOverLimit,
 | 
			
		||||
                  'HTTPInternalServerError': exc.HTTPInternalServerError,
 | 
			
		||||
                  'HTTPNotImplemented': exc.HTTPNotImplemented,
 | 
			
		||||
                  'HTTPBadGateway': exc.HTTPBadGateway,
 | 
			
		||||
                  'HTTPServiceUnavailable': exc.HTTPServiceUnavailable}
 | 
			
		||||
 | 
			
		||||
class HTTPBadRequestTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
class HTTPExceptionsTest(utils.BaseTestCase):
 | 
			
		||||
    def test_str_no_details(self):
 | 
			
		||||
        exception = exc.HTTPBadRequest()
 | 
			
		||||
        self.assertEqual("HTTPBadRequest (HTTP 400)", str(exception))
 | 
			
		||||
        for k, v in HTTPEXCEPTIONS.items():
 | 
			
		||||
            exception = v()
 | 
			
		||||
            ret_str = k + " (HTTP " + str(exception.code) + ")"
 | 
			
		||||
            self.assertEqual(ret_str, str(exception))
 | 
			
		||||
 | 
			
		||||
    def test_str_no_json(self):
 | 
			
		||||
        exception = exc.HTTPBadRequest(details="foo")
 | 
			
		||||
        self.assertEqual("HTTPBadRequest (HTTP 400)", str(exception))
 | 
			
		||||
        for k, v in HTTPEXCEPTIONS.items():
 | 
			
		||||
            exception = v(details="foo")
 | 
			
		||||
            ret_str = k + " (HTTP " + str(exception.code) + ")"
 | 
			
		||||
            self.assertEqual(ret_str, str(exception))
 | 
			
		||||
 | 
			
		||||
    def test_str_no_error_message(self):
 | 
			
		||||
        exception = exc.HTTPBadRequest(details=json.dumps({}))
 | 
			
		||||
        self.assertEqual("HTTPBadRequest (HTTP 400)", str(exception))
 | 
			
		||||
        for k, v in HTTPEXCEPTIONS.items():
 | 
			
		||||
            exception = v(details=json.dumps({}))
 | 
			
		||||
            ret_str = k + " (HTTP " + str(exception.code) + ")"
 | 
			
		||||
            self.assertEqual(ret_str, str(exception))
 | 
			
		||||
 | 
			
		||||
    def test_str_no_faultstring(self):
 | 
			
		||||
        exception = exc.HTTPBadRequest(
 | 
			
		||||
        for k, v in HTTPEXCEPTIONS.items():
 | 
			
		||||
            exception = v(
 | 
			
		||||
                details=json.dumps({"error_message": {"foo": "bar"}}))
 | 
			
		||||
        self.assertEqual("HTTPBadRequest (HTTP 400)", str(exception))
 | 
			
		||||
            ret_str = k + " (HTTP " + str(exception.code) + ")"
 | 
			
		||||
            self.assertEqual(ret_str, str(exception))
 | 
			
		||||
 | 
			
		||||
    def test_str_error_message_unknown_format(self):
 | 
			
		||||
        exception = exc.HTTPBadRequest(
 | 
			
		||||
            details=json.dumps({"error_message": "oops"}))
 | 
			
		||||
        self.assertEqual("HTTPBadRequest (HTTP 400)", str(exception))
 | 
			
		||||
        for k, v in HTTPEXCEPTIONS.items():
 | 
			
		||||
            exception = v(details=json.dumps({"error_message": "oops"}))
 | 
			
		||||
            ret_str = k + " (HTTP " + str(exception.code) + ")"
 | 
			
		||||
            self.assertEqual(ret_str, str(exception))
 | 
			
		||||
 | 
			
		||||
    def test_str_faultstring(self):
 | 
			
		||||
        exception = exc.HTTPBadRequest(
 | 
			
		||||
            details=json.dumps({"error_message": {"faultstring": "oops"}}))
 | 
			
		||||
        self.assertEqual("HTTPBadRequest (HTTP 400) ERROR oops",
 | 
			
		||||
                         str(exception))
 | 
			
		||||
        for k, v in HTTPEXCEPTIONS.items():
 | 
			
		||||
            exception = v(details=json.dumps(
 | 
			
		||||
                {"error_message": {"faultstring": "oops"}}))
 | 
			
		||||
            ret_str = k + " (HTTP " + str(exception.code) + ") ERROR oops"
 | 
			
		||||
            self.assertEqual(ret_str, str(exception))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
# Copyright 2012 OpenStack Foundation
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import mock
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.common import http
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HttpClientTest(utils.BaseTestCase):
 | 
			
		||||
    url = 'http://localhost'
 | 
			
		||||
 | 
			
		||||
    def test_url_generation_trailing_slash_in_base(self):
 | 
			
		||||
        client = http.HTTPClient("%s/" % self.url)
 | 
			
		||||
        url = client._make_connection_url('/v1/resources')
 | 
			
		||||
        self.assertEqual(url, '/v1/resources')
 | 
			
		||||
 | 
			
		||||
    def test_url_generation_without_trailing_slash_in_base(self):
 | 
			
		||||
        client = http.HTTPClient(self.url)
 | 
			
		||||
        url = client._make_connection_url('/v1/resources')
 | 
			
		||||
        self.assertEqual(url, '/v1/resources')
 | 
			
		||||
 | 
			
		||||
    def test_url_generation_prefix_slash_in_path(self):
 | 
			
		||||
        client = http.HTTPClient("%s/" % self.url)
 | 
			
		||||
        url = client._make_connection_url('/v1/resources')
 | 
			
		||||
        self.assertEqual(url, '/v1/resources')
 | 
			
		||||
 | 
			
		||||
    def test_url_generation_without_prefix_slash_in_path(self):
 | 
			
		||||
        client = http.HTTPClient(self.url)
 | 
			
		||||
        url = client._make_connection_url('v1/resources')
 | 
			
		||||
        self.assertEqual(url, '/v1/resources')
 | 
			
		||||
 | 
			
		||||
    def test_get_connection(self):
 | 
			
		||||
        client = http.HTTPClient(self.url)
 | 
			
		||||
        self.assertIsNotNone(client.get_connection())
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(http.HTTPClient, 'get_connection')
 | 
			
		||||
    def test_url_generation_with_proxy(self, get_conn):
 | 
			
		||||
        client = http.HTTPClient(self.url, token=lambda: 'token')
 | 
			
		||||
        client.proxy_url = "http://localhost:3128/"
 | 
			
		||||
        conn = mock.MagicMock()
 | 
			
		||||
        conn.request.side_effect = Exception("stop")
 | 
			
		||||
        get_conn.return_value = conn
 | 
			
		||||
        try:
 | 
			
		||||
            client._http_request('/v1/resources', 'GET')
 | 
			
		||||
        except Exception:
 | 
			
		||||
            pass
 | 
			
		||||
        conn.request.assert_called_once_with('GET', (self.url.rstrip('/') +
 | 
			
		||||
                                                     '/v1/resources'),
 | 
			
		||||
                                             headers=mock.ANY)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HttpsClientTest(HttpClientTest):
 | 
			
		||||
    url = 'https://localhost'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HttpEndingSlashClientTest(HttpClientTest):
 | 
			
		||||
    url = 'http://localhost/'
 | 
			
		||||
@@ -11,55 +11,56 @@
 | 
			
		||||
#   under the License.
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
import six
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
import fixtures
 | 
			
		||||
from keystoneclient import session as ks_session
 | 
			
		||||
import mock
 | 
			
		||||
import six
 | 
			
		||||
from testtools import matchers
 | 
			
		||||
 | 
			
		||||
from keystoneclient.v2_0 import client as ksclient
 | 
			
		||||
 | 
			
		||||
from ceilometerclient import exc
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client as api_client
 | 
			
		||||
from ceilometerclient import shell as ceilometer_shell
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
from ceilometerclient.v1 import client as v1client
 | 
			
		||||
from ceilometerclient.v2 import client as v2client
 | 
			
		||||
 | 
			
		||||
FAKE_ENV = {'OS_USERNAME': 'username',
 | 
			
		||||
FAKE_V2_ENV = {'OS_USERNAME': 'username',
 | 
			
		||||
               'OS_PASSWORD': 'password',
 | 
			
		||||
               'OS_TENANT_NAME': 'tenant_name',
 | 
			
		||||
            'OS_AUTH_URL': 'http://no.where'}
 | 
			
		||||
               'OS_AUTH_URL': 'http://localhost:5000/v2.0'}
 | 
			
		||||
 | 
			
		||||
FAKE_V3_ENV = {'OS_USERNAME': 'username',
 | 
			
		||||
               'OS_PASSWORD': 'password',
 | 
			
		||||
               'OS_USER_DOMAIN_NAME': 'domain_name',
 | 
			
		||||
               'OS_PROJECT_ID': '1234567890',
 | 
			
		||||
               'OS_AUTH_URL': 'http://localhost:5000/v3'}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellTest(utils.BaseTestCase):
 | 
			
		||||
    re_options = re.DOTALL | re.MULTILINE
 | 
			
		||||
class ShellTestBase(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    # Patch os.environ to avoid required auth info.
 | 
			
		||||
    def make_env(self, exclude=None):
 | 
			
		||||
        env = dict((k, v) for k, v in FAKE_ENV.items() if k != exclude)
 | 
			
		||||
    def make_env(self, env_version, exclude=None):
 | 
			
		||||
        env = dict((k, v) for k, v in env_version.items() if k != exclude)
 | 
			
		||||
        self.useFixture(fixtures.MonkeyPatch('os.environ', env))
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(ShellTest, self).setUp()
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(ksclient, 'Client')
 | 
			
		||||
    @mock.patch.object(v1client.http.HTTPClient, 'json_request')
 | 
			
		||||
    @mock.patch.object(v1client.http.HTTPClient, 'raw_request')
 | 
			
		||||
    def shell(self, argstr, mock_ksclient, mock_json, mock_raw):
 | 
			
		||||
        orig = sys.stdout
 | 
			
		||||
class ShellHelpTest(ShellTestBase):
 | 
			
		||||
    RE_OPTIONS = re.DOTALL | re.MULTILINE
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    @mock.patch.object(ks_session, 'Session', mock.MagicMock())
 | 
			
		||||
    @mock.patch.object(v2client.client.HTTPClient,
 | 
			
		||||
                       'client_request', mock.MagicMock())
 | 
			
		||||
    def shell(self, argstr):
 | 
			
		||||
        try:
 | 
			
		||||
            sys.stdout = six.StringIO()
 | 
			
		||||
            _shell = ceilometer_shell.CeilometerShell()
 | 
			
		||||
            _shell.main(argstr.split())
 | 
			
		||||
        except SystemExit:
 | 
			
		||||
            exc_type, exc_value, exc_traceback = sys.exc_info()
 | 
			
		||||
            self.assertEqual(exc_value.code, 0)
 | 
			
		||||
        finally:
 | 
			
		||||
            out = sys.stdout.getvalue()
 | 
			
		||||
            sys.stdout.close()
 | 
			
		||||
            sys.stdout = orig
 | 
			
		||||
 | 
			
		||||
        return out
 | 
			
		||||
        return sys.stdout.getvalue()
 | 
			
		||||
 | 
			
		||||
    def test_help_unknown_command(self):
 | 
			
		||||
        self.assertRaises(exc.CommandError, self.shell, 'help foofoo')
 | 
			
		||||
@@ -75,7 +76,7 @@ class ShellTest(utils.BaseTestCase):
 | 
			
		||||
            for r in required:
 | 
			
		||||
                self.assertThat(help_text,
 | 
			
		||||
                                matchers.MatchesRegex(r,
 | 
			
		||||
                                                      self.re_options))
 | 
			
		||||
                                                      self.RE_OPTIONS))
 | 
			
		||||
 | 
			
		||||
    def test_help_on_subcommand(self):
 | 
			
		||||
        required = [
 | 
			
		||||
@@ -89,29 +90,136 @@ class ShellTest(utils.BaseTestCase):
 | 
			
		||||
            help_text = self.shell(argstr)
 | 
			
		||||
            for r in required:
 | 
			
		||||
                self.assertThat(help_text,
 | 
			
		||||
                                matchers.MatchesRegex(r, self.re_options))
 | 
			
		||||
                                matchers.MatchesRegex(r, self.RE_OPTIONS))
 | 
			
		||||
 | 
			
		||||
    def test_auth_param(self):
 | 
			
		||||
        self.make_env(exclude='OS_USERNAME')
 | 
			
		||||
        self.test_help()
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(ksclient, 'Client')
 | 
			
		||||
class ShellKeystoneV2Test(ShellTestBase):
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(ks_session, 'Session')
 | 
			
		||||
    def test_debug_switch_raises_error(self, mock_ksclient):
 | 
			
		||||
        mock_ksclient.side_effect = exc.Unauthorized
 | 
			
		||||
        self.make_env()
 | 
			
		||||
        mock_ksclient.side_effect = exc.HTTPUnauthorized
 | 
			
		||||
        self.make_env(FAKE_V2_ENV)
 | 
			
		||||
        args = ['--debug', 'event-list']
 | 
			
		||||
        self.assertRaises(exc.Unauthorized, ceilometer_shell.main, args)
 | 
			
		||||
        self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(ksclient, 'Client')
 | 
			
		||||
    @mock.patch.object(ks_session, 'Session')
 | 
			
		||||
    def test_dash_d_switch_raises_error(self, mock_ksclient):
 | 
			
		||||
        mock_ksclient.side_effect = exc.CommandError("FAIL")
 | 
			
		||||
        self.make_env()
 | 
			
		||||
        self.make_env(FAKE_V2_ENV)
 | 
			
		||||
        args = ['-d', 'event-list']
 | 
			
		||||
        self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(ksclient, 'Client')
 | 
			
		||||
    def test_no_debug_switch_no_raises_errors(self, mock_ksclient):
 | 
			
		||||
        mock_ksclient.side_effect = exc.Unauthorized("FAIL")
 | 
			
		||||
        self.make_env()
 | 
			
		||||
    @mock.patch('sys.stderr')
 | 
			
		||||
    @mock.patch.object(ks_session, 'Session')
 | 
			
		||||
    def test_no_debug_switch_no_raises_errors(self, mock_ksclient, __):
 | 
			
		||||
        mock_ksclient.side_effect = exc.HTTPUnauthorized("FAIL")
 | 
			
		||||
        self.make_env(FAKE_V2_ENV)
 | 
			
		||||
        args = ['event-list']
 | 
			
		||||
        self.assertRaises(SystemExit, ceilometer_shell.main, args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellKeystoneV3Test(ShellTestBase):
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(ks_session, 'Session')
 | 
			
		||||
    def test_debug_switch_raises_error(self, mock_ksclient):
 | 
			
		||||
        mock_ksclient.side_effect = exc.HTTPUnauthorized
 | 
			
		||||
        self.make_env(FAKE_V3_ENV)
 | 
			
		||||
        args = ['--debug', 'event-list']
 | 
			
		||||
        self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(ks_session, 'Session')
 | 
			
		||||
    def test_dash_d_switch_raises_error(self, mock_ksclient):
 | 
			
		||||
        mock_ksclient.side_effect = exc.CommandError("FAIL")
 | 
			
		||||
        self.make_env(FAKE_V3_ENV)
 | 
			
		||||
        args = ['-d', 'event-list']
 | 
			
		||||
        self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stderr')
 | 
			
		||||
    @mock.patch.object(ks_session, 'Session')
 | 
			
		||||
    def test_no_debug_switch_no_raises_errors(self, mock_ksclient, __):
 | 
			
		||||
        mock_ksclient.side_effect = exc.HTTPUnauthorized("FAIL")
 | 
			
		||||
        self.make_env(FAKE_V3_ENV)
 | 
			
		||||
        args = ['event-list']
 | 
			
		||||
        self.assertRaises(SystemExit, ceilometer_shell.main, args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellTimeoutTest(ShellTestBase):
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stderr', new=six.StringIO())
 | 
			
		||||
    def _test_timeout(self, timeout, expected_msg):
 | 
			
		||||
        args = ['--timeout', timeout, 'alarm-list']
 | 
			
		||||
        self.assertRaises(SystemExit, ceilometer_shell.main, args)
 | 
			
		||||
        self.assertEqual(expected_msg, sys.stderr.getvalue().splitlines()[-1])
 | 
			
		||||
 | 
			
		||||
    def test_timeout_invalid_value(self):
 | 
			
		||||
        expected_msg = ('ceilometer: error: argument --timeout: '
 | 
			
		||||
                        'abc must be an integer')
 | 
			
		||||
        self._test_timeout('abc', expected_msg)
 | 
			
		||||
 | 
			
		||||
    def test_timeout_negative_value(self):
 | 
			
		||||
        expected_msg = ('ceilometer: error: argument --timeout: '
 | 
			
		||||
                        '-1 must be greater than 0')
 | 
			
		||||
        self._test_timeout('-1', expected_msg)
 | 
			
		||||
 | 
			
		||||
    def test_timeout_float_value(self):
 | 
			
		||||
        expected_msg = ('ceilometer: error: argument --timeout: '
 | 
			
		||||
                        '1.5 must be an integer')
 | 
			
		||||
        self._test_timeout('1.5', expected_msg)
 | 
			
		||||
 | 
			
		||||
    def test_timeout_zero(self):
 | 
			
		||||
        expected_msg = ('ceilometer: error: argument --timeout: '
 | 
			
		||||
                        '0 must be greater than 0')
 | 
			
		||||
        self._test_timeout('0', expected_msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellInsecureTest(ShellTestBase):
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(api_client, 'HTTPClient')
 | 
			
		||||
    def test_insecure_true_ceilometer(self, mocked_client):
 | 
			
		||||
        self.make_env(FAKE_V2_ENV)
 | 
			
		||||
        args = ['--debug', '--os-insecure', 'true', 'alarm-list']
 | 
			
		||||
        self.assertIsNone(ceilometer_shell.main(args))
 | 
			
		||||
        args, kwargs = mocked_client.call_args
 | 
			
		||||
        self.assertEqual(False, kwargs.get('verify'))
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(ks_session, 'Session')
 | 
			
		||||
    def test_insecure_true_keystone(self, mocked_session):
 | 
			
		||||
        mocked_session.side_effect = exc.HTTPUnauthorized("FAIL")
 | 
			
		||||
        self.make_env(FAKE_V2_ENV)
 | 
			
		||||
        args = ['--debug', '--os-insecure', 'true', 'alarm-list']
 | 
			
		||||
        self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
 | 
			
		||||
        mocked_session.assert_called_with(verify=False, cert='')
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(api_client, 'HTTPClient')
 | 
			
		||||
    def test_insecure_false_ceilometer(self, mocked_client):
 | 
			
		||||
        self.make_env(FAKE_V2_ENV)
 | 
			
		||||
        args = ['--debug', '--os-insecure', 'false', 'alarm-list']
 | 
			
		||||
        self.assertIsNone(ceilometer_shell.main(args))
 | 
			
		||||
        args, kwargs = mocked_client.call_args
 | 
			
		||||
        self.assertEqual(True, kwargs.get('verify'))
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(ks_session, 'Session')
 | 
			
		||||
    def test_insecure_false_keystone(self, mocked_session):
 | 
			
		||||
        mocked_session.side_effect = exc.HTTPUnauthorized("FAIL")
 | 
			
		||||
        self.make_env(FAKE_V2_ENV)
 | 
			
		||||
        args = ['--debug', '--os-insecure', 'false', 'alarm-list']
 | 
			
		||||
        self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
 | 
			
		||||
        mocked_session.assert_called_with(verify=True, cert='')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellEndpointTest(ShellTestBase):
 | 
			
		||||
 | 
			
		||||
    @mock.patch('ceilometerclient.v2.client.Client')
 | 
			
		||||
    def _test_endpoint_and_token(self, token_name, endpoint_name, mocked):
 | 
			
		||||
        args = ['--debug', token_name, 'fake-token',
 | 
			
		||||
                endpoint_name, 'http://fake-url', 'alarm-list']
 | 
			
		||||
        self.assertEqual(None, ceilometer_shell.main(args))
 | 
			
		||||
        args, kwargs = mocked.call_args
 | 
			
		||||
        self.assertEqual('http://fake-url', kwargs.get('endpoint'))
 | 
			
		||||
        self.assertEqual('fake-token', kwargs.get('token'))
 | 
			
		||||
 | 
			
		||||
    def test_endpoint_and_token(self):
 | 
			
		||||
        self._test_endpoint_and_token('--os-auth-token', '--ceilometer-url')
 | 
			
		||||
        self._test_endpoint_and_token('--os-auth-token', '--os-endpoint')
 | 
			
		||||
        self._test_endpoint_and_token('--os-token', '--ceilometer-url')
 | 
			
		||||
        self._test_endpoint_and_token('--os-token', '--os-endpoint')
 | 
			
		||||
 
 | 
			
		||||
@@ -13,11 +13,10 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import itertools
 | 
			
		||||
 | 
			
		||||
import mock
 | 
			
		||||
import six
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.common import utils
 | 
			
		||||
from ceilometerclient.tests import utils as test_utils
 | 
			
		||||
@@ -26,30 +25,56 @@ from ceilometerclient.tests import utils as test_utils
 | 
			
		||||
class UtilsTest(test_utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_prettytable(self):
 | 
			
		||||
        class Struct:
 | 
			
		||||
        class Struct(object):
 | 
			
		||||
            def __init__(self, **entries):
 | 
			
		||||
                self.__dict__.update(entries)
 | 
			
		||||
 | 
			
		||||
        # test that the prettytable output is wellformatted (left-aligned)
 | 
			
		||||
        saved_stdout = sys.stdout
 | 
			
		||||
        try:
 | 
			
		||||
            sys.stdout = output_dict = six.StringIO()
 | 
			
		||||
        with mock.patch('sys.stdout', new=six.StringIO()) as stdout:
 | 
			
		||||
            utils.print_dict({'K': 'k', 'Key': 'Value'})
 | 
			
		||||
 | 
			
		||||
        finally:
 | 
			
		||||
            sys.stdout = saved_stdout
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(output_dict.getvalue(), '''\
 | 
			
		||||
            self.assertEqual('''\
 | 
			
		||||
+----------+-------+
 | 
			
		||||
| Property | Value |
 | 
			
		||||
+----------+-------+
 | 
			
		||||
| K        | k     |
 | 
			
		||||
| Key      | Value |
 | 
			
		||||
+----------+-------+
 | 
			
		||||
''')
 | 
			
		||||
''', stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
        with mock.patch('sys.stdout', new=six.StringIO()) as stdout:
 | 
			
		||||
            utils.print_dict({'alarm_id': '262567fd-d79a-4bbb-a9d0-59d879b6',
 | 
			
		||||
                              'description': 'test alarm',
 | 
			
		||||
                              'state': 'insufficient data',
 | 
			
		||||
                              'repeat_actions': 'False',
 | 
			
		||||
                              'type': 'threshold',
 | 
			
		||||
                              'threshold': '1.0',
 | 
			
		||||
                              'statistic': 'avg',
 | 
			
		||||
                              'time_constraints': '[{name: c1,'
 | 
			
		||||
                                                  '\\n  description: test,'
 | 
			
		||||
                                                  '\\n  start: 0 18 * * *,'
 | 
			
		||||
                                                  '\\n  duration: 1,'
 | 
			
		||||
                                                  '\\n  timezone: US}]'})
 | 
			
		||||
            self.assertEqual('''\
 | 
			
		||||
+------------------+----------------------------------+
 | 
			
		||||
| Property         | Value                            |
 | 
			
		||||
+------------------+----------------------------------+
 | 
			
		||||
| alarm_id         | 262567fd-d79a-4bbb-a9d0-59d879b6 |
 | 
			
		||||
| description      | test alarm                       |
 | 
			
		||||
| repeat_actions   | False                            |
 | 
			
		||||
| state            | insufficient data                |
 | 
			
		||||
| statistic        | avg                              |
 | 
			
		||||
| threshold        | 1.0                              |
 | 
			
		||||
| time_constraints | [{name: c1,                      |
 | 
			
		||||
|                  |   description: test,             |
 | 
			
		||||
|                  |   start: 0 18 * * *,             |
 | 
			
		||||
|                  |   duration: 1,                   |
 | 
			
		||||
|                  |   timezone: US}]                 |
 | 
			
		||||
| type             | threshold                        |
 | 
			
		||||
+------------------+----------------------------------+
 | 
			
		||||
''', stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
    def test_print_list(self):
 | 
			
		||||
        class Foo:
 | 
			
		||||
        class Foo(object):
 | 
			
		||||
            def __init__(self, one, two, three):
 | 
			
		||||
                self.one = one
 | 
			
		||||
                self.two = two
 | 
			
		||||
@@ -61,17 +86,13 @@ class UtilsTest(test_utils.BaseTestCase):
 | 
			
		||||
            Foo(12, '0', 'Z')]
 | 
			
		||||
 | 
			
		||||
        def do_print_list(sortby):
 | 
			
		||||
            saved_stdout = sys.stdout
 | 
			
		||||
            try:
 | 
			
		||||
                sys.stdout = output = six.StringIO()
 | 
			
		||||
            with mock.patch('sys.stdout', new=six.StringIO()) as stdout:
 | 
			
		||||
                utils.print_list(foo_list,
 | 
			
		||||
                                 ['one', 'two', 'three'],
 | 
			
		||||
                                 ['1st', '2nd', '3rd'],
 | 
			
		||||
                                 {'one': lambda o: o.one * 10},
 | 
			
		||||
                                 sortby)
 | 
			
		||||
            finally:
 | 
			
		||||
                sys.stdout = saved_stdout
 | 
			
		||||
            return output.getvalue()
 | 
			
		||||
                return stdout.getvalue()
 | 
			
		||||
 | 
			
		||||
        printed = do_print_list(None)
 | 
			
		||||
        self.assertEqual(printed, '''\
 | 
			
		||||
 
 | 
			
		||||
@@ -13,54 +13,12 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
import fixtures
 | 
			
		||||
import six
 | 
			
		||||
import testtools
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.common import http
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseTestCase(testtools.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(BaseTestCase, self).setUp()
 | 
			
		||||
        self.useFixture(fixtures.FakeLogger())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeAPI(object):
 | 
			
		||||
    def __init__(self, fixtures):
 | 
			
		||||
        self.fixtures = fixtures
 | 
			
		||||
        self.calls = []
 | 
			
		||||
 | 
			
		||||
    def _request(self, method, url, headers=None, body=None):
 | 
			
		||||
        call = (method, url, headers or {}, body)
 | 
			
		||||
        self.calls.append(call)
 | 
			
		||||
        return self.fixtures[url][method]
 | 
			
		||||
 | 
			
		||||
    def raw_request(self, *args, **kwargs):
 | 
			
		||||
        fixture = self._request(*args, **kwargs)
 | 
			
		||||
        body_iter = http.ResponseBodyIterator(six.StringIO(fixture[1]))
 | 
			
		||||
        return FakeResponse(fixture[0]), body_iter
 | 
			
		||||
 | 
			
		||||
    def json_request(self, *args, **kwargs):
 | 
			
		||||
        fixture = self._request(*args, **kwargs)
 | 
			
		||||
        return FakeResponse(fixture[0]), fixture[1]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeResponse(object):
 | 
			
		||||
    def __init__(self, headers, body=None, version=None):
 | 
			
		||||
        """:param headers: dict representing HTTP response headers
 | 
			
		||||
        :param body: file-like object
 | 
			
		||||
        """
 | 
			
		||||
        self.headers = headers
 | 
			
		||||
        self.body = body
 | 
			
		||||
 | 
			
		||||
    def getheaders(self):
 | 
			
		||||
        return copy.deepcopy(self.headers).items()
 | 
			
		||||
 | 
			
		||||
    def getheader(self, key, default):
 | 
			
		||||
        return self.headers.get(key, default)
 | 
			
		||||
 | 
			
		||||
    def read(self, amt):
 | 
			
		||||
        return self.body.read(amt)
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,8 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v1.meters
 | 
			
		||||
 | 
			
		||||
@@ -109,15 +110,16 @@ class MeterManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(MeterManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v1.meters.MeterManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list_all(self):
 | 
			
		||||
        resources = list(self.mgr.list())
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/meters', {}, None),
 | 
			
		||||
            'GET', '/v1/meters'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 2)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'a')
 | 
			
		||||
        self.assertEqual(resources[1].resource_id, 'b')
 | 
			
		||||
@@ -125,9 +127,9 @@ class MeterManagerTest(utils.BaseTestCase):
 | 
			
		||||
    def test_list_by_source(self):
 | 
			
		||||
        resources = list(self.mgr.list(source='openstack'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/sources/openstack/meters', {}, None),
 | 
			
		||||
            'GET', '/v1/sources/openstack/meters'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 2)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'b')
 | 
			
		||||
        self.assertEqual(resources[1].resource_id, 'q')
 | 
			
		||||
@@ -135,26 +137,26 @@ class MeterManagerTest(utils.BaseTestCase):
 | 
			
		||||
    def test_list_by_user(self):
 | 
			
		||||
        resources = list(self.mgr.list(user_id='joey'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/users/joey/meters', {}, None),
 | 
			
		||||
            'GET', '/v1/users/joey/meters'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 1)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'b')
 | 
			
		||||
 | 
			
		||||
    def test_list_by_project(self):
 | 
			
		||||
        resources = list(self.mgr.list(project_id='dig_the_ditch'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/projects/dig_the_ditch/meters', {}, None),
 | 
			
		||||
            'GET', '/v1/projects/dig_the_ditch/meters'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 1)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'b')
 | 
			
		||||
 | 
			
		||||
    def test_list_by_metaquery(self):
 | 
			
		||||
        resources = list(self.mgr.list(metaquery='metadata.zxc_id=foo'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/meters?metadata.zxc_id=foo', {}, None),
 | 
			
		||||
            'GET', '/v1/meters?metadata.zxc_id=foo'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 1)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'b')
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,8 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v1.meters
 | 
			
		||||
 | 
			
		||||
@@ -40,15 +41,16 @@ class ProjectManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(ProjectManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v1.meters.ProjectManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list_all(self):
 | 
			
		||||
        projects = list(self.mgr.list())
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/projects', {}, None),
 | 
			
		||||
            'GET', '/v1/projects'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(projects), 2)
 | 
			
		||||
        self.assertEqual(projects[0].project_id, 'a')
 | 
			
		||||
        self.assertEqual(projects[1].project_id, 'b')
 | 
			
		||||
@@ -56,8 +58,8 @@ class ProjectManagerTest(utils.BaseTestCase):
 | 
			
		||||
    def test_list_by_source(self):
 | 
			
		||||
        projects = list(self.mgr.list(source='source_b'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/sources/source_b/projects', {}, None),
 | 
			
		||||
            'GET', '/v1/sources/source_b/projects'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(projects), 1)
 | 
			
		||||
        self.assertEqual(projects[0].project_id, 'b')
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,8 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v1.meters
 | 
			
		||||
 | 
			
		||||
@@ -108,15 +109,16 @@ class ResourceManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(ResourceManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v1.meters.ResourceManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list_all(self):
 | 
			
		||||
        resources = list(self.mgr.list())
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/resources', {}, None),
 | 
			
		||||
            'GET', '/v1/resources'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 2)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'a')
 | 
			
		||||
        self.assertEqual(resources[1].resource_id, 'b')
 | 
			
		||||
@@ -124,27 +126,27 @@ class ResourceManagerTest(utils.BaseTestCase):
 | 
			
		||||
    def test_list_by_user(self):
 | 
			
		||||
        resources = list(self.mgr.list(user_id='joey'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/users/joey/resources', {}, None),
 | 
			
		||||
            'GET', '/v1/users/joey/resources'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 1)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'b')
 | 
			
		||||
 | 
			
		||||
    def test_list_by_metaquery(self):
 | 
			
		||||
        resources = list(self.mgr.list(metaquery='metadata.zxc_id=foo'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/resources?metadata.zxc_id=foo', {}, None),
 | 
			
		||||
            'GET', '/v1/resources?metadata.zxc_id=foo'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 1)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'b')
 | 
			
		||||
 | 
			
		||||
    def test_list_by_project(self):
 | 
			
		||||
        resources = list(self.mgr.list(project_id='project_bla'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/projects/project_bla/resources', {}, None),
 | 
			
		||||
            'GET', '/v1/projects/project_bla/resources'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 1)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'a')
 | 
			
		||||
 | 
			
		||||
@@ -152,9 +154,8 @@ class ResourceManagerTest(utils.BaseTestCase):
 | 
			
		||||
        resources = list(self.mgr.list(start_timestamp='now',
 | 
			
		||||
                                       end_timestamp='now'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/resources?start_timestamp=now&end_timestamp=now',
 | 
			
		||||
             {}, None),
 | 
			
		||||
            'GET', '/v1/resources?start_timestamp=now&end_timestamp=now'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 1)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'b')
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,8 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v1.meters
 | 
			
		||||
 | 
			
		||||
@@ -122,24 +123,25 @@ class SampleManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(SampleManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v1.meters.SampleManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list_all(self):
 | 
			
		||||
        samples = list(self.mgr.list(counter_name=None))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/meters', {}, None),
 | 
			
		||||
            'GET', '/v1/meters'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(samples), 0)
 | 
			
		||||
 | 
			
		||||
    def test_list_by_source(self):
 | 
			
		||||
        samples = list(self.mgr.list(source='openstack',
 | 
			
		||||
                                     counter_name='this'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/sources/openstack/meters/this', {}, None),
 | 
			
		||||
            'GET', '/v1/sources/openstack/meters/this'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(samples), 1)
 | 
			
		||||
        self.assertEqual(samples[0].resource_id, 'b')
 | 
			
		||||
 | 
			
		||||
@@ -147,9 +149,9 @@ class SampleManagerTest(utils.BaseTestCase):
 | 
			
		||||
        samples = list(self.mgr.list(user_id='freddy',
 | 
			
		||||
                                     counter_name='balls'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/users/freddy/meters/balls', {}, None),
 | 
			
		||||
            'GET', '/v1/users/freddy/meters/balls'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(samples), 1)
 | 
			
		||||
        self.assertEqual(samples[0].project_id, 'melbourne_open')
 | 
			
		||||
        self.assertEqual(samples[0].user_id, 'freddy')
 | 
			
		||||
@@ -159,9 +161,9 @@ class SampleManagerTest(utils.BaseTestCase):
 | 
			
		||||
        samples = list(self.mgr.list(project_id='dig_the_ditch',
 | 
			
		||||
                                     counter_name='meters'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/projects/dig_the_ditch/meters/meters', {}, None),
 | 
			
		||||
            'GET', '/v1/projects/dig_the_ditch/meters/meters'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(samples), 1)
 | 
			
		||||
        self.assertEqual(samples[0].project_id, 'dig_the_ditch')
 | 
			
		||||
        self.assertEqual(samples[0].volume, 345)
 | 
			
		||||
@@ -171,9 +173,9 @@ class SampleManagerTest(utils.BaseTestCase):
 | 
			
		||||
        samples = list(self.mgr.list(metaquery='metadata.zxc_id=foo',
 | 
			
		||||
                                     counter_name='this'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/meters?metadata.zxc_id=foo', {}, None),
 | 
			
		||||
            'GET', '/v1/meters?metadata.zxc_id=foo'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(samples), 1)
 | 
			
		||||
        self.assertEqual(samples[0].resource_metadata['zxc_id'], 'foo')
 | 
			
		||||
 | 
			
		||||
@@ -183,12 +185,11 @@ class SampleManagerTest(utils.BaseTestCase):
 | 
			
		||||
                                     start_timestamp='now',
 | 
			
		||||
                                     end_timestamp='now'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET',
 | 
			
		||||
            'GET',
 | 
			
		||||
            '/v1/users/freddy/meters/balls?' +
 | 
			
		||||
             'start_timestamp=now&end_timestamp=now',
 | 
			
		||||
             {}, None),
 | 
			
		||||
            'start_timestamp=now&end_timestamp=now'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(samples), 1)
 | 
			
		||||
        self.assertEqual(samples[0].project_id, 'melbourne_open')
 | 
			
		||||
        self.assertEqual(samples[0].user_id, 'freddy')
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,8 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v1.meters
 | 
			
		||||
 | 
			
		||||
@@ -40,15 +41,16 @@ class UserManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(UserManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v1.meters.UserManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list_all(self):
 | 
			
		||||
        users = list(self.mgr.list())
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/users', {}, None),
 | 
			
		||||
            'GET', '/v1/users'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(users), 2)
 | 
			
		||||
        self.assertEqual(users[0].user_id, 'a')
 | 
			
		||||
        self.assertEqual(users[1].user_id, 'b')
 | 
			
		||||
@@ -56,8 +58,8 @@ class UserManagerTest(utils.BaseTestCase):
 | 
			
		||||
    def test_list_by_source(self):
 | 
			
		||||
        users = list(self.mgr.list(source='source_b'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v1/sources/source_b/users', {}, None),
 | 
			
		||||
            'GET', '/v1/sources/source_b/users'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(users), 1)
 | 
			
		||||
        self.assertEqual(users[0].user_id, 'b')
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,5 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
# Copyright © 2013 Red Hat, Inc
 | 
			
		||||
#
 | 
			
		||||
# Author:  Eoghan Glynn <eglynn@redhat.com>
 | 
			
		||||
# Copyright 2013 Red Hat, Inc
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
@@ -22,13 +19,16 @@ import six
 | 
			
		||||
from six.moves import xrange  # noqa
 | 
			
		||||
import testtools
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
from ceilometerclient import exc
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.v2 import alarms
 | 
			
		||||
 | 
			
		||||
AN_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'],
 | 
			
		||||
            u'ok_actions': [u'http://site:8000/ok'],
 | 
			
		||||
            u'description': u'An alarm',
 | 
			
		||||
            u'type': u'threshold',
 | 
			
		||||
            u'severity': 'low',
 | 
			
		||||
            u'threshold_rule': {
 | 
			
		||||
                u'meter_name': u'storage.objects',
 | 
			
		||||
                u'query': [{u'field': u'key_name',
 | 
			
		||||
@@ -38,14 +38,16 @@ AN_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'],
 | 
			
		||||
                u'period': 240.0,
 | 
			
		||||
                u'statistic': u'avg',
 | 
			
		||||
                u'threshold': 200.0,
 | 
			
		||||
                u'comparison_operator': 'gt',
 | 
			
		||||
            },
 | 
			
		||||
            u'time_constraints': [{u'name': u'cons1',
 | 
			
		||||
                u'comparison_operator': 'gt'},
 | 
			
		||||
            u'time_constraints': [
 | 
			
		||||
                {
 | 
			
		||||
                    u'name': u'cons1',
 | 
			
		||||
                    u'description': u'desc1',
 | 
			
		||||
                    u'start': u'0 11 * * *',
 | 
			
		||||
                    u'duration': 300,
 | 
			
		||||
                    u'timezone': u''},
 | 
			
		||||
                                  {u'name': u'cons2',
 | 
			
		||||
                {
 | 
			
		||||
                    u'name': u'cons2',
 | 
			
		||||
                    u'description': u'desc2',
 | 
			
		||||
                    u'start': u'0 23 * * *',
 | 
			
		||||
                    u'duration': 600,
 | 
			
		||||
@@ -106,6 +108,7 @@ AN_LEGACY_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'],
 | 
			
		||||
                   u'period': 240.0,
 | 
			
		||||
                   u'alarm_id': u'alarm-id',
 | 
			
		||||
                   u'state': u'ok',
 | 
			
		||||
                   u'severity': u'low',
 | 
			
		||||
                   u'insufficient_data_actions': [u'http://site:8000/nodata'],
 | 
			
		||||
                   u'statistic': u'avg',
 | 
			
		||||
                   u'threshold': 200.0,
 | 
			
		||||
@@ -200,16 +203,31 @@ fixtures = {
 | 
			
		||||
            {},
 | 
			
		||||
            UPDATED_ALARM,
 | 
			
		||||
        ),
 | 
			
		||||
        'DELETE': (
 | 
			
		||||
            {},
 | 
			
		||||
            None,
 | 
			
		||||
        ),
 | 
			
		||||
    },
 | 
			
		||||
    '/v2/alarms/unk-alarm-id':
 | 
			
		||||
    {
 | 
			
		||||
        'GET': (
 | 
			
		||||
            {},
 | 
			
		||||
            None,
 | 
			
		||||
        ),
 | 
			
		||||
        'PUT': (
 | 
			
		||||
            {},
 | 
			
		||||
            None,
 | 
			
		||||
        ),
 | 
			
		||||
    },
 | 
			
		||||
    '/v2/alarms/alarm-id/state':
 | 
			
		||||
    {
 | 
			
		||||
        'PUT': (
 | 
			
		||||
            {},
 | 
			
		||||
            'alarm'
 | 
			
		||||
            {'alarm': 'alarm'}
 | 
			
		||||
        ),
 | 
			
		||||
        'GET': (
 | 
			
		||||
            {},
 | 
			
		||||
            'alarm'
 | 
			
		||||
            {'alarm': 'alarm'}
 | 
			
		||||
        ),
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
@@ -249,15 +267,16 @@ class AlarmManagerTest(testtools.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(AlarmManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = alarms.AlarmManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list_all(self):
 | 
			
		||||
        alarms = list(self.mgr.list())
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/alarms', {}, None),
 | 
			
		||||
            'GET', '/v2/alarms'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(alarms), 1)
 | 
			
		||||
        self.assertEqual(alarms[0].alarm_id, 'alarm-id')
 | 
			
		||||
 | 
			
		||||
@@ -267,53 +286,58 @@ class AlarmManagerTest(testtools.TestCase):
 | 
			
		||||
                                       {"field": "name",
 | 
			
		||||
                                        "value": "SwiftObjectAlarm"}]))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET',
 | 
			
		||||
            'GET',
 | 
			
		||||
            '/v2/alarms?q.field=project_id&q.field=name&q.op=&q.op='
 | 
			
		||||
            '&q.type=&q.type=&q.value=project-id&q.value=SwiftObjectAlarm',
 | 
			
		||||
             {}, None),
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(alarms), 1)
 | 
			
		||||
        self.assertEqual(alarms[0].alarm_id, 'alarm-id')
 | 
			
		||||
 | 
			
		||||
    def test_get(self):
 | 
			
		||||
        alarm = self.mgr.get(alarm_id='alarm-id')
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/alarms/alarm-id', {}, None),
 | 
			
		||||
            'GET', '/v2/alarms/alarm-id'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(alarm)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertIsNotNone(alarm)
 | 
			
		||||
        self.assertEqual(alarm.alarm_id, 'alarm-id')
 | 
			
		||||
        self.assertEqual(alarm.rule, alarm.threshold_rule)
 | 
			
		||||
 | 
			
		||||
    def test_create(self):
 | 
			
		||||
        alarm = self.mgr.create(**CREATE_ALARM)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('POST', '/v2/alarms', {}, CREATE_ALARM),
 | 
			
		||||
            'POST', '/v2/alarms'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(alarm)
 | 
			
		||||
        self.http_client.assert_called(*expect, body=CREATE_ALARM)
 | 
			
		||||
        self.assertIsNotNone(alarm)
 | 
			
		||||
 | 
			
		||||
    def test_update(self):
 | 
			
		||||
        alarm = self.mgr.update(alarm_id='alarm-id', **UPDATE_ALARM)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/alarms/alarm-id', {}, None),
 | 
			
		||||
            ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM),
 | 
			
		||||
        expect_get = [
 | 
			
		||||
            'GET', '/v2/alarms/alarm-id'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(alarm)
 | 
			
		||||
        expect_put = [
 | 
			
		||||
            'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect_get, pos=0)
 | 
			
		||||
        self.http_client.assert_called(*expect_put, pos=1)
 | 
			
		||||
        self.assertIsNotNone(alarm)
 | 
			
		||||
        self.assertEqual(alarm.alarm_id, 'alarm-id')
 | 
			
		||||
        for (key, value) in six.iteritems(UPDATED_ALARM):
 | 
			
		||||
            self.assertEqual(getattr(alarm, key), value)
 | 
			
		||||
 | 
			
		||||
    def test_update_delta(self):
 | 
			
		||||
        alarm = self.mgr.update(alarm_id='alarm-id', **DELTA_ALARM)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/alarms/alarm-id', {}, None),
 | 
			
		||||
            ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM),
 | 
			
		||||
        expect_get = [
 | 
			
		||||
            'GET', '/v2/alarms/alarm-id'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(alarm)
 | 
			
		||||
        expect_put = [
 | 
			
		||||
            'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect_get, pos=0)
 | 
			
		||||
        self.http_client.assert_called(*expect_put, pos=1)
 | 
			
		||||
        self.assertIsNotNone(alarm)
 | 
			
		||||
        self.assertEqual(alarm.alarm_id, 'alarm-id')
 | 
			
		||||
        for (key, value) in six.iteritems(UPDATED_ALARM):
 | 
			
		||||
            self.assertEqual(getattr(alarm, key), value)
 | 
			
		||||
@@ -321,31 +345,79 @@ class AlarmManagerTest(testtools.TestCase):
 | 
			
		||||
    def test_set_state(self):
 | 
			
		||||
        state = self.mgr.set_state(alarm_id='alarm-id', state='alarm')
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('PUT', '/v2/alarms/alarm-id/state', {}, 'alarm'),
 | 
			
		||||
            'PUT', '/v2/alarms/alarm-id/state'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertEqual(state, 'alarm')
 | 
			
		||||
        self.http_client.assert_called(*expect, body='alarm')
 | 
			
		||||
        self.assertEqual(state, {'alarm': 'alarm'})
 | 
			
		||||
 | 
			
		||||
    def test_get_state(self):
 | 
			
		||||
        state = self.mgr.get_state(alarm_id='alarm-id')
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/alarms/alarm-id/state', {}, None),
 | 
			
		||||
            'GET', '/v2/alarms/alarm-id/state'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertEqual(state, 'alarm')
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(state, {'alarm': 'alarm'})
 | 
			
		||||
 | 
			
		||||
    def test_delete(self):
 | 
			
		||||
        deleted = self.mgr.delete(alarm_id='victim-id')
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('DELETE', '/v2/alarms/victim-id', {}, None),
 | 
			
		||||
            'DELETE', '/v2/alarms/victim-id'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(deleted is None)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertIsNone(deleted)
 | 
			
		||||
 | 
			
		||||
    def test_get_from_alarm_class(self):
 | 
			
		||||
        alarm = self.mgr.get(alarm_id='alarm-id')
 | 
			
		||||
        self.assertIsNotNone(alarm)
 | 
			
		||||
        alarm.get()
 | 
			
		||||
        expect = [
 | 
			
		||||
            'GET', '/v2/alarms/alarm-id'
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect, pos=0)
 | 
			
		||||
        self.http_client.assert_called(*expect, pos=1)
 | 
			
		||||
        self.assertEqual('alarm-id', alarm.alarm_id)
 | 
			
		||||
        self.assertEqual(alarm.threshold_rule, alarm.rule)
 | 
			
		||||
 | 
			
		||||
    def test_get_state_from_alarm_class(self):
 | 
			
		||||
        alarm = self.mgr.get(alarm_id='alarm-id')
 | 
			
		||||
        self.assertIsNotNone(alarm)
 | 
			
		||||
        state = alarm.get_state()
 | 
			
		||||
        expect_get_1 = [
 | 
			
		||||
            'GET', '/v2/alarms/alarm-id'
 | 
			
		||||
        ]
 | 
			
		||||
        expect_get_2 = [
 | 
			
		||||
            'GET', '/v2/alarms/alarm-id/state'
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect_get_1, pos=0)
 | 
			
		||||
        self.http_client.assert_called(*expect_get_2, pos=1)
 | 
			
		||||
        self.assertEqual('alarm', state)
 | 
			
		||||
 | 
			
		||||
    def test_update_missing(self):
 | 
			
		||||
        alarm = None
 | 
			
		||||
        try:
 | 
			
		||||
            alarm = self.mgr.update(alarm_id='unk-alarm-id', **UPDATE_ALARM)
 | 
			
		||||
        except exc.CommandError:
 | 
			
		||||
            pass
 | 
			
		||||
        self.assertEqual(alarm, None)
 | 
			
		||||
 | 
			
		||||
    def test_delete_from_alarm_class(self):
 | 
			
		||||
        alarm = self.mgr.get(alarm_id='alarm-id')
 | 
			
		||||
        self.assertIsNotNone(alarm)
 | 
			
		||||
        deleted = alarm.delete()
 | 
			
		||||
        expect_get = [
 | 
			
		||||
            'GET', '/v2/alarms/alarm-id'
 | 
			
		||||
        ]
 | 
			
		||||
        expect_delete = [
 | 
			
		||||
            'DELETE', '/v2/alarms/alarm-id'
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect_get, pos=0)
 | 
			
		||||
        self.http_client.assert_called(*expect_delete, pos=1)
 | 
			
		||||
        self.assertIsNone(deleted)
 | 
			
		||||
 | 
			
		||||
    def _do_test_get_history(self, q, url):
 | 
			
		||||
        history = self.mgr.get_history(q=q, alarm_id='alarm-id')
 | 
			
		||||
        expect = [('GET', url, {}, None)]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        expect = ['GET', url]
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        for i in xrange(len(history)):
 | 
			
		||||
            change = history[i]
 | 
			
		||||
            self.assertIsInstance(change, alarms.AlarmChange)
 | 
			
		||||
@@ -367,16 +439,17 @@ class AlarmLegacyManagerTest(testtools.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(AlarmLegacyManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = alarms.AlarmManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_create(self):
 | 
			
		||||
        alarm = self.mgr.create(**CREATE_LEGACY_ALARM)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('POST', '/v2/alarms', {}, CREATE_ALARM_WITHOUT_TC),
 | 
			
		||||
            'POST', '/v2/alarms', CREATE_ALARM_WITHOUT_TC,
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(alarm)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertIsNotNone(alarm)
 | 
			
		||||
 | 
			
		||||
    def test_create_counter_name(self):
 | 
			
		||||
        create = {}
 | 
			
		||||
@@ -385,19 +458,18 @@ class AlarmLegacyManagerTest(testtools.TestCase):
 | 
			
		||||
        del create['meter_name']
 | 
			
		||||
        alarm = self.mgr.create(**create)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('POST', '/v2/alarms', {}, CREATE_ALARM_WITHOUT_TC),
 | 
			
		||||
            'POST', '/v2/alarms', CREATE_ALARM_WITHOUT_TC,
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(alarm)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertIsNotNone(alarm)
 | 
			
		||||
 | 
			
		||||
    def test_update(self):
 | 
			
		||||
        alarm = self.mgr.update(alarm_id='alarm-id', **DELTA_LEGACY_ALARM)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/alarms/alarm-id', {}, None),
 | 
			
		||||
            ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM),
 | 
			
		||||
        expect_put = [
 | 
			
		||||
            'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(alarm)
 | 
			
		||||
        self.http_client.assert_called(*expect_put)
 | 
			
		||||
        self.assertIsNotNone(alarm)
 | 
			
		||||
        self.assertEqual(alarm.alarm_id, 'alarm-id')
 | 
			
		||||
        for (key, value) in six.iteritems(UPDATED_ALARM):
 | 
			
		||||
            self.assertEqual(getattr(alarm, key), value)
 | 
			
		||||
@@ -408,12 +480,11 @@ class AlarmLegacyManagerTest(testtools.TestCase):
 | 
			
		||||
        updated['counter_name'] = UPDATED_LEGACY_ALARM['meter_name']
 | 
			
		||||
        del updated['meter_name']
 | 
			
		||||
        alarm = self.mgr.update(alarm_id='alarm-id', **updated)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/alarms/alarm-id', {}, None),
 | 
			
		||||
            ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM),
 | 
			
		||||
        expect_put = [
 | 
			
		||||
            'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(alarm)
 | 
			
		||||
        self.http_client.assert_called(*expect_put)
 | 
			
		||||
        self.assertIsNotNone(alarm)
 | 
			
		||||
        self.assertEqual(alarm.alarm_id, 'alarm-id')
 | 
			
		||||
        for (key, value) in six.iteritems(UPDATED_ALARM):
 | 
			
		||||
            self.assertEqual(getattr(alarm, key), value)
 | 
			
		||||
@@ -423,7 +494,8 @@ class AlarmTimeConstraintTest(testtools.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(AlarmTimeConstraintTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = alarms.AlarmManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_add_new(self):
 | 
			
		||||
@@ -432,26 +504,37 @@ class AlarmTimeConstraintTest(testtools.TestCase):
 | 
			
		||||
                              duration=500)
 | 
			
		||||
        kwargs = dict(time_constraints=[new_constraint])
 | 
			
		||||
        self.mgr.update(alarm_id='alarm-id', **kwargs)
 | 
			
		||||
        actual = self.api.calls[1][3]['time_constraints']
 | 
			
		||||
        expected = AN_ALARM[u'time_constraints'] + [new_constraint]
 | 
			
		||||
        self.assertEqual(expected, actual)
 | 
			
		||||
        body = copy.deepcopy(AN_ALARM)
 | 
			
		||||
        body[u'time_constraints'] = \
 | 
			
		||||
            AN_ALARM[u'time_constraints'] + [new_constraint]
 | 
			
		||||
        expect = [
 | 
			
		||||
            'PUT', '/v2/alarms/alarm-id', body
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
 | 
			
		||||
    def test_update_existing(self):
 | 
			
		||||
        updated_constraint = dict(name='cons2',
 | 
			
		||||
                                  duration=500)
 | 
			
		||||
        kwargs = dict(time_constraints=[updated_constraint])
 | 
			
		||||
        self.mgr.update(alarm_id='alarm-id', **kwargs)
 | 
			
		||||
        actual = self.api.calls[1][3]['time_constraints']
 | 
			
		||||
        expected = [AN_ALARM[u'time_constraints'][0], dict(name='cons2',
 | 
			
		||||
        body = copy.deepcopy(AN_ALARM)
 | 
			
		||||
        body[u'time_constraints'][1] = dict(name='cons2',
 | 
			
		||||
                                            description='desc2',
 | 
			
		||||
                                            start='0 23 * * *',
 | 
			
		||||
                                            duration=500,
 | 
			
		||||
                                                           timezone='')]
 | 
			
		||||
        self.assertEqual(expected, actual)
 | 
			
		||||
                                            timezone='')
 | 
			
		||||
 | 
			
		||||
        expect = [
 | 
			
		||||
            'PUT', '/v2/alarms/alarm-id', body
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
 | 
			
		||||
    def test_remove(self):
 | 
			
		||||
        kwargs = dict(remove_time_constraints=['cons2'])
 | 
			
		||||
        self.mgr.update(alarm_id='alarm-id', **kwargs)
 | 
			
		||||
        actual = self.api.calls[1][3]['time_constraints']
 | 
			
		||||
        expected = [AN_ALARM[u'time_constraints'][0]]
 | 
			
		||||
        self.assertEqual(expected, actual)
 | 
			
		||||
        body = copy.deepcopy(AN_ALARM)
 | 
			
		||||
        body[u'time_constraints'] = AN_ALARM[u'time_constraints'][:1]
 | 
			
		||||
        expect = [
 | 
			
		||||
            'PUT', '/v2/alarms/alarm-id', body
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
@@ -13,6 +12,9 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v2.event_types
 | 
			
		||||
 | 
			
		||||
@@ -31,15 +33,16 @@ class EventTypesManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(EventTypesManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v2.event_types.EventTypeManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list(self):
 | 
			
		||||
        event_types = list(self.mgr.list())
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/event_types/', {}, None),
 | 
			
		||||
            'GET', '/v2/event_types/'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(event_types), 4)
 | 
			
		||||
        self.assertEqual(event_types[0].event_type, "Foo")
 | 
			
		||||
        self.assertEqual(event_types[1].event_type, "Bar")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
@@ -12,7 +11,8 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v2.events
 | 
			
		||||
 | 
			
		||||
@@ -23,22 +23,22 @@ fixtures = {
 | 
			
		||||
            {},
 | 
			
		||||
            [
 | 
			
		||||
                {
 | 
			
		||||
                    'message_id': '1',
 | 
			
		||||
                    'event_type': 'Foo',
 | 
			
		||||
                    'generated': '1970-01-01T00:00:00',
 | 
			
		||||
                    'traits': {'trait_A': 'abc',
 | 
			
		||||
                               'message_id': '1'},
 | 
			
		||||
                    'traits': {'trait_A': 'abc'},
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    'message_id': '2',
 | 
			
		||||
                    'event_type': 'Foo',
 | 
			
		||||
                    'generated': '1970-01-01T00:00:00',
 | 
			
		||||
                    'traits': {'trait_A': 'def',
 | 
			
		||||
                               'message_id': '2'},
 | 
			
		||||
                    'traits': {'trait_A': 'def'},
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    'message_id': '3',
 | 
			
		||||
                    'event_type': 'Bar',
 | 
			
		||||
                    'generated': '1970-01-01T00:00:00',
 | 
			
		||||
                    'traits': {'trait_B': 'bartrait',
 | 
			
		||||
                               'message_id': '3'},
 | 
			
		||||
                    'traits': {'trait_B': 'bartrait'},
 | 
			
		||||
                },
 | 
			
		||||
            ]
 | 
			
		||||
        ),
 | 
			
		||||
@@ -49,18 +49,18 @@ fixtures = {
 | 
			
		||||
            {},
 | 
			
		||||
            [
 | 
			
		||||
                {
 | 
			
		||||
                    'message_id': '1',
 | 
			
		||||
                    'event_type': 'Foo',
 | 
			
		||||
                    'generated': '1970-01-01T00:00:00',
 | 
			
		||||
                    'traits': {'trait_A': 'abc',
 | 
			
		||||
                               'hostname': 'localhost',
 | 
			
		||||
                               'message_id': '1'},
 | 
			
		||||
                               'hostname': 'localhost'},
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    'message_id': '2',
 | 
			
		||||
                    'event_type': 'Foo',
 | 
			
		||||
                    'generated': '1970-01-01T00:00:00',
 | 
			
		||||
                    'traits': {'trait_A': 'def',
 | 
			
		||||
                               'hostname': 'localhost',
 | 
			
		||||
                               'message_id': '2'},
 | 
			
		||||
                               'hostname': 'localhost'},
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        ),
 | 
			
		||||
@@ -71,18 +71,18 @@ fixtures = {
 | 
			
		||||
            {},
 | 
			
		||||
            [
 | 
			
		||||
                {
 | 
			
		||||
                    'message_id': '1',
 | 
			
		||||
                    'event_type': 'Foo',
 | 
			
		||||
                    'generated': '1970-01-01T00:00:00',
 | 
			
		||||
                    'traits': {'trait_A': 'abc',
 | 
			
		||||
                               'hostname': 'foreignhost',
 | 
			
		||||
                               'message_id': '1'},
 | 
			
		||||
                               'hostname': 'foreignhost'},
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    'message_id': '2',
 | 
			
		||||
                    'event_type': 'Foo',
 | 
			
		||||
                    'generated': '1970-01-01T00:00:00',
 | 
			
		||||
                    'traits': {'trait_A': 'def',
 | 
			
		||||
                               'hostname': 'foreignhost',
 | 
			
		||||
                               'message_id': '2'},
 | 
			
		||||
                               'hostname': 'foreignhost'},
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        ),
 | 
			
		||||
@@ -94,12 +94,12 @@ fixtures = {
 | 
			
		||||
            {},
 | 
			
		||||
            [
 | 
			
		||||
                {
 | 
			
		||||
                    'message_id': '1',
 | 
			
		||||
                    'event_type': 'Bar',
 | 
			
		||||
                    'generated': '1970-01-01T00:00:00',
 | 
			
		||||
                    'traits': {'trait_A': 'abc',
 | 
			
		||||
                               'hostname': 'localhost',
 | 
			
		||||
                               'num_cpus': '5',
 | 
			
		||||
                               'message_id': '1'},
 | 
			
		||||
                               'num_cpus': '5'},
 | 
			
		||||
                },
 | 
			
		||||
            ]
 | 
			
		||||
        ),
 | 
			
		||||
@@ -110,10 +110,10 @@ fixtures = {
 | 
			
		||||
        'GET': (
 | 
			
		||||
            {},
 | 
			
		||||
            {
 | 
			
		||||
                'message_id': '2',
 | 
			
		||||
                'event_type': 'Foo',
 | 
			
		||||
                'generated': '1970-01-01T00:00:00',
 | 
			
		||||
                'traits': {'trait_A': 'def',
 | 
			
		||||
                           'message_id': '2',
 | 
			
		||||
                           'intTrait': '42'},
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
@@ -125,15 +125,16 @@ class EventManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(EventManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v2.events.EventManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list_all(self):
 | 
			
		||||
        events = list(self.mgr.list())
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/events', {}, None),
 | 
			
		||||
            'GET', '/v2/events'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(events), 3)
 | 
			
		||||
        self.assertEqual(events[0].event_type, 'Foo')
 | 
			
		||||
        self.assertEqual(events[1].event_type, 'Foo')
 | 
			
		||||
@@ -142,10 +143,10 @@ class EventManagerTest(utils.BaseTestCase):
 | 
			
		||||
    def test_list_one(self):
 | 
			
		||||
        event = self.mgr.get(2)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/events/2', {}, None),
 | 
			
		||||
            'GET', '/v2/events/2'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(event)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertIsNotNone(event)
 | 
			
		||||
        self.assertEqual(event.event_type, 'Foo')
 | 
			
		||||
 | 
			
		||||
    def test_list_with_query(self):
 | 
			
		||||
@@ -153,11 +154,10 @@ class EventManagerTest(utils.BaseTestCase):
 | 
			
		||||
                                        "value": "localhost",
 | 
			
		||||
                                        "type": "string"}]))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/events?q.field=hostname&q.op=&q.type=string'
 | 
			
		||||
                    '&q.value=localhost',
 | 
			
		||||
             {}, None),
 | 
			
		||||
            'GET', '/v2/events?q.field=hostname&q.op=&q.type=string'
 | 
			
		||||
            '&q.value=localhost'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(events), 2)
 | 
			
		||||
        self.assertEqual(events[0].event_type, 'Foo')
 | 
			
		||||
 | 
			
		||||
@@ -165,11 +165,10 @@ class EventManagerTest(utils.BaseTestCase):
 | 
			
		||||
        events = list(self.mgr.list(q=[{"field": "hostname",
 | 
			
		||||
                                        "value": "foreignhost"}]))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/events?q.field=hostname&q.op='
 | 
			
		||||
                    '&q.type=&q.value=foreignhost',
 | 
			
		||||
             {}, None),
 | 
			
		||||
            'GET', '/v2/events?q.field=hostname&q.op='
 | 
			
		||||
            '&q.type=&q.value=foreignhost'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(events), 2)
 | 
			
		||||
        self.assertEqual(events[0].event_type, 'Foo')
 | 
			
		||||
 | 
			
		||||
@@ -181,9 +180,19 @@ class EventManagerTest(utils.BaseTestCase):
 | 
			
		||||
                                        "type": "integer"}]))
 | 
			
		||||
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/events?q.field=hostname&q.field=num_cpus&q.op=&q.op='
 | 
			
		||||
                    '&q.type=&q.type=integer&q.value=localhost&q.value=5',
 | 
			
		||||
             {}, None),
 | 
			
		||||
            'GET', '/v2/events?q.field=hostname&q.field=num_cpus&q.op=&q.op='
 | 
			
		||||
            '&q.type=&q.type=integer&q.value=localhost&q.value=5'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(events), 1)
 | 
			
		||||
 | 
			
		||||
    def test_get_from_event_class(self):
 | 
			
		||||
        event = self.mgr.get(2)
 | 
			
		||||
        self.assertIsNotNone(event)
 | 
			
		||||
        event.get()
 | 
			
		||||
        expect = [
 | 
			
		||||
            'GET', '/v2/events/2'
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect, pos=0)
 | 
			
		||||
        self.http_client.assert_called(*expect, pos=1)
 | 
			
		||||
        self.assertEqual('Foo', event.event_type)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
from ceilometerclient.v2 import options
 | 
			
		||||
 | 
			
		||||
@@ -83,9 +82,57 @@ class CliTest(utils.BaseTestCase):
 | 
			
		||||
                               'op': 'le', 'value': '283.347',
 | 
			
		||||
                               'type': ''}])
 | 
			
		||||
 | 
			
		||||
    def test_invalid_seperator(self):
 | 
			
		||||
        self.assertRaises(ValueError, options.cli_to_array,
 | 
			
		||||
                          'this=2.4,fooo=doof')
 | 
			
		||||
    def test_comma(self):
 | 
			
		||||
        ar = options.cli_to_array('this=2.4,fooo=doof')
 | 
			
		||||
        self.assertEqual([{'field': 'this',
 | 
			
		||||
                           'op': 'eq',
 | 
			
		||||
                           'value': '2.4,fooo=doof',
 | 
			
		||||
                           'type': ''}],
 | 
			
		||||
                         ar)
 | 
			
		||||
 | 
			
		||||
    def test_special_character(self):
 | 
			
		||||
        ar = options.cli_to_array('key~123=value!123')
 | 
			
		||||
        self.assertEqual([{'field': 'key~123',
 | 
			
		||||
                           'op': 'eq',
 | 
			
		||||
                           'value': 'value!123',
 | 
			
		||||
                           'type': ''}],
 | 
			
		||||
                         ar)
 | 
			
		||||
 | 
			
		||||
    def _do_test_typed_float_op(self, op, op_str):
 | 
			
		||||
        ar = options.cli_to_array('that%sfloat::283.347' % op)
 | 
			
		||||
        self.assertEqual([{'field': 'that',
 | 
			
		||||
                           'type': 'float',
 | 
			
		||||
                           'value': '283.347',
 | 
			
		||||
                           'op': op_str}],
 | 
			
		||||
                         ar)
 | 
			
		||||
 | 
			
		||||
    def test_typed_float_eq(self):
 | 
			
		||||
        self._do_test_typed_float_op('<', 'lt')
 | 
			
		||||
 | 
			
		||||
    def test_typed_float_le(self):
 | 
			
		||||
        self._do_test_typed_float_op('<=', 'le')
 | 
			
		||||
 | 
			
		||||
    def test_typed_string_whitespace(self):
 | 
			
		||||
        ar = options.cli_to_array('state=string::insufficient data')
 | 
			
		||||
        self.assertEqual([{'field': 'state',
 | 
			
		||||
                           'op': 'eq',
 | 
			
		||||
                           'type': 'string',
 | 
			
		||||
                           'value': 'insufficient data'}],
 | 
			
		||||
                         ar)
 | 
			
		||||
 | 
			
		||||
    def test_typed_string_whitespace_complex(self):
 | 
			
		||||
        ar = options.cli_to_array(
 | 
			
		||||
            'that>=float::99.9999;state=string::insufficient data'
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual([{'field': 'that',
 | 
			
		||||
                           'op': 'ge',
 | 
			
		||||
                           'type': 'float',
 | 
			
		||||
                           'value': '99.9999'},
 | 
			
		||||
                          {'field': 'state',
 | 
			
		||||
                           'op': 'eq',
 | 
			
		||||
                           'type': 'string',
 | 
			
		||||
                           'value': 'insufficient data'}],
 | 
			
		||||
                         ar)
 | 
			
		||||
 | 
			
		||||
    def test_invalid_operator(self):
 | 
			
		||||
        self.assertRaises(ValueError, options.cli_to_array,
 | 
			
		||||
@@ -97,6 +144,22 @@ class CliTest(utils.BaseTestCase):
 | 
			
		||||
                               'op': 'le', 'value': '34',
 | 
			
		||||
                               'type': ''}])
 | 
			
		||||
 | 
			
		||||
    def test_single_char_field_or_value(self):
 | 
			
		||||
        ar = options.cli_to_array('m<=34;large.thing>s;x!=y')
 | 
			
		||||
        self.assertEqual([{'field': 'm',
 | 
			
		||||
                           'op': 'le',
 | 
			
		||||
                           'value': '34',
 | 
			
		||||
                           'type': ''},
 | 
			
		||||
                          {'field': 'large.thing',
 | 
			
		||||
                           'op': 'gt',
 | 
			
		||||
                           'value': 's',
 | 
			
		||||
                           'type': ''},
 | 
			
		||||
                          {'field': 'x',
 | 
			
		||||
                           'op': 'ne',
 | 
			
		||||
                           'value': 'y',
 | 
			
		||||
                           'type': ''}],
 | 
			
		||||
                         ar)
 | 
			
		||||
 | 
			
		||||
    def test_without_data_type(self):
 | 
			
		||||
        ar = options.cli_to_array('hostname=localhost')
 | 
			
		||||
        self.assertEqual(ar, [{'field': 'hostname',
 | 
			
		||||
@@ -152,3 +215,25 @@ class CliTest(utils.BaseTestCase):
 | 
			
		||||
                               'op': 'eq',
 | 
			
		||||
                               'type': '',
 | 
			
		||||
                               'value': 'datetime:sometimestamp'}])
 | 
			
		||||
 | 
			
		||||
    def test_missing_key(self):
 | 
			
		||||
        self.assertRaises(ValueError, options.cli_to_array,
 | 
			
		||||
                          'average=float::1234.0;>=string::hello')
 | 
			
		||||
 | 
			
		||||
    def test_missing_value(self):
 | 
			
		||||
        self.assertRaises(ValueError, options.cli_to_array,
 | 
			
		||||
                          'average=float::1234.0;house>=')
 | 
			
		||||
 | 
			
		||||
    def test_timestamp_value(self):
 | 
			
		||||
        ar = options.cli_to_array(
 | 
			
		||||
            'project=cow;timestamp>=datetime::2014-03-11T16:02:58'
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual([{'field': 'project',
 | 
			
		||||
                           'op': 'eq',
 | 
			
		||||
                           'type': '',
 | 
			
		||||
                           'value': 'cow'},
 | 
			
		||||
                          {'field': 'timestamp',
 | 
			
		||||
                           'op': 'ge',
 | 
			
		||||
                           'type': 'datetime',
 | 
			
		||||
                           'value': '2014-03-11T16:02:58'}],
 | 
			
		||||
                         ar)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,8 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
from ceilometerclient.v2 import query
 | 
			
		||||
 | 
			
		||||
@@ -49,13 +51,16 @@ class QueryAlarmsManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(QueryAlarmsManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = query.QueryAlarmHistoryManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_query(self):
 | 
			
		||||
        alarm_history = self.mgr.query(**QUERY)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('POST', '/v2/query/alarms/history', {}, QUERY),
 | 
			
		||||
 | 
			
		||||
            'POST', '/v2/query/alarms/history', QUERY,
 | 
			
		||||
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(expect, self.api.calls)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(1, len(alarm_history))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
# Copyright Ericsson AB 2014. All rights reserved
 | 
			
		||||
#
 | 
			
		||||
# Author: Balazs Gibizer <balazs.gibizer@ericsson.com>
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
@@ -14,6 +12,8 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
from ceilometerclient.v2 import query
 | 
			
		||||
 | 
			
		||||
@@ -60,13 +60,15 @@ class QueryAlarmsManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(QueryAlarmsManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = query.QueryAlarmsManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_query(self):
 | 
			
		||||
        alarms = self.mgr.query(**QUERY)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('POST', '/v2/query/alarms', {}, QUERY),
 | 
			
		||||
            'POST', '/v2/query/alarms', QUERY,
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(expect, self.api.calls)
 | 
			
		||||
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(1, len(alarms))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
# Copyright Ericsson AB 2014. All rights reserved
 | 
			
		||||
#
 | 
			
		||||
# Author: Balazs Gibizer <balazs.gibizer@ericsson.com>
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
@@ -14,6 +12,8 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
from ceilometerclient.v2 import query
 | 
			
		||||
 | 
			
		||||
@@ -53,13 +53,15 @@ class QuerySamplesManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(QuerySamplesManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = query.QuerySamplesManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_query(self):
 | 
			
		||||
        samples = self.mgr.query(**QUERY)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('POST', '/v2/query/samples', {}, QUERY),
 | 
			
		||||
 | 
			
		||||
            'POST', '/v2/query/samples', QUERY,
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(expect, self.api.calls)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(1, len(samples))
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,8 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v2.resources
 | 
			
		||||
 | 
			
		||||
@@ -70,15 +71,16 @@ class ResourceManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(ResourceManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v2.resources.ResourceManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list_all(self):
 | 
			
		||||
        resources = list(self.mgr.list())
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/resources', {}, None),
 | 
			
		||||
            'GET', '/v2/resources'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 2)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'a')
 | 
			
		||||
        self.assertEqual(resources[1].resource_id, 'b')
 | 
			
		||||
@@ -86,10 +88,10 @@ class ResourceManagerTest(utils.BaseTestCase):
 | 
			
		||||
    def test_list_one(self):
 | 
			
		||||
        resource = self.mgr.get(resource_id='a')
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/resources/a', {}, None),
 | 
			
		||||
            'GET', '/v2/resources/a'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(resource)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertIsNotNone(resource)
 | 
			
		||||
        self.assertEqual(resource.resource_id, 'a')
 | 
			
		||||
 | 
			
		||||
    def test_list_by_query(self):
 | 
			
		||||
@@ -97,10 +99,20 @@ class ResourceManagerTest(utils.BaseTestCase):
 | 
			
		||||
                                           "value": "a"},
 | 
			
		||||
                                          ]))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/resources?q.field=resource_id&q.op='
 | 
			
		||||
                    '&q.type=&q.value=a',
 | 
			
		||||
             {}, None),
 | 
			
		||||
            'GET', '/v2/resources?q.field=resource_id&q.op='
 | 
			
		||||
            '&q.type=&q.value=a'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(resources), 1)
 | 
			
		||||
        self.assertEqual(resources[0].resource_id, 'a')
 | 
			
		||||
 | 
			
		||||
    def test_get_from_resource_class(self):
 | 
			
		||||
        resource = self.mgr.get(resource_id='a')
 | 
			
		||||
        self.assertIsNotNone(resource)
 | 
			
		||||
        resource.get()
 | 
			
		||||
        expect = [
 | 
			
		||||
            'GET', '/v2/resources/a'
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect, pos=0)
 | 
			
		||||
        self.http_client.assert_called(*expect, pos=1)
 | 
			
		||||
        self.assertEqual('a', resource.resource_id)
 | 
			
		||||
 
 | 
			
		||||
@@ -15,10 +15,12 @@
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v2.samples
 | 
			
		||||
 | 
			
		||||
GET_SAMPLE = {u'counter_name': u'instance',
 | 
			
		||||
GET_OLD_SAMPLE = {u'counter_name': u'instance',
 | 
			
		||||
                  u'user_id': u'user-id',
 | 
			
		||||
                  u'resource_id': u'resource-id',
 | 
			
		||||
                  u'timestamp': u'2012-07-02T10:40:00',
 | 
			
		||||
@@ -30,56 +32,98 @@ GET_SAMPLE = {u'counter_name': u'instance',
 | 
			
		||||
                  u'resource_metadata': {u'tag': u'self.counter',
 | 
			
		||||
                                         u'display_name': u'test-server'},
 | 
			
		||||
                  u'counter_type': u'cumulative'}
 | 
			
		||||
CREATE_SAMPLE = copy.deepcopy(GET_SAMPLE)
 | 
			
		||||
CREATE_SAMPLE = copy.deepcopy(GET_OLD_SAMPLE)
 | 
			
		||||
del CREATE_SAMPLE['message_id']
 | 
			
		||||
del CREATE_SAMPLE['source']
 | 
			
		||||
 | 
			
		||||
base_url = '/v2/meters/instance'
 | 
			
		||||
args = ('q.field=resource_id&q.field=source&q.op=&q.op='
 | 
			
		||||
GET_SAMPLE = {
 | 
			
		||||
    "user_id": None,
 | 
			
		||||
    "resource_id": "9b651dfd-7d30-402b-972e-212b2c4bfb05",
 | 
			
		||||
    "timestamp": "2014-11-03T13:37:46",
 | 
			
		||||
    "meter": "image",
 | 
			
		||||
    "volume": 1.0,
 | 
			
		||||
    "source": "openstack",
 | 
			
		||||
    "recorded_at": "2014-11-03T13:37:46.994458",
 | 
			
		||||
    "project_id": "2cc3a7bb859b4bacbeab0aa9ca673033",
 | 
			
		||||
    "type": "gauge",
 | 
			
		||||
    "id": "98b5f258-635e-11e4-8bdd-0025647390c1",
 | 
			
		||||
    "unit": "image",
 | 
			
		||||
    "resource_metadata": {},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
METER_URL = '/v2/meters/instance'
 | 
			
		||||
SAMPLE_URL = '/v2/samples'
 | 
			
		||||
QUERIES = ('q.field=resource_id&q.field=source&q.op=&q.op='
 | 
			
		||||
           '&q.type=&q.type=&q.value=foo&q.value=bar')
 | 
			
		||||
args_limit = 'limit=1'
 | 
			
		||||
fixtures = {
 | 
			
		||||
    base_url:
 | 
			
		||||
    {
 | 
			
		||||
LIMIT = 'limit=1'
 | 
			
		||||
 | 
			
		||||
OLD_SAMPLE_FIXTURES = {
 | 
			
		||||
    METER_URL: {
 | 
			
		||||
        'GET': (
 | 
			
		||||
            {},
 | 
			
		||||
            [GET_SAMPLE]
 | 
			
		||||
            [GET_OLD_SAMPLE]
 | 
			
		||||
        ),
 | 
			
		||||
        'POST': (
 | 
			
		||||
            {},
 | 
			
		||||
            [CREATE_SAMPLE],
 | 
			
		||||
        ),
 | 
			
		||||
    },
 | 
			
		||||
    '%s?%s' % (base_url, args):
 | 
			
		||||
    {
 | 
			
		||||
    '%s?%s' % (METER_URL, QUERIES): {
 | 
			
		||||
        'GET': (
 | 
			
		||||
            {},
 | 
			
		||||
            [],
 | 
			
		||||
        ),
 | 
			
		||||
    },
 | 
			
		||||
    '%s?%s' % (base_url, args_limit):
 | 
			
		||||
    {
 | 
			
		||||
    '%s?%s' % (METER_URL, LIMIT): {
 | 
			
		||||
        'GET': (
 | 
			
		||||
            {},
 | 
			
		||||
            [GET_SAMPLE]
 | 
			
		||||
            [GET_OLD_SAMPLE]
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
SAMPLE_FIXTURES = {
 | 
			
		||||
    SAMPLE_URL: {
 | 
			
		||||
        'GET': (
 | 
			
		||||
            (),
 | 
			
		||||
            [GET_SAMPLE]
 | 
			
		||||
        ),
 | 
			
		||||
    },
 | 
			
		||||
    '%s?%s' % (SAMPLE_URL, QUERIES): {
 | 
			
		||||
        'GET': (
 | 
			
		||||
            {},
 | 
			
		||||
            [],
 | 
			
		||||
        ),
 | 
			
		||||
    },
 | 
			
		||||
    '%s?%s' % (SAMPLE_URL, LIMIT): {
 | 
			
		||||
        'GET': (
 | 
			
		||||
            {},
 | 
			
		||||
            [GET_SAMPLE],
 | 
			
		||||
        ),
 | 
			
		||||
    },
 | 
			
		||||
    '%s/%s' % (SAMPLE_URL, GET_SAMPLE['id']): {
 | 
			
		||||
        'GET': (
 | 
			
		||||
            {},
 | 
			
		||||
            GET_SAMPLE,
 | 
			
		||||
        ),
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SampleManagerTest(utils.BaseTestCase):
 | 
			
		||||
class OldSampleManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(SampleManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.mgr = ceilometerclient.v2.samples.SampleManager(self.api)
 | 
			
		||||
        super(OldSampleManagerTest, self).setUp()
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(
 | 
			
		||||
            fixtures=OLD_SAMPLE_FIXTURES)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v2.samples.OldSampleManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list_by_meter_name(self):
 | 
			
		||||
        samples = list(self.mgr.list(meter_name='instance'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/meters/instance', {}, None),
 | 
			
		||||
            'GET', '/v2/meters/instance'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(samples), 1)
 | 
			
		||||
        self.assertEqual(samples[0].resource_id, 'resource-id')
 | 
			
		||||
 | 
			
		||||
@@ -91,20 +135,64 @@ class SampleManagerTest(utils.BaseTestCase):
 | 
			
		||||
                                         {"field": "source",
 | 
			
		||||
                                          "value": "bar"},
 | 
			
		||||
                                     ]))
 | 
			
		||||
        expect = [('GET', '%s?%s' % (base_url, args), {}, None)]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        expect = ['GET', '%s?%s' % (METER_URL, QUERIES)]
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(samples), 0)
 | 
			
		||||
 | 
			
		||||
    def test_create(self):
 | 
			
		||||
        sample = self.mgr.create(**CREATE_SAMPLE)
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('POST', '/v2/meters/instance', {}, [CREATE_SAMPLE]),
 | 
			
		||||
            'POST', '/v2/meters/instance'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.assertTrue(sample)
 | 
			
		||||
        self.http_client.assert_called(*expect, body=[CREATE_SAMPLE])
 | 
			
		||||
        self.assertIsNotNone(sample)
 | 
			
		||||
 | 
			
		||||
    def test_limit(self):
 | 
			
		||||
        samples = list(self.mgr.list(meter_name='instance', limit=1))
 | 
			
		||||
        expect = [('GET', '/v2/meters/instance?limit=1', {}, None)]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        expect = ['GET', '/v2/meters/instance?limit=1']
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(samples), 1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SampleManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(SampleManagerTest, self).setUp()
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(
 | 
			
		||||
            fixtures=SAMPLE_FIXTURES)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v2.samples.SampleManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_sample_list(self):
 | 
			
		||||
        samples = list(self.mgr.list())
 | 
			
		||||
        expect = [
 | 
			
		||||
            'GET', '/v2/samples'
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(1, len(samples))
 | 
			
		||||
        self.assertEqual('9b651dfd-7d30-402b-972e-212b2c4bfb05',
 | 
			
		||||
                         samples[0].resource_id)
 | 
			
		||||
 | 
			
		||||
    def test_sample_list_with_queries(self):
 | 
			
		||||
        queries = [
 | 
			
		||||
            {"field": "resource_id",
 | 
			
		||||
             "value": "foo"},
 | 
			
		||||
            {"field": "source",
 | 
			
		||||
             "value": "bar"},
 | 
			
		||||
        ]
 | 
			
		||||
        samples = list(self.mgr.list(q=queries))
 | 
			
		||||
        expect = ['GET', '%s?%s' % (SAMPLE_URL, QUERIES)]
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(0, len(samples))
 | 
			
		||||
 | 
			
		||||
    def test_sample_list_with_limit(self):
 | 
			
		||||
        samples = list(self.mgr.list(limit=1))
 | 
			
		||||
        expect = ['GET', '/v2/samples?limit=1']
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(1, len(samples))
 | 
			
		||||
 | 
			
		||||
    def test_sample_get(self):
 | 
			
		||||
        sample = self.mgr.get(GET_SAMPLE['id'])
 | 
			
		||||
        expect = ['GET', '/v2/samples/' + GET_SAMPLE['id']]
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(GET_SAMPLE, sample.to_dict())
 | 
			
		||||
 
 | 
			
		||||
@@ -15,16 +15,18 @@
 | 
			
		||||
#   License for the specific language governing permissions and limitations
 | 
			
		||||
#   under the License.
 | 
			
		||||
 | 
			
		||||
import mock
 | 
			
		||||
import re
 | 
			
		||||
import six
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
import mock
 | 
			
		||||
import six
 | 
			
		||||
from testtools import matchers
 | 
			
		||||
 | 
			
		||||
from ceilometerclient import exc
 | 
			
		||||
from ceilometerclient import shell as base_shell
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
from ceilometerclient.v2 import alarms
 | 
			
		||||
from ceilometerclient.v2 import events
 | 
			
		||||
from ceilometerclient.v2 import samples
 | 
			
		||||
from ceilometerclient.v2 import shell as ceilometer_shell
 | 
			
		||||
from ceilometerclient.v2 import statistics
 | 
			
		||||
@@ -107,15 +109,13 @@ class ShellAlarmHistoryCommandTest(utils.BaseTestCase):
 | 
			
		||||
        self.args = mock.Mock()
 | 
			
		||||
        self.args.alarm_id = self.ALARM_ID
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def _do_test_alarm_history(self, raw_query=None, parsed_query=None):
 | 
			
		||||
        self.args.query = raw_query
 | 
			
		||||
        orig = sys.stdout
 | 
			
		||||
        sys.stdout = six.StringIO()
 | 
			
		||||
        history = [alarms.AlarmChange(mock.Mock(), change)
 | 
			
		||||
                   for change in self.ALARM_HISTORY]
 | 
			
		||||
        self.cc.alarms.get_history.return_value = history
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
        ceilometer_shell.do_alarm_history(self.cc, self.args)
 | 
			
		||||
        self.cc.alarms.get_history.assert_called_once_with(
 | 
			
		||||
            q=parsed_query,
 | 
			
		||||
@@ -130,9 +130,6 @@ class ShellAlarmHistoryCommandTest(utils.BaseTestCase):
 | 
			
		||||
        ]
 | 
			
		||||
        for r in required:
 | 
			
		||||
            self.assertThat(out, matchers.MatchesRegex(r, re.DOTALL))
 | 
			
		||||
        finally:
 | 
			
		||||
            sys.stdout.close()
 | 
			
		||||
            sys.stdout = orig
 | 
			
		||||
 | 
			
		||||
    def test_alarm_all_history(self):
 | 
			
		||||
        self._do_test_alarm_history()
 | 
			
		||||
@@ -176,6 +173,7 @@ class ShellAlarmCommandTest(utils.BaseTestCase):
 | 
			
		||||
                                   "timezone": ""}],
 | 
			
		||||
             "alarm_id": ALARM_ID,
 | 
			
		||||
             "state": "insufficient data",
 | 
			
		||||
             "severity": "low",
 | 
			
		||||
             "insufficient_data_actions": [],
 | 
			
		||||
             "repeat_actions": True,
 | 
			
		||||
             "user_id": "528d9b68fa774689834b5c04b4564f8a",
 | 
			
		||||
@@ -183,6 +181,20 @@ class ShellAlarmCommandTest(utils.BaseTestCase):
 | 
			
		||||
             "type": "threshold",
 | 
			
		||||
             "name": "cpu_high"}
 | 
			
		||||
 | 
			
		||||
    THRESHOLD_ALARM_CLI_ARGS = [
 | 
			
		||||
        '--name', 'cpu_high',
 | 
			
		||||
        '--description', 'instance running hot',
 | 
			
		||||
        '--meter-name', 'cpu_util',
 | 
			
		||||
        '--threshold', '70.0',
 | 
			
		||||
        '--comparison-operator', 'gt',
 | 
			
		||||
        '--statistic', 'avg',
 | 
			
		||||
        '--period', '600',
 | 
			
		||||
        '--evaluation-periods', '3',
 | 
			
		||||
        '--alarm-action', 'log://',
 | 
			
		||||
        '--alarm-action', 'http://example.com/alarm/state',
 | 
			
		||||
        '--query', 'resource_id=INSTANCE_ID'
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(ShellAlarmCommandTest, self).setUp()
 | 
			
		||||
        self.cc = mock.Mock()
 | 
			
		||||
@@ -190,17 +202,15 @@ class ShellAlarmCommandTest(utils.BaseTestCase):
 | 
			
		||||
        self.args = mock.Mock()
 | 
			
		||||
        self.args.alarm_id = self.ALARM_ID
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def _do_test_alarm_update_repeat_actions(self, method, repeat_actions):
 | 
			
		||||
        self.args.threshold = 42.0
 | 
			
		||||
        if repeat_actions is not None:
 | 
			
		||||
            self.args.repeat_actions = repeat_actions
 | 
			
		||||
        orig = sys.stdout
 | 
			
		||||
        sys.stdout = six.StringIO()
 | 
			
		||||
        alarm = [alarms.Alarm(mock.Mock(), self.ALARM)]
 | 
			
		||||
        self.cc.alarms.get.return_value = alarm
 | 
			
		||||
        self.cc.alarms.update.return_value = alarm[0]
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
        method(self.cc, self.args)
 | 
			
		||||
        args, kwargs = self.cc.alarms.update.call_args
 | 
			
		||||
        self.assertEqual(self.ALARM_ID, args[0])
 | 
			
		||||
@@ -208,10 +218,7 @@ class ShellAlarmCommandTest(utils.BaseTestCase):
 | 
			
		||||
        if repeat_actions is not None:
 | 
			
		||||
            self.assertEqual(repeat_actions, kwargs.get('repeat_actions'))
 | 
			
		||||
        else:
 | 
			
		||||
                self.assertFalse('repeat_actions' in kwargs)
 | 
			
		||||
        finally:
 | 
			
		||||
            sys.stdout.close()
 | 
			
		||||
            sys.stdout = orig
 | 
			
		||||
            self.assertNotIn('repeat_actions', kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_alarm_update_repeat_actions_untouched(self):
 | 
			
		||||
        method = ceilometer_shell.do_alarm_update
 | 
			
		||||
@@ -249,35 +256,34 @@ class ShellAlarmCommandTest(utils.BaseTestCase):
 | 
			
		||||
        method = ceilometer_shell.do_alarm_threshold_update
 | 
			
		||||
        self._do_test_alarm_update_repeat_actions(method, False)
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_alarm_threshold_create_args(self):
 | 
			
		||||
        argv = ['alarm-threshold-create'] + self.THRESHOLD_ALARM_CLI_ARGS
 | 
			
		||||
        self._test_alarm_threshold_action_args('create', argv)
 | 
			
		||||
 | 
			
		||||
    def test_alarm_threshold_update_args(self):
 | 
			
		||||
        argv = ['alarm-threshold-update', 'x'] + self.THRESHOLD_ALARM_CLI_ARGS
 | 
			
		||||
        self._test_alarm_threshold_action_args('update', argv)
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def _test_alarm_threshold_action_args(self, action, argv):
 | 
			
		||||
        shell = base_shell.CeilometerShell()
 | 
			
		||||
        argv = ['alarm-threshold-create',
 | 
			
		||||
                '--name', 'cpu_high',
 | 
			
		||||
                '--description', 'instance running hot',
 | 
			
		||||
                '--meter-name', 'cpu_util',
 | 
			
		||||
                '--threshold', '70.0',
 | 
			
		||||
                '--comparison-operator', 'gt',
 | 
			
		||||
                '--statistic', 'avg',
 | 
			
		||||
                '--period', '600',
 | 
			
		||||
                '--evaluation-periods', '3',
 | 
			
		||||
                '--alarm-action', 'log://',
 | 
			
		||||
                '--alarm-action', 'http://example.com/alarm/state',
 | 
			
		||||
                '--query', 'resource_id=INSTANCE_ID']
 | 
			
		||||
        _, args = shell.parse_args(argv)
 | 
			
		||||
 | 
			
		||||
        orig = sys.stdout
 | 
			
		||||
        sys.stdout = six.StringIO()
 | 
			
		||||
        alarm = alarms.Alarm(mock.Mock(), self.ALARM)
 | 
			
		||||
        self.cc.alarms.create.return_value = alarm
 | 
			
		||||
        getattr(self.cc.alarms, action).return_value = alarm
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            ceilometer_shell.do_alarm_threshold_create(self.cc, args)
 | 
			
		||||
            _, kwargs = self.cc.alarms.create.call_args
 | 
			
		||||
        func = getattr(ceilometer_shell, 'do_alarm_threshold_' + action)
 | 
			
		||||
        func(self.cc, args)
 | 
			
		||||
        _, kwargs = getattr(self.cc.alarms, action).call_args
 | 
			
		||||
        self._check_alarm_threshold_args(kwargs)
 | 
			
		||||
 | 
			
		||||
    def _check_alarm_threshold_args(self, kwargs):
 | 
			
		||||
        self.assertEqual('cpu_high', kwargs.get('name'))
 | 
			
		||||
        self.assertEqual('instance running hot', kwargs.get('description'))
 | 
			
		||||
        actions = ['log://', 'http://example.com/alarm/state']
 | 
			
		||||
        self.assertEqual(actions, kwargs.get('alarm_actions'))
 | 
			
		||||
            self.assertTrue('threshold_rule' in kwargs)
 | 
			
		||||
        self.assertIn('threshold_rule', kwargs)
 | 
			
		||||
        rule = kwargs['threshold_rule']
 | 
			
		||||
        self.assertEqual('cpu_util', rule.get('meter_name'))
 | 
			
		||||
        self.assertEqual(70.0, rule.get('threshold'))
 | 
			
		||||
@@ -288,10 +294,8 @@ class ShellAlarmCommandTest(utils.BaseTestCase):
 | 
			
		||||
        query = dict(field='resource_id', type='',
 | 
			
		||||
                     value='INSTANCE_ID', op='eq')
 | 
			
		||||
        self.assertEqual([query], rule['query'])
 | 
			
		||||
        finally:
 | 
			
		||||
            sys.stdout.close()
 | 
			
		||||
            sys.stdout = orig
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_alarm_create_time_constraints(self):
 | 
			
		||||
        shell = base_shell.CeilometerShell()
 | 
			
		||||
        argv = ['alarm-threshold-create',
 | 
			
		||||
@@ -305,12 +309,9 @@ class ShellAlarmCommandTest(utils.BaseTestCase):
 | 
			
		||||
                ]
 | 
			
		||||
        _, args = shell.parse_args(argv)
 | 
			
		||||
 | 
			
		||||
        orig = sys.stdout
 | 
			
		||||
        sys.stdout = six.StringIO()
 | 
			
		||||
        alarm = alarms.Alarm(mock.Mock(), self.ALARM)
 | 
			
		||||
        self.cc.alarms.create.return_value = alarm
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
        ceilometer_shell.do_alarm_threshold_create(self.cc, args)
 | 
			
		||||
        _, kwargs = self.cc.alarms.create.call_args
 | 
			
		||||
        time_constraints = [dict(name='cons1', start='0 11 * * *',
 | 
			
		||||
@@ -318,65 +319,86 @@ class ShellAlarmCommandTest(utils.BaseTestCase):
 | 
			
		||||
                            dict(name='cons2', start='0 23 * * *',
 | 
			
		||||
                                 duration='600')]
 | 
			
		||||
        self.assertEqual(time_constraints, kwargs['time_constraints'])
 | 
			
		||||
        finally:
 | 
			
		||||
            sys.stdout.close()
 | 
			
		||||
            sys.stdout = orig
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellSampleListCommandTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    METER = 'cpu_util'
 | 
			
		||||
    SAMPLES = [{"counter_name": "cpu_util",
 | 
			
		||||
                "resource_id": "5dcf5537-3161-4e25-9235-407e1385bd35",
 | 
			
		||||
                "timestamp": "2013-10-15T05:50:30",
 | 
			
		||||
                "counter_unit": "%",
 | 
			
		||||
                "counter_volume": 0.261666666667,
 | 
			
		||||
                "counter_type": "gauge"},
 | 
			
		||||
               {"counter_name": "cpu_util",
 | 
			
		||||
                "resource_id": "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f",
 | 
			
		||||
                "timestamp": "2013-10-15T05:50:29",
 | 
			
		||||
                "counter_unit": "%",
 | 
			
		||||
                "counter_volume": 0.261666666667,
 | 
			
		||||
                "counter_type": "gauge"},
 | 
			
		||||
               {"counter_name": "cpu_util",
 | 
			
		||||
                "resource_id": "5dcf5537-3161-4e25-9235-407e1385bd35",
 | 
			
		||||
                "timestamp": "2013-10-15T05:40:30",
 | 
			
		||||
                "counter_unit": "%",
 | 
			
		||||
                "counter_volume": 0.251247920133,
 | 
			
		||||
                "counter_type": "gauge"},
 | 
			
		||||
               {"counter_name": "cpu_util",
 | 
			
		||||
                "resource_id": "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f",
 | 
			
		||||
                "timestamp": "2013-10-15T05:40:29",
 | 
			
		||||
                "counter_unit": "%",
 | 
			
		||||
                "counter_volume": 0.26,
 | 
			
		||||
                "counter_type": "gauge"}]
 | 
			
		||||
    SAMPLE_VALUES = (
 | 
			
		||||
        ("cpu_util",
 | 
			
		||||
         "5dcf5537-3161-4e25-9235-407e1385bd35",
 | 
			
		||||
         "2013-10-15T05:50:30",
 | 
			
		||||
         "%",
 | 
			
		||||
         0.261666666667,
 | 
			
		||||
         "gauge",
 | 
			
		||||
         "86536501-b2c9-48f6-9c6a-7a5b14ba7482"),
 | 
			
		||||
        ("cpu_util",
 | 
			
		||||
         "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f",
 | 
			
		||||
         "2013-10-15T05:50:29",
 | 
			
		||||
         "%",
 | 
			
		||||
         0.261666666667,
 | 
			
		||||
         "gauge",
 | 
			
		||||
         "fe2a91ec-602b-4b55-8cba-5302ce3b916e",),
 | 
			
		||||
        ("cpu_util",
 | 
			
		||||
         "5dcf5537-3161-4e25-9235-407e1385bd35",
 | 
			
		||||
         "2013-10-15T05:40:30",
 | 
			
		||||
         "%",
 | 
			
		||||
         0.251247920133,
 | 
			
		||||
         "gauge",
 | 
			
		||||
         "52768bcb-b4e9-4db9-a30c-738c758b6f43"),
 | 
			
		||||
        ("cpu_util",
 | 
			
		||||
         "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f",
 | 
			
		||||
         "2013-10-15T05:40:29",
 | 
			
		||||
         "%",
 | 
			
		||||
         0.26,
 | 
			
		||||
         "gauge",
 | 
			
		||||
         "31ae614a-ac6b-4fb9-b106-4667bae03308"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    OLD_SAMPLES = [
 | 
			
		||||
        dict(counter_name=s[0],
 | 
			
		||||
             resource_id=s[1],
 | 
			
		||||
             timestamp=s[2],
 | 
			
		||||
             counter_unit=s[3],
 | 
			
		||||
             counter_volume=s[4],
 | 
			
		||||
             counter_type=s[5])
 | 
			
		||||
        for s in SAMPLE_VALUES
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    SAMPLES = [
 | 
			
		||||
        dict(meter=s[0],
 | 
			
		||||
             resource_id=s[1],
 | 
			
		||||
             timestamp=s[2],
 | 
			
		||||
             unit=s[3],
 | 
			
		||||
             volume=s[4],
 | 
			
		||||
             type=s[5],
 | 
			
		||||
             id=s[6])
 | 
			
		||||
        for s in SAMPLE_VALUES
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(ShellSampleListCommandTest, self).setUp()
 | 
			
		||||
        self.cc = mock.Mock()
 | 
			
		||||
        self.cc.samples = mock.Mock()
 | 
			
		||||
        self.cc.new_samples = mock.Mock()
 | 
			
		||||
        self.args = mock.Mock()
 | 
			
		||||
        self.args.meter = self.METER
 | 
			
		||||
        self.args.query = None
 | 
			
		||||
        self.args.limit = None
 | 
			
		||||
 | 
			
		||||
    def test_sample_list(self):
 | 
			
		||||
 | 
			
		||||
        sample_list = [samples.Sample(mock.Mock(), sample)
 | 
			
		||||
                       for sample in self.SAMPLES]
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_old_sample_list(self):
 | 
			
		||||
        self.args.meter = self.METER
 | 
			
		||||
        sample_list = [samples.OldSample(mock.Mock(), sample)
 | 
			
		||||
                       for sample in self.OLD_SAMPLES]
 | 
			
		||||
        self.cc.samples.list.return_value = sample_list
 | 
			
		||||
 | 
			
		||||
        org_stdout = sys.stdout
 | 
			
		||||
        try:
 | 
			
		||||
            sys.stdout = output = six.StringIO()
 | 
			
		||||
        ceilometer_shell.do_sample_list(self.cc, self.args)
 | 
			
		||||
        self.cc.samples.list.assert_called_once_with(
 | 
			
		||||
            meter_name=self.METER,
 | 
			
		||||
            q=None,
 | 
			
		||||
            limit=None)
 | 
			
		||||
        finally:
 | 
			
		||||
            sys.stdout = org_stdout
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(output.getvalue(), '''\
 | 
			
		||||
        self.assertEqual('''\
 | 
			
		||||
+--------------------------------------+----------+-------+----------------\
 | 
			
		||||
+------+---------------------+
 | 
			
		||||
| Resource ID                          | Name     | Type  | Volume         \
 | 
			
		||||
@@ -393,7 +415,93 @@ class ShellSampleListCommandTest(utils.BaseTestCase):
 | 
			
		||||
| %    | 2013-10-15T05:40:29 |
 | 
			
		||||
+--------------------------------------+----------+-------+----------------\
 | 
			
		||||
+------+---------------------+
 | 
			
		||||
''')
 | 
			
		||||
''', sys.stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_sample_list(self):
 | 
			
		||||
        self.args.meter = None
 | 
			
		||||
        sample_list = [samples.Sample(mock.Mock(), sample)
 | 
			
		||||
                       for sample in self.SAMPLES]
 | 
			
		||||
        self.cc.new_samples.list.return_value = sample_list
 | 
			
		||||
 | 
			
		||||
        ceilometer_shell.do_sample_list(self.cc, self.args)
 | 
			
		||||
        self.cc.new_samples.list.assert_called_once_with(
 | 
			
		||||
            q=None,
 | 
			
		||||
            limit=None)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual('''\
 | 
			
		||||
+--------------------------------------+--------------------------------------\
 | 
			
		||||
+----------+-------+----------------+------+---------------------+
 | 
			
		||||
| ID                                   | Resource ID                          \
 | 
			
		||||
| Name     | Type  | Volume         | Unit | Timestamp           |
 | 
			
		||||
+--------------------------------------+--------------------------------------\
 | 
			
		||||
+----------+-------+----------------+------+---------------------+
 | 
			
		||||
| 86536501-b2c9-48f6-9c6a-7a5b14ba7482 | 5dcf5537-3161-4e25-9235-407e1385bd35 \
 | 
			
		||||
| cpu_util | gauge | 0.261666666667 | %    | 2013-10-15T05:50:30 |
 | 
			
		||||
| fe2a91ec-602b-4b55-8cba-5302ce3b916e | 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f \
 | 
			
		||||
| cpu_util | gauge | 0.261666666667 | %    | 2013-10-15T05:50:29 |
 | 
			
		||||
| 52768bcb-b4e9-4db9-a30c-738c758b6f43 | 5dcf5537-3161-4e25-9235-407e1385bd35 \
 | 
			
		||||
| cpu_util | gauge | 0.251247920133 | %    | 2013-10-15T05:40:30 |
 | 
			
		||||
| 31ae614a-ac6b-4fb9-b106-4667bae03308 | 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f \
 | 
			
		||||
| cpu_util | gauge | 0.26           | %    | 2013-10-15T05:40:29 |
 | 
			
		||||
+--------------------------------------+--------------------------------------\
 | 
			
		||||
+----------+-------+----------------+------+---------------------+
 | 
			
		||||
''', sys.stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellSampleShowCommandTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    SAMPLE = {
 | 
			
		||||
        "user_id": None,
 | 
			
		||||
        "resource_id": "9b651dfd-7d30-402b-972e-212b2c4bfb05",
 | 
			
		||||
        "timestamp": "2014-11-03T13:37:46",
 | 
			
		||||
        "meter": "image",
 | 
			
		||||
        "volume": 1.0,
 | 
			
		||||
        "source": "openstack",
 | 
			
		||||
        "recorded_at": "2014-11-03T13:37:46.994458",
 | 
			
		||||
        "project_id": "2cc3a7bb859b4bacbeab0aa9ca673033",
 | 
			
		||||
        "type": "gauge",
 | 
			
		||||
        "id": "98b5f258-635e-11e4-8bdd-0025647390c1",
 | 
			
		||||
        "unit": "image",
 | 
			
		||||
        "metadata": {
 | 
			
		||||
            "name": "cirros-0.3.2-x86_64-uec",
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(ShellSampleShowCommandTest, self).setUp()
 | 
			
		||||
        self.cc = mock.Mock()
 | 
			
		||||
        self.cc.new_samples = mock.Mock()
 | 
			
		||||
        self.args = mock.Mock()
 | 
			
		||||
        self.args.sample_id = "98b5f258-635e-11e4-8bdd-0025647390c1"
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_sample_show(self):
 | 
			
		||||
        sample = samples.Sample(mock.Mock(), self.SAMPLE)
 | 
			
		||||
        self.cc.new_samples.get.return_value = sample
 | 
			
		||||
 | 
			
		||||
        ceilometer_shell.do_sample_show(self.cc, self.args)
 | 
			
		||||
        self.cc.new_samples.get.assert_called_once_with(
 | 
			
		||||
            "98b5f258-635e-11e4-8bdd-0025647390c1")
 | 
			
		||||
 | 
			
		||||
        self.assertEqual('''\
 | 
			
		||||
+-------------+--------------------------------------+
 | 
			
		||||
| Property    | Value                                |
 | 
			
		||||
+-------------+--------------------------------------+
 | 
			
		||||
| id          | 98b5f258-635e-11e4-8bdd-0025647390c1 |
 | 
			
		||||
| metadata    | {"name": "cirros-0.3.2-x86_64-uec"}  |
 | 
			
		||||
| meter       | image                                |
 | 
			
		||||
| project_id  | 2cc3a7bb859b4bacbeab0aa9ca673033     |
 | 
			
		||||
| recorded_at | 2014-11-03T13:37:46.994458           |
 | 
			
		||||
| resource_id | 9b651dfd-7d30-402b-972e-212b2c4bfb05 |
 | 
			
		||||
| source      | openstack                            |
 | 
			
		||||
| timestamp   | 2014-11-03T13:37:46                  |
 | 
			
		||||
| type        | gauge                                |
 | 
			
		||||
| unit        | image                                |
 | 
			
		||||
| user_id     | None                                 |
 | 
			
		||||
| volume      | 1.0                                  |
 | 
			
		||||
+-------------+--------------------------------------+
 | 
			
		||||
''', sys.stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellSampleCreateCommandTest(utils.BaseTestCase):
 | 
			
		||||
@@ -420,6 +528,7 @@ class ShellSampleCreateCommandTest(utils.BaseTestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(ShellSampleCreateCommandTest, self).setUp()
 | 
			
		||||
        self.cc = mock.Mock()
 | 
			
		||||
        self.cc.samples = mock.Mock()
 | 
			
		||||
        self.args = mock.Mock()
 | 
			
		||||
        self.args.meter_name = self.METER
 | 
			
		||||
        self.args.meter_type = self.METER_TYPE
 | 
			
		||||
@@ -427,19 +536,15 @@ class ShellSampleCreateCommandTest(utils.BaseTestCase):
 | 
			
		||||
        self.args.resource_id = self.RESOURCE_ID
 | 
			
		||||
        self.args.sample_volume = self.SAMPLE_VOLUME
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_sample_create(self):
 | 
			
		||||
 | 
			
		||||
        ret_sample = [samples.Sample(mock.Mock(), sample)
 | 
			
		||||
        ret_sample = [samples.OldSample(mock.Mock(), sample)
 | 
			
		||||
                      for sample in self.SAMPLE]
 | 
			
		||||
        self.cc.samples.create.return_value = ret_sample
 | 
			
		||||
        org_stdout = sys.stdout
 | 
			
		||||
        try:
 | 
			
		||||
            sys.stdout = output = six.StringIO()
 | 
			
		||||
            ceilometer_shell.do_sample_create(self.cc, self.args)
 | 
			
		||||
        finally:
 | 
			
		||||
            sys.stdout = org_stdout
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(output.getvalue(), '''\
 | 
			
		||||
        ceilometer_shell.do_sample_create(self.cc, self.args)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual('''\
 | 
			
		||||
+-------------------+---------------------------------------------+
 | 
			
		||||
| Property          | Value                                       |
 | 
			
		||||
+-------------------+---------------------------------------------+
 | 
			
		||||
@@ -455,7 +560,7 @@ class ShellSampleCreateCommandTest(utils.BaseTestCase):
 | 
			
		||||
| user_id           | 21b442b8101d407d8242b6610e0ed0eb            |
 | 
			
		||||
| volume            | 1.0                                         |
 | 
			
		||||
+-------------------+---------------------------------------------+
 | 
			
		||||
''')
 | 
			
		||||
''', sys.stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellQuerySamplesCommandTest(utils.BaseTestCase):
 | 
			
		||||
@@ -487,17 +592,13 @@ class ShellQuerySamplesCommandTest(utils.BaseTestCase):
 | 
			
		||||
        self.args.orderby = self.QUERY["orderby"]
 | 
			
		||||
        self.args.limit = self.QUERY["limit"]
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_query(self):
 | 
			
		||||
 | 
			
		||||
        ret_sample = [samples.Sample(mock.Mock(), sample)
 | 
			
		||||
                      for sample in self.SAMPLE]
 | 
			
		||||
        self.cc.query_samples.query.return_value = ret_sample
 | 
			
		||||
        org_stdout = sys.stdout
 | 
			
		||||
        try:
 | 
			
		||||
            sys.stdout = output = six.StringIO()
 | 
			
		||||
 | 
			
		||||
        ceilometer_shell.do_query_samples(self.cc, self.args)
 | 
			
		||||
        finally:
 | 
			
		||||
            sys.stdout = org_stdout
 | 
			
		||||
 | 
			
		||||
        self.assertEqual('''\
 | 
			
		||||
+--------------------------------------+----------+-------+--------+---------\
 | 
			
		||||
@@ -510,7 +611,7 @@ class ShellQuerySamplesCommandTest(utils.BaseTestCase):
 | 
			
		||||
 | 2014-02-19T05:50:16.673604 |
 | 
			
		||||
+--------------------------------------+----------+-------+--------+---------\
 | 
			
		||||
-+----------------------------+
 | 
			
		||||
''', output.getvalue())
 | 
			
		||||
''', sys.stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellQueryAlarmsCommandTest(utils.BaseTestCase):
 | 
			
		||||
@@ -530,9 +631,12 @@ class ShellQueryAlarmsCommandTest(utils.BaseTestCase):
 | 
			
		||||
              "project_id": "c96c887c216949acbdfbd8b494863567",
 | 
			
		||||
              "repeat_actions": False,
 | 
			
		||||
              "state": "ok",
 | 
			
		||||
              "severity": "critical",
 | 
			
		||||
              "state_timestamp": "2014-02-20T10:37:15.589860",
 | 
			
		||||
              "threshold_rule": None,
 | 
			
		||||
              "timestamp": "2014-02-20T10:37:15.589856",
 | 
			
		||||
              "time_constraints": [{"name": "test", "start": "0 23 * * *",
 | 
			
		||||
                                    "duration": 10800}],
 | 
			
		||||
              "type": "combination",
 | 
			
		||||
              "user_id": "c96c887c216949acbdfbd8b494863567"}]
 | 
			
		||||
 | 
			
		||||
@@ -549,35 +653,69 @@ class ShellQueryAlarmsCommandTest(utils.BaseTestCase):
 | 
			
		||||
        self.args.orderby = self.QUERY["orderby"]
 | 
			
		||||
        self.args.limit = self.QUERY["limit"]
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_query(self):
 | 
			
		||||
 | 
			
		||||
        ret_alarm = [alarms.Alarm(mock.Mock(), alarm)
 | 
			
		||||
                     for alarm in self.ALARM]
 | 
			
		||||
        self.cc.query_alarms.query.return_value = ret_alarm
 | 
			
		||||
        org_stdout = sys.stdout
 | 
			
		||||
        try:
 | 
			
		||||
            sys.stdout = output = six.StringIO()
 | 
			
		||||
 | 
			
		||||
        ceilometer_shell.do_query_alarms(self.cc, self.args)
 | 
			
		||||
        finally:
 | 
			
		||||
            sys.stdout = org_stdout
 | 
			
		||||
 | 
			
		||||
        self.assertEqual('''\
 | 
			
		||||
+--------------------------------------+------------------+-------+---------\
 | 
			
		||||
+------------+--------------------------------------------------------------\
 | 
			
		||||
----------------------------------------+
 | 
			
		||||
| Alarm ID                             | Name             | State | Enabled \
 | 
			
		||||
| Continuous | Alarm condition                                              \
 | 
			
		||||
+--------------------------------------+------------------+-------+----------+\
 | 
			
		||||
---------+------------+-------------------------------------------------------\
 | 
			
		||||
-----------------------------------------------+-------------------------------\
 | 
			
		||||
-+
 | 
			
		||||
| Alarm ID                             | Name             | State | Severity \
 | 
			
		||||
| Enabled | Continuous | Alarm condition                                     \
 | 
			
		||||
                                                 | Time constraints          \
 | 
			
		||||
     |
 | 
			
		||||
+--------------------------------------+------------------+-------+---------\
 | 
			
		||||
+------------+--------------------------------------------------------------\
 | 
			
		||||
----------------------------------------+
 | 
			
		||||
| 768ff714-8cfb-4db9-9753-d484cb33a1cc | SwiftObjectAlarm | ok    | True    \
 | 
			
		||||
| False      | combinated states (OR) of 739e99cb-c2ec-4718-b900-332502355f3\
 | 
			
		||||
8, 153462d0-a9b8-4b5b-8175-9e4b05e9b856 |
 | 
			
		||||
+--------------------------------------+------------------+-------+---------\
 | 
			
		||||
+------------+--------------------------------------------------------------\
 | 
			
		||||
----------------------------------------+
 | 
			
		||||
''', output.getvalue())
 | 
			
		||||
+--------------------------------------+------------------+-------+----------+\
 | 
			
		||||
---------+------------+-------------------------------------------------------\
 | 
			
		||||
-----------------------------------------------+--------------------------------+
 | 
			
		||||
| 768ff714-8cfb-4db9-9753-d484cb33a1cc | SwiftObjectAlarm | ok    | critical \
 | 
			
		||||
| True    | False      | combinated states (OR) of \
 | 
			
		||||
739e99cb-c2ec-4718-b900-332502355f38, 153462d0-a9b8-4b5b-8175-9e4b05e9b856 |\
 | 
			
		||||
 test at 0 23 * * *  for 10800s |
 | 
			
		||||
+--------------------------------------+------------------+-------+----------+\
 | 
			
		||||
---------+------------+-------------------------------------------------------\
 | 
			
		||||
-----------------------------------------------+------------------------------\
 | 
			
		||||
--+
 | 
			
		||||
''', sys.stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_time_constraints_compatibility(self):
 | 
			
		||||
        # client should be backwards compatible
 | 
			
		||||
        alarm_without_tc = dict(self.ALARM[0])
 | 
			
		||||
        del alarm_without_tc['time_constraints']
 | 
			
		||||
 | 
			
		||||
        # NOTE(nsaje): Since we're accessing a nonexisting key in the resource,
 | 
			
		||||
        # the resource is looking it up with the manager (which is a mock).
 | 
			
		||||
        manager_mock = mock.Mock()
 | 
			
		||||
        del manager_mock.get
 | 
			
		||||
        ret_alarm = [alarms.Alarm(manager_mock, alarm_without_tc)]
 | 
			
		||||
        self.cc.query_alarms.query.return_value = ret_alarm
 | 
			
		||||
 | 
			
		||||
        ceilometer_shell.do_query_alarms(self.cc, self.args)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual('''\
 | 
			
		||||
+--------------------------------------+------------------+-------+----------+\
 | 
			
		||||
---------+------------+-------------------------------------------------------\
 | 
			
		||||
-----------------------------------------------+------------------+
 | 
			
		||||
| Alarm ID                             | Name             | State | Severity \
 | 
			
		||||
| Enabled | Continuous | Alarm condition                                     \
 | 
			
		||||
                                                 | Time constraints |
 | 
			
		||||
+--------------------------------------+------------------+-------+----------+\
 | 
			
		||||
---------+------------+-------------------------------------------------------\
 | 
			
		||||
-----------------------------------------------+------------------+
 | 
			
		||||
| 768ff714-8cfb-4db9-9753-d484cb33a1cc | SwiftObjectAlarm | ok    | critical \
 | 
			
		||||
| True    | False      | combinated states (OR) of \
 | 
			
		||||
739e99cb-c2ec-4718-b900-332502355f38, 153462d0-a9b8-4b5b-8175-9e4b05e9b856 \
 | 
			
		||||
| None             |
 | 
			
		||||
+--------------------------------------+------------------+-------+----------+\
 | 
			
		||||
---------+------------+-------------------------------------------------------\
 | 
			
		||||
-----------------------------------------------+------------------+
 | 
			
		||||
''', sys.stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellQueryAlarmHistoryCommandTest(utils.BaseTestCase):
 | 
			
		||||
@@ -606,17 +744,13 @@ class ShellQueryAlarmHistoryCommandTest(utils.BaseTestCase):
 | 
			
		||||
        self.args.orderby = self.QUERY["orderby"]
 | 
			
		||||
        self.args.limit = self.QUERY["limit"]
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_query(self):
 | 
			
		||||
 | 
			
		||||
        ret_alarm_history = [alarms.AlarmChange(mock.Mock(), alarm_history)
 | 
			
		||||
                             for alarm_history in self.ALARM_HISTORY]
 | 
			
		||||
        self.cc.query_alarm_history.query.return_value = ret_alarm_history
 | 
			
		||||
        org_stdout = sys.stdout
 | 
			
		||||
        try:
 | 
			
		||||
            sys.stdout = output = six.StringIO()
 | 
			
		||||
 | 
			
		||||
        ceilometer_shell.do_query_alarm_history(self.cc, self.args)
 | 
			
		||||
        finally:
 | 
			
		||||
            sys.stdout = org_stdout
 | 
			
		||||
 | 
			
		||||
        self.assertEqual('''\
 | 
			
		||||
+----------------------------------+--------------------------------------+-\
 | 
			
		||||
@@ -634,7 +768,7 @@ rule change | {"threshold": 42.0, "evaluation_periods": 4} | 2014-03-11T16:0\
 | 
			
		||||
+----------------------------------+--------------------------------------+-\
 | 
			
		||||
------------+----------------------------------------------+----------------\
 | 
			
		||||
------------+
 | 
			
		||||
''', output.getvalue())
 | 
			
		||||
''', sys.stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellStatisticsTest(utils.BaseTestCase):
 | 
			
		||||
@@ -788,3 +922,200 @@ class ShellStatisticsTest(utils.BaseTestCase):
 | 
			
		||||
                fields,
 | 
			
		||||
                [self.displays.get(f, f) for f in fields],
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellEmptyIdTest(utils.BaseTestCase):
 | 
			
		||||
    """Test empty field which will cause calling incorrect rest uri."""
 | 
			
		||||
 | 
			
		||||
    def _test_entity_action_with_empty_values(self, entity,
 | 
			
		||||
                                              *args, **kwargs):
 | 
			
		||||
        positional = kwargs.pop('positional', False)
 | 
			
		||||
        for value in ('', ' ', '   ', '\t'):
 | 
			
		||||
            self._test_entity_action_with_empty_value(entity, value,
 | 
			
		||||
                                                      positional, *args)
 | 
			
		||||
 | 
			
		||||
    def _test_entity_action_with_empty_value(self, entity, value,
 | 
			
		||||
                                             positional, *args):
 | 
			
		||||
        new_args = [value] if positional else ['--%s' % entity, value]
 | 
			
		||||
        argv = list(args) + new_args
 | 
			
		||||
        shell = base_shell.CeilometerShell()
 | 
			
		||||
        with mock.patch('ceilometerclient.exc.CommandError') as e:
 | 
			
		||||
            e.return_value = exc.BaseException()
 | 
			
		||||
            self.assertRaises(exc.BaseException, shell.parse_args, argv)
 | 
			
		||||
            entity = entity.replace('-', '_')
 | 
			
		||||
            e.assert_called_with('%s should not be empty' % entity)
 | 
			
		||||
 | 
			
		||||
    def _test_alarm_action_with_empty_ids(self, method, *args):
 | 
			
		||||
        args = [method] + list(args)
 | 
			
		||||
        self._test_entity_action_with_empty_values('alarm_id',
 | 
			
		||||
                                                   positional=True, *args)
 | 
			
		||||
 | 
			
		||||
    def test_alarm_show_with_empty_id(self):
 | 
			
		||||
        self._test_alarm_action_with_empty_ids('alarm-show')
 | 
			
		||||
 | 
			
		||||
    def test_alarm_update_with_empty_id(self):
 | 
			
		||||
        self._test_alarm_action_with_empty_ids('alarm-update')
 | 
			
		||||
 | 
			
		||||
    def test_alarm_threshold_update_with_empty_id(self):
 | 
			
		||||
        self._test_alarm_action_with_empty_ids('alarm-threshold-update')
 | 
			
		||||
 | 
			
		||||
    def test_alarm_combination_update_with_empty_id(self):
 | 
			
		||||
        self._test_alarm_action_with_empty_ids('alarm-combination-update')
 | 
			
		||||
 | 
			
		||||
    def test_alarm_delete_with_empty_id(self):
 | 
			
		||||
        self._test_alarm_action_with_empty_ids('alarm-delete')
 | 
			
		||||
 | 
			
		||||
    def test_alarm_state_get_with_empty_id(self):
 | 
			
		||||
        self._test_alarm_action_with_empty_ids('alarm-state-get')
 | 
			
		||||
 | 
			
		||||
    def test_alarm_state_set_with_empty_id(self):
 | 
			
		||||
        args = ['alarm-state-set', '--state', 'ok']
 | 
			
		||||
        self._test_alarm_action_with_empty_ids(*args)
 | 
			
		||||
 | 
			
		||||
    def test_alarm_history_with_empty_id(self):
 | 
			
		||||
        self._test_alarm_action_with_empty_ids('alarm-history')
 | 
			
		||||
 | 
			
		||||
    def test_event_show_with_empty_message_id(self):
 | 
			
		||||
        args = ['event-show']
 | 
			
		||||
        self._test_entity_action_with_empty_values('message_id', *args)
 | 
			
		||||
 | 
			
		||||
    def test_resource_show_with_empty_id(self):
 | 
			
		||||
        args = ['resource-show']
 | 
			
		||||
        self._test_entity_action_with_empty_values('resource_id', *args)
 | 
			
		||||
 | 
			
		||||
    def test_sample_list_with_empty_meter(self):
 | 
			
		||||
        args = ['sample-list']
 | 
			
		||||
        self._test_entity_action_with_empty_values('meter', *args)
 | 
			
		||||
 | 
			
		||||
    def test_sample_create_with_empty_meter(self):
 | 
			
		||||
        args = ['sample-create', '-r', 'x', '--meter-type', 'gauge',
 | 
			
		||||
                '--meter-unit', 'B', '--sample-volume', '1']
 | 
			
		||||
        self._test_entity_action_with_empty_values('meter-name', *args)
 | 
			
		||||
 | 
			
		||||
    def test_statistics_with_empty_meter(self):
 | 
			
		||||
        args = ['statistics']
 | 
			
		||||
        self._test_entity_action_with_empty_values('meter', *args)
 | 
			
		||||
 | 
			
		||||
    def test_trait_description_list_with_empty_event_type(self):
 | 
			
		||||
        args = ['trait-description-list']
 | 
			
		||||
        self._test_entity_action_with_empty_values('event_type', *args)
 | 
			
		||||
 | 
			
		||||
    def test_trait_list_with_empty_event_type(self):
 | 
			
		||||
        args = ['trait-list', '--trait_name', 'x']
 | 
			
		||||
        self._test_entity_action_with_empty_values('event_type', *args)
 | 
			
		||||
 | 
			
		||||
    def test_trait_list_with_empty_trait_name(self):
 | 
			
		||||
        args = ['trait-list', '--event_type', 'x']
 | 
			
		||||
        self._test_entity_action_with_empty_values('trait_name', *args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellObsoletedArgsTest(utils.BaseTestCase):
 | 
			
		||||
    """Test arguments that have been obsoleted."""
 | 
			
		||||
 | 
			
		||||
    def _test_entity_obsoleted(self, entity, value, positional, *args):
 | 
			
		||||
        new_args = [value] if positional else ['--%s' % entity, value]
 | 
			
		||||
        argv = list(args) + new_args
 | 
			
		||||
        shell = base_shell.CeilometerShell()
 | 
			
		||||
        with mock.patch('sys.stdout', new_callable=six.StringIO) as stdout:
 | 
			
		||||
            shell.parse_args(argv)
 | 
			
		||||
            self.assertIn('obsolete', stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
    def test_obsolete_alarm_id(self):
 | 
			
		||||
        for method in ['alarm-show', 'alarm-update', 'alarm-threshold-update',
 | 
			
		||||
                       'alarm-combination-update', 'alarm-delete',
 | 
			
		||||
                       'alarm-state-get', 'alarm-history']:
 | 
			
		||||
            self._test_entity_obsoleted('alarm_id', 'abcde', False, method)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShellEventListCommandTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    EVENTS = [
 | 
			
		||||
        {
 | 
			
		||||
            "traits": [],
 | 
			
		||||
            "generated": "2015-01-12T04:03:25.741471",
 | 
			
		||||
            "message_id": "fb2bef58-88af-4380-8698-e0f18fcf452d",
 | 
			
		||||
            "event_type": "compute.instance.create.start",
 | 
			
		||||
            "traits": [{
 | 
			
		||||
                "name": "state",
 | 
			
		||||
                "type": "string",
 | 
			
		||||
                "value": "building",
 | 
			
		||||
            }],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "traits": [],
 | 
			
		||||
            "generated": "2015-01-12T04:03:28.452495",
 | 
			
		||||
            "message_id": "9b20509a-576b-4995-acfa-1a24ee5cf49f",
 | 
			
		||||
            "event_type": "compute.instance.create.end",
 | 
			
		||||
            "traits": [{
 | 
			
		||||
                "name": "state",
 | 
			
		||||
                "type": "string",
 | 
			
		||||
                "value": "active",
 | 
			
		||||
            }],
 | 
			
		||||
        },
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(ShellEventListCommandTest, self).setUp()
 | 
			
		||||
        self.cc = mock.Mock()
 | 
			
		||||
        self.args = mock.Mock()
 | 
			
		||||
        self.args.query = None
 | 
			
		||||
        self.args.no_traits = None
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_event_list(self):
 | 
			
		||||
        ret_events = [events.Event(mock.Mock(), event)
 | 
			
		||||
                      for event in self.EVENTS]
 | 
			
		||||
        self.cc.events.list.return_value = ret_events
 | 
			
		||||
        ceilometer_shell.do_event_list(self.cc, self.args)
 | 
			
		||||
        self.assertEqual('''\
 | 
			
		||||
+--------------------------------------+-------------------------------+\
 | 
			
		||||
----------------------------+-------------------------------+
 | 
			
		||||
| Message ID                           | Event Type                    |\
 | 
			
		||||
 Generated                  | Traits                        |
 | 
			
		||||
+--------------------------------------+-------------------------------+\
 | 
			
		||||
----------------------------+-------------------------------+
 | 
			
		||||
| fb2bef58-88af-4380-8698-e0f18fcf452d | compute.instance.create.start |\
 | 
			
		||||
 2015-01-12T04:03:25.741471 | +-------+--------+----------+ |
 | 
			
		||||
|                                      |                               |\
 | 
			
		||||
                            | |  name |  type  |  value   | |
 | 
			
		||||
|                                      |                               |\
 | 
			
		||||
                            | +-------+--------+----------+ |
 | 
			
		||||
|                                      |                               |\
 | 
			
		||||
                            | | state | string | building | |
 | 
			
		||||
|                                      |                               |\
 | 
			
		||||
                            | +-------+--------+----------+ |
 | 
			
		||||
| 9b20509a-576b-4995-acfa-1a24ee5cf49f | compute.instance.create.end   |\
 | 
			
		||||
 2015-01-12T04:03:28.452495 | +-------+--------+--------+   |
 | 
			
		||||
|                                      |                               |\
 | 
			
		||||
                            | |  name |  type  | value  |   |
 | 
			
		||||
|                                      |                               |\
 | 
			
		||||
                            | +-------+--------+--------+   |
 | 
			
		||||
|                                      |                               |\
 | 
			
		||||
                            | | state | string | active |   |
 | 
			
		||||
|                                      |                               |\
 | 
			
		||||
                            | +-------+--------+--------+   |
 | 
			
		||||
+--------------------------------------+-------------------------------+\
 | 
			
		||||
----------------------------+-------------------------------+
 | 
			
		||||
''', sys.stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
    @mock.patch('sys.stdout', new=six.StringIO())
 | 
			
		||||
    def test_event_list_no_traits(self):
 | 
			
		||||
        self.args.no_traits = True
 | 
			
		||||
        ret_events = [events.Event(mock.Mock(), event)
 | 
			
		||||
                      for event in self.EVENTS]
 | 
			
		||||
        self.cc.events.list.return_value = ret_events
 | 
			
		||||
        ceilometer_shell.do_event_list(self.cc, self.args)
 | 
			
		||||
        self.assertEqual('''\
 | 
			
		||||
+--------------------------------------+-------------------------------\
 | 
			
		||||
+----------------------------+
 | 
			
		||||
| Message ID                           | Event Type                    \
 | 
			
		||||
| Generated                  |
 | 
			
		||||
+--------------------------------------+-------------------------------\
 | 
			
		||||
+----------------------------+
 | 
			
		||||
| fb2bef58-88af-4380-8698-e0f18fcf452d | compute.instance.create.start \
 | 
			
		||||
| 2015-01-12T04:03:25.741471 |
 | 
			
		||||
| 9b20509a-576b-4995-acfa-1a24ee5cf49f | compute.instance.create.end   \
 | 
			
		||||
| 2015-01-12T04:03:28.452495 |
 | 
			
		||||
+--------------------------------------+-------------------------------\
 | 
			
		||||
+----------------------------+
 | 
			
		||||
''', sys.stdout.getvalue())
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,8 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v2.statistics
 | 
			
		||||
 | 
			
		||||
@@ -114,15 +115,16 @@ class StatisticsManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(StatisticsManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v2.statistics.StatisticsManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list_by_meter_name(self):
 | 
			
		||||
        stats = list(self.mgr.list(meter_name='instance'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/meters/instance/statistics', {}, None),
 | 
			
		||||
            'GET', '/v2/meters/instance/statistics'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(stats), 1)
 | 
			
		||||
        self.assertEqual(stats[0].count, 135)
 | 
			
		||||
 | 
			
		||||
@@ -135,10 +137,9 @@ class StatisticsManagerTest(utils.BaseTestCase):
 | 
			
		||||
                                        "value": "bar"},
 | 
			
		||||
                                   ]))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET',
 | 
			
		||||
             '%s?%s' % (base_url, qry), {}, None),
 | 
			
		||||
            'GET', '%s?%s' % (base_url, qry)
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(stats), 1)
 | 
			
		||||
        self.assertEqual(stats[0].count, 135)
 | 
			
		||||
 | 
			
		||||
@@ -152,10 +153,9 @@ class StatisticsManagerTest(utils.BaseTestCase):
 | 
			
		||||
                                   ],
 | 
			
		||||
                                   period=60))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET',
 | 
			
		||||
             '%s?%s%s' % (base_url, qry, period), {}, None),
 | 
			
		||||
            'GET', '%s?%s%s' % (base_url, qry, period)
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(stats), 1)
 | 
			
		||||
        self.assertEqual(stats[0].count, 135)
 | 
			
		||||
 | 
			
		||||
@@ -169,16 +169,36 @@ class StatisticsManagerTest(utils.BaseTestCase):
 | 
			
		||||
                                   ],
 | 
			
		||||
                                   groupby=['resource_id']))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET',
 | 
			
		||||
             '%s?%s%s' % (base_url, qry, groupby), {}, None),
 | 
			
		||||
            'GET',
 | 
			
		||||
            '%s?%s%s' % (base_url, qry, groupby)
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(stats), 2)
 | 
			
		||||
        self.assertEqual(stats[0].count, 135)
 | 
			
		||||
        self.assertEqual(stats[1].count, 12)
 | 
			
		||||
        self.assertEqual(stats[0].groupby.get('resource_id'), 'foo')
 | 
			
		||||
        self.assertEqual(stats[1].groupby.get('resource_id'), 'bar')
 | 
			
		||||
 | 
			
		||||
    def test_list_by_meter_name_with_groupby_as_str(self):
 | 
			
		||||
        stats = list(self.mgr.list(meter_name='instance',
 | 
			
		||||
                                   q=[
 | 
			
		||||
                                       {"field": "resource_id",
 | 
			
		||||
                                        "value": "foo"},
 | 
			
		||||
                                       {"field": "source",
 | 
			
		||||
                                        "value": "bar"},
 | 
			
		||||
                                   ],
 | 
			
		||||
                                   groupby='resource_id'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            'GET',
 | 
			
		||||
            '%s?%s%s' % (base_url, qry, groupby)
 | 
			
		||||
        ]
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(2, len(stats))
 | 
			
		||||
        self.assertEqual(135, stats[0].count)
 | 
			
		||||
        self.assertEqual(12, stats[1].count)
 | 
			
		||||
        self.assertEqual('foo', stats[0].groupby.get('resource_id'))
 | 
			
		||||
        self.assertEqual('bar', stats[1].groupby.get('resource_id'))
 | 
			
		||||
 | 
			
		||||
    def test_list_by_meter_name_with_aggregates(self):
 | 
			
		||||
        aggregates = [
 | 
			
		||||
            {
 | 
			
		||||
@@ -192,10 +212,10 @@ class StatisticsManagerTest(utils.BaseTestCase):
 | 
			
		||||
        stats = list(self.mgr.list(meter_name='instance',
 | 
			
		||||
                                   aggregates=aggregates))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET',
 | 
			
		||||
             '%s?%s' % (base_url, aggregate_query), {}, None),
 | 
			
		||||
            'GET',
 | 
			
		||||
            '%s?%s' % (base_url, aggregate_query)
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(expect, self.api.calls)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(1, len(stats))
 | 
			
		||||
        self.assertEqual(2, stats[0].count)
 | 
			
		||||
        self.assertEqual(2.0, stats[0].aggregate.get('count'))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
@@ -12,7 +11,8 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v2.trait_descriptions
 | 
			
		||||
 | 
			
		||||
@@ -35,16 +35,17 @@ class TraitDescriptionManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(TraitDescriptionManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = (ceilometerclient.v2.trait_descriptions.
 | 
			
		||||
                    TraitDescriptionManager(self.api))
 | 
			
		||||
 | 
			
		||||
    def test_list(self):
 | 
			
		||||
        trait_descriptions = list(self.mgr.list('Foo'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/event_types/Foo/traits', {}, None),
 | 
			
		||||
            'GET', '/v2/event_types/Foo/traits'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(trait_descriptions), 3)
 | 
			
		||||
        for i, vals in enumerate([('trait_1', 'string'),
 | 
			
		||||
                                  ('trait_2', 'integer'),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
@@ -12,7 +11,8 @@
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import fake_client
 | 
			
		||||
from ceilometerclient.tests import utils
 | 
			
		||||
import ceilometerclient.v2.traits
 | 
			
		||||
 | 
			
		||||
@@ -38,15 +38,16 @@ class TraitManagerTest(utils.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(TraitManagerTest, self).setUp()
 | 
			
		||||
        self.api = utils.FakeAPI(fixtures)
 | 
			
		||||
        self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
 | 
			
		||||
        self.api = client.BaseClient(self.http_client)
 | 
			
		||||
        self.mgr = ceilometerclient.v2.traits.TraitManager(self.api)
 | 
			
		||||
 | 
			
		||||
    def test_list(self):
 | 
			
		||||
        traits = list(self.mgr.list('Foo', 'trait_1'))
 | 
			
		||||
        expect = [
 | 
			
		||||
            ('GET', '/v2/event_types/Foo/traits/trait_1', {}, None),
 | 
			
		||||
            'GET', '/v2/event_types/Foo/traits/trait_1'
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(self.api.calls, expect)
 | 
			
		||||
        self.http_client.assert_called(*expect)
 | 
			
		||||
        self.assertEqual(len(traits), 2)
 | 
			
		||||
        for i, vals in enumerate([('trait_1',
 | 
			
		||||
                                   'datetime',
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,9 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.common import http
 | 
			
		||||
 | 
			
		||||
from ceilometerclient import client as ceiloclient
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.v1 import meters
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -29,7 +31,24 @@ class Client(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        """Initialize a new client for the Ceilometer v1 API."""
 | 
			
		||||
        self.http_client = http.HTTPClient(*args, **kwargs)
 | 
			
		||||
        self.auth_plugin = kwargs.get('auth_plugin') \
 | 
			
		||||
            or ceiloclient.get_auth_plugin(*args, **kwargs)
 | 
			
		||||
        self.client = client.HTTPClient(
 | 
			
		||||
            auth_plugin=self.auth_plugin,
 | 
			
		||||
            region_name=kwargs.get('region_name'),
 | 
			
		||||
            endpoint_type=kwargs.get('endpoint_type'),
 | 
			
		||||
            original_ip=kwargs.get('original_ip'),
 | 
			
		||||
            verify=kwargs.get('verify'),
 | 
			
		||||
            cert=kwargs.get('cert'),
 | 
			
		||||
            timeout=kwargs.get('timeout'),
 | 
			
		||||
            timings=kwargs.get('timings'),
 | 
			
		||||
            keyring_saver=kwargs.get('keyring_saver'),
 | 
			
		||||
            debug=kwargs.get('debug'),
 | 
			
		||||
            user_agent=kwargs.get('user_agent'),
 | 
			
		||||
            http=kwargs.get('http')
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.http_client = client.BaseClient(self.client)
 | 
			
		||||
        self.meters = meters.MeterManager(self.http_client)
 | 
			
		||||
        self.samples = meters.SampleManager(self.http_client)
 | 
			
		||||
        self.users = meters.UserManager(self.http_client)
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ class UserManager(base.Manager):
 | 
			
		||||
    def list(self, **kwargs):
 | 
			
		||||
        s = kwargs.get('source')
 | 
			
		||||
        if s:
 | 
			
		||||
            path = '/sources/%s/users' % (s)
 | 
			
		||||
            path = '/sources/%s/users' % s
 | 
			
		||||
        else:
 | 
			
		||||
            path = '/users'
 | 
			
		||||
        return self._list('/v1%s' % path, 'users')
 | 
			
		||||
@@ -100,17 +100,17 @@ class ResourceManager(base.Manager):
 | 
			
		||||
        opts_path = _get_opt_path(['start_timestamp', 'end_timestamp'],
 | 
			
		||||
                                  **kwargs)
 | 
			
		||||
        if u:
 | 
			
		||||
            path = '/users/%s/resources' % (u)
 | 
			
		||||
            path = '/users/%s/resources' % u
 | 
			
		||||
        elif s:
 | 
			
		||||
            path = '/sources/%s/resources' % (s)
 | 
			
		||||
            path = '/sources/%s/resources' % s
 | 
			
		||||
        elif p:
 | 
			
		||||
            path = '/projects/%s/resources' % (p)
 | 
			
		||||
            path = '/projects/%s/resources' % p
 | 
			
		||||
        else:
 | 
			
		||||
            path = '/resources'
 | 
			
		||||
        if opts_path:
 | 
			
		||||
            path = '/v1%s?%s' % (path, opts_path)
 | 
			
		||||
        else:
 | 
			
		||||
            path = '/v1%s' % (path)
 | 
			
		||||
            path = '/v1%s' % path
 | 
			
		||||
        return self._list(path, 'resources')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -152,7 +152,7 @@ class SampleManager(base.Manager):
 | 
			
		||||
        if opts_path:
 | 
			
		||||
            path = '/v1%s?%s' % (path, opts_path)
 | 
			
		||||
        else:
 | 
			
		||||
            path = '/v1%s' % (path)
 | 
			
		||||
            path = '/v1%s' % path
 | 
			
		||||
        return self._list(path, 'events')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -186,5 +186,5 @@ class MeterManager(base.Manager):
 | 
			
		||||
        if opts_path:
 | 
			
		||||
            path = '/v1%s?%s' % (path, opts_path)
 | 
			
		||||
        else:
 | 
			
		||||
            path = '/v1%s' % (path)
 | 
			
		||||
            path = '/v1%s' % path
 | 
			
		||||
        return self._list(path, 'meters')
 | 
			
		||||
 
 | 
			
		||||
@@ -68,7 +68,7 @@ def do_sample_list(cc, args):
 | 
			
		||||
@utils.arg('-p', '--project_id', metavar='<PROJECT_ID>',
 | 
			
		||||
           help='ID of the project to show samples for.')
 | 
			
		||||
def do_meter_list(cc, args={}):
 | 
			
		||||
    '''List the user's meter'''
 | 
			
		||||
    '''List the user's meter.'''
 | 
			
		||||
    fields = {'resource_id': args.resource_id,
 | 
			
		||||
              'user_id': args.user_id,
 | 
			
		||||
              'project_id': args.project_id,
 | 
			
		||||
@@ -108,7 +108,7 @@ def do_user_list(cc, args={}):
 | 
			
		||||
           help='ISO date in UTC which limits resouces by '
 | 
			
		||||
           'last update time <= this value')
 | 
			
		||||
def do_resource_list(cc, args={}):
 | 
			
		||||
    '''List the resources.'''
 | 
			
		||||
    """List the resources."""
 | 
			
		||||
    kwargs = {'source': args.source,
 | 
			
		||||
              'user_id': args.user_id,
 | 
			
		||||
              'project_id': args.project_id,
 | 
			
		||||
@@ -126,7 +126,7 @@ def do_resource_list(cc, args={}):
 | 
			
		||||
@utils.arg('-s', '--source', metavar='<SOURCE>',
 | 
			
		||||
           help='ID of the resource to show projects for.')
 | 
			
		||||
def do_project_list(cc, args={}):
 | 
			
		||||
    '''List the projects.'''
 | 
			
		||||
    """List the projects."""
 | 
			
		||||
    kwargs = {'source': args.source}
 | 
			
		||||
    projects = cc.projects.list(**kwargs)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,5 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
# Copyright © 2013 Red Hat, Inc
 | 
			
		||||
#
 | 
			
		||||
# Author:  Eoghan Glynn <eglynn@redhat.com>
 | 
			
		||||
# Copyright 2013 Red Hat, Inc
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
@@ -20,6 +17,7 @@ import warnings
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.common import base
 | 
			
		||||
from ceilometerclient.common import utils
 | 
			
		||||
from ceilometerclient import exc
 | 
			
		||||
from ceilometerclient.v2 import options
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -28,13 +26,12 @@ UPDATABLE_ATTRIBUTES = [
 | 
			
		||||
    'description',
 | 
			
		||||
    'type',
 | 
			
		||||
    'state',
 | 
			
		||||
    'severity',
 | 
			
		||||
    'enabled',
 | 
			
		||||
    'alarm_actions',
 | 
			
		||||
    'ok_actions',
 | 
			
		||||
    'insufficient_data_actions',
 | 
			
		||||
    'repeat_actions',
 | 
			
		||||
    'threshold_rule',
 | 
			
		||||
    'combination_rule',
 | 
			
		||||
]
 | 
			
		||||
CREATION_ATTRIBUTES = UPDATABLE_ATTRIBUTES + ['project_id', 'user_id',
 | 
			
		||||
                                              'time_constraints']
 | 
			
		||||
@@ -49,8 +46,17 @@ class Alarm(base.Resource):
 | 
			
		||||
        # that look like the Alarm storage object
 | 
			
		||||
        if k == 'rule':
 | 
			
		||||
            k = '%s_rule' % self.type
 | 
			
		||||
        if k == 'id':
 | 
			
		||||
            return self.alarm_id
 | 
			
		||||
        return super(Alarm, self).__getattr__(k)
 | 
			
		||||
 | 
			
		||||
    def delete(self):
 | 
			
		||||
        return self.manager.delete(self.alarm_id)
 | 
			
		||||
 | 
			
		||||
    def get_state(self):
 | 
			
		||||
        state = self.manager.get_state(self.alarm_id)
 | 
			
		||||
        return state.get('alarm')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AlarmChange(base.Resource):
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
@@ -76,6 +82,14 @@ class AlarmManager(base.Manager):
 | 
			
		||||
        except IndexError:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        except exc.HTTPNotFound:
 | 
			
		||||
            # When we try to get deleted alarm HTTPNotFound occurs
 | 
			
		||||
            # or when alarm doesn't exists this exception don't must
 | 
			
		||||
            # go deeper because cleanUp() (method which remove all
 | 
			
		||||
            # created things like instance, alarm, etc.) at scenario
 | 
			
		||||
            # tests doesn't know how to process it
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _compat_legacy_alarm_kwargs(cls, kwargs, create=False):
 | 
			
		||||
        cls._compat_counter_rename_kwargs(kwargs, create)
 | 
			
		||||
@@ -135,16 +149,21 @@ class AlarmManager(base.Manager):
 | 
			
		||||
    def create(self, **kwargs):
 | 
			
		||||
        self._compat_legacy_alarm_kwargs(kwargs, create=True)
 | 
			
		||||
        new = dict((key, value) for (key, value) in kwargs.items()
 | 
			
		||||
                   if key in CREATION_ATTRIBUTES)
 | 
			
		||||
                   if (key in CREATION_ATTRIBUTES
 | 
			
		||||
                       or key.endswith('_rule')))
 | 
			
		||||
        return self._create(self._path(), new)
 | 
			
		||||
 | 
			
		||||
    def update(self, alarm_id, **kwargs):
 | 
			
		||||
        self._compat_legacy_alarm_kwargs(kwargs)
 | 
			
		||||
        updated = self.get(alarm_id).to_dict()
 | 
			
		||||
        alarm = self.get(alarm_id)
 | 
			
		||||
        if alarm is None:
 | 
			
		||||
            raise exc.CommandError('Alarm not found: %s' % alarm_id)
 | 
			
		||||
        updated = alarm.to_dict()
 | 
			
		||||
        updated['time_constraints'] = self._merge_time_constraints(
 | 
			
		||||
            updated.get('time_constraints', []), kwargs)
 | 
			
		||||
        kwargs = dict((k, v) for k, v in kwargs.items()
 | 
			
		||||
                      if k in updated and k in UPDATABLE_ATTRIBUTES)
 | 
			
		||||
                      if k in updated and (k in UPDATABLE_ATTRIBUTES
 | 
			
		||||
                                           or k.endswith('_rule')))
 | 
			
		||||
        utils.merge_nested_dict(updated, kwargs, depth=1)
 | 
			
		||||
        return self._update(self._path(alarm_id), updated)
 | 
			
		||||
 | 
			
		||||
@@ -152,14 +171,12 @@ class AlarmManager(base.Manager):
 | 
			
		||||
        return self._delete(self._path(alarm_id))
 | 
			
		||||
 | 
			
		||||
    def set_state(self, alarm_id, state):
 | 
			
		||||
        resp, body = self.api.json_request('PUT',
 | 
			
		||||
                                           "%s/state" % self._path(alarm_id),
 | 
			
		||||
                                           body=state)
 | 
			
		||||
        body = self.api.put("%s/state" % self._path(alarm_id),
 | 
			
		||||
                            json=state).json()
 | 
			
		||||
        return body
 | 
			
		||||
 | 
			
		||||
    def get_state(self, alarm_id):
 | 
			
		||||
        resp, body = self.api.json_request('GET',
 | 
			
		||||
                                           "%s/state" % self._path(alarm_id))
 | 
			
		||||
        body = self.api.get("%s/state" % self._path(alarm_id)).json()
 | 
			
		||||
        return body
 | 
			
		||||
 | 
			
		||||
    def get_history(self, alarm_id, q=None):
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,8 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.common import http
 | 
			
		||||
from ceilometerclient import client as ceiloclient
 | 
			
		||||
from ceilometerclient.openstack.common.apiclient import client
 | 
			
		||||
from ceilometerclient.v2 import alarms
 | 
			
		||||
from ceilometerclient.v2 import event_types
 | 
			
		||||
from ceilometerclient.v2 import events
 | 
			
		||||
@@ -31,26 +32,48 @@ from ceilometerclient.v2 import traits
 | 
			
		||||
class Client(object):
 | 
			
		||||
    """Client for the Ceilometer v2 API.
 | 
			
		||||
 | 
			
		||||
    :param string endpoint: A user-supplied endpoint URL for the ceilometer
 | 
			
		||||
    :param endpoint: A user-supplied endpoint URL for the ceilometer
 | 
			
		||||
                            service.
 | 
			
		||||
    :param function token: Provides token for authentication.
 | 
			
		||||
    :param integer timeout: Allows customization of the timeout for client
 | 
			
		||||
    :type endpoint: string
 | 
			
		||||
    :param token: Provides token for authentication.
 | 
			
		||||
    :type token: function
 | 
			
		||||
    :param timeout: Allows customization of the timeout for client
 | 
			
		||||
                    http requests. (optional)
 | 
			
		||||
    :type timeout: integer
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        """Initialize a new client for the Ceilometer v2 API."""
 | 
			
		||||
        self.http_client = http.HTTPClient(*args, **kwargs)
 | 
			
		||||
        self.auth_plugin = kwargs.get('auth_plugin') \
 | 
			
		||||
            or ceiloclient.get_auth_plugin(*args, **kwargs)
 | 
			
		||||
        self.client = client.HTTPClient(
 | 
			
		||||
            auth_plugin=self.auth_plugin,
 | 
			
		||||
            region_name=kwargs.get('region_name'),
 | 
			
		||||
            endpoint_type=kwargs.get('endpoint_type'),
 | 
			
		||||
            original_ip=kwargs.get('original_ip'),
 | 
			
		||||
            verify=kwargs.get('verify'),
 | 
			
		||||
            cert=kwargs.get('cert'),
 | 
			
		||||
            timeout=kwargs.get('timeout'),
 | 
			
		||||
            timings=kwargs.get('timings'),
 | 
			
		||||
            keyring_saver=kwargs.get('keyring_saver'),
 | 
			
		||||
            debug=kwargs.get('debug'),
 | 
			
		||||
            user_agent=kwargs.get('user_agent'),
 | 
			
		||||
            http=kwargs.get('http')
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.http_client = client.BaseClient(self.client)
 | 
			
		||||
        self.meters = meters.MeterManager(self.http_client)
 | 
			
		||||
        self.samples = samples.SampleManager(self.http_client)
 | 
			
		||||
        self.samples = samples.OldSampleManager(self.http_client)
 | 
			
		||||
        self.new_samples = samples.SampleManager(self.http_client)
 | 
			
		||||
        self.statistics = statistics.StatisticsManager(self.http_client)
 | 
			
		||||
        self.resources = resources.ResourceManager(self.http_client)
 | 
			
		||||
        self.alarms = alarms.AlarmManager(self.http_client)
 | 
			
		||||
        self.events = events.EventManager(self.http_client)
 | 
			
		||||
        self.event_types = event_types.EventTypeManager(self.http_client)
 | 
			
		||||
        self.traits = traits.TraitManager(self.http_client)
 | 
			
		||||
        self.trait_info = trait_descriptions.\
 | 
			
		||||
        self.trait_descriptions = trait_descriptions.\
 | 
			
		||||
            TraitDescriptionManager(self.http_client)
 | 
			
		||||
 | 
			
		||||
        self.query_samples = query.QuerySamplesManager(
 | 
			
		||||
            self.http_client)
 | 
			
		||||
        self.query_alarms = query.QueryAlarmsManager(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
@@ -21,6 +20,11 @@ class Event(base.Resource):
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return "<Event %s>" % self._info
 | 
			
		||||
 | 
			
		||||
    def __getattr__(self, k):
 | 
			
		||||
        if k == 'id':
 | 
			
		||||
            return self.message_id
 | 
			
		||||
        return super(Event, self).__getattr__(k)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EventManager(base.Manager):
 | 
			
		||||
    resource_class = Event
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
# Copyright © 2013 Red Hat, Inc
 | 
			
		||||
# Copyright 2013 Red Hat, Inc
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
 
 | 
			
		||||
@@ -13,19 +13,35 @@
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
from six.moves.urllib import parse
 | 
			
		||||
from six.moves import urllib
 | 
			
		||||
 | 
			
		||||
OP_LOOKUP = {'!=': 'ne',
 | 
			
		||||
             '>=': 'ge',
 | 
			
		||||
             '<=': 'le',
 | 
			
		||||
             '>': 'gt',
 | 
			
		||||
             '<': 'lt',
 | 
			
		||||
             '=': 'eq'}
 | 
			
		||||
 | 
			
		||||
OP_LOOKUP_KEYS = '|'.join(sorted(OP_LOOKUP.keys(), key=len, reverse=True))
 | 
			
		||||
OP_SPLIT_RE = re.compile(r'(%s)' % OP_LOOKUP_KEYS)
 | 
			
		||||
 | 
			
		||||
DATA_TYPE_RE = re.compile(r'^(string|integer|float|datetime|boolean)(::)(.+)$')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_url(path, q, params=None):
 | 
			
		||||
    '''This converts from a list of dicts and a list of params to
 | 
			
		||||
       what the rest api needs, so from:
 | 
			
		||||
    "[{field=this,op=le,value=34},{field=that,op=eq,value=foo}],
 | 
			
		||||
    """Convert list of dicts and a list of params to query url format.
 | 
			
		||||
 | 
			
		||||
    This will convert the following:
 | 
			
		||||
        "[{field=this,op=le,value=34},
 | 
			
		||||
          {field=that,op=eq,value=foo,type=string}],
 | 
			
		||||
         ['foo=bar','sna=fu']"
 | 
			
		||||
    to:
 | 
			
		||||
    "?q.field=this&q.op=le&q.value=34&
 | 
			
		||||
      q.field=that&q.op=eq&q.value=foo&
 | 
			
		||||
        "?q.field=this&q.field=that&
 | 
			
		||||
          q.op=le&q.op=eq&
 | 
			
		||||
          q.type=&q.type=string&
 | 
			
		||||
          q.value=34&q.value=foo&
 | 
			
		||||
          foo=bar&sna=fu"
 | 
			
		||||
    '''
 | 
			
		||||
    """
 | 
			
		||||
    if q:
 | 
			
		||||
        query_params = {'q.field': [],
 | 
			
		||||
                        'q.value': [],
 | 
			
		||||
@@ -39,7 +55,7 @@ def build_url(path, q, params=None):
 | 
			
		||||
        # Transform the dict to a sequence of two-element tuples in fixed
 | 
			
		||||
        # order, then the encoded string will be consistent in Python 2&3.
 | 
			
		||||
        new_qparams = sorted(query_params.items(), key=lambda x: x[0])
 | 
			
		||||
        path += "?" + parse.urlencode(new_qparams, doseq=True)
 | 
			
		||||
        path += "?" + urllib.parse.urlencode(new_qparams, doseq=True)
 | 
			
		||||
 | 
			
		||||
        if params:
 | 
			
		||||
            for p in params:
 | 
			
		||||
@@ -52,56 +68,53 @@ def build_url(path, q, params=None):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def cli_to_array(cli_query):
 | 
			
		||||
    """This converts from the cli list of queries to what is required
 | 
			
		||||
    by the python api.
 | 
			
		||||
    so from:
 | 
			
		||||
    "this<=34;that=foo"
 | 
			
		||||
    """Convert CLI list of queries to the Python API format.
 | 
			
		||||
 | 
			
		||||
    This will convert the following:
 | 
			
		||||
        "this<=34;that=string::foo"
 | 
			
		||||
    to
 | 
			
		||||
    "[{field=this,op=le,value=34},{field=that,op=eq,value=foo}]"
 | 
			
		||||
        "[{field=this,op=le,value=34,type=''},
 | 
			
		||||
          {field=that,op=eq,value=foo,type=string}]"
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    if cli_query is None:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    op_lookup = {'!=': 'ne',
 | 
			
		||||
                 '>=': 'ge',
 | 
			
		||||
                 '<=': 'le',
 | 
			
		||||
                 '>': 'gt',
 | 
			
		||||
                 '<': 'lt',
 | 
			
		||||
                 '=': 'eq'}
 | 
			
		||||
    def split_by_op(query):
 | 
			
		||||
        """Split a single query string to field, operator, value."""
 | 
			
		||||
 | 
			
		||||
    def split_by_op(string):
 | 
			
		||||
        # two character split (<=,!=)
 | 
			
		||||
        frags = re.findall(r'([[a-zA-Z0-9_.]+)([><!]=)([^ -,\t\n\r\f\v]+)',
 | 
			
		||||
                           string)
 | 
			
		||||
        if len(frags) == 0:
 | 
			
		||||
            #single char split (<,=)
 | 
			
		||||
            frags = re.findall(r'([a-zA-Z0-9_.]+)([><=])([^ -,\t\n\r\f\v]+)',
 | 
			
		||||
                               string)
 | 
			
		||||
        return frags
 | 
			
		||||
        def _value_error(message):
 | 
			
		||||
            raise ValueError('invalid query %(query)s: missing %(message)s' %
 | 
			
		||||
                             {'query': query, 'message': message})
 | 
			
		||||
 | 
			
		||||
    def split_by_data_type(string):
 | 
			
		||||
        frags = re.findall(r'^(string|integer|float|datetime|boolean)(::)'
 | 
			
		||||
                           r'([^ -,\t\n\r\f\v]+)$', string)
 | 
			
		||||
        try:
 | 
			
		||||
            field, operator, value = OP_SPLIT_RE.split(query, maxsplit=1)
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            _value_error('operator')
 | 
			
		||||
 | 
			
		||||
        # frags[1] is the separator. Return a list without it if the type
 | 
			
		||||
        # identifier was found.
 | 
			
		||||
        return [frags[0][0], frags[0][2]] if frags else None
 | 
			
		||||
        if not len(field):
 | 
			
		||||
            _value_error('field')
 | 
			
		||||
 | 
			
		||||
        if not len(value):
 | 
			
		||||
            _value_error('value')
 | 
			
		||||
 | 
			
		||||
        return field, operator, value
 | 
			
		||||
 | 
			
		||||
    def split_by_data_type(query_value):
 | 
			
		||||
        frags = DATA_TYPE_RE.match(query_value)
 | 
			
		||||
 | 
			
		||||
        # The second match is the separator. Return a list without it if
 | 
			
		||||
        # a type identifier was found.
 | 
			
		||||
        return frags.group(1, 3) if frags else None
 | 
			
		||||
 | 
			
		||||
    opts = []
 | 
			
		||||
    queries = cli_query.split(';')
 | 
			
		||||
    for q in queries:
 | 
			
		||||
        frag = split_by_op(q)
 | 
			
		||||
        if len(frag) > 1:
 | 
			
		||||
            raise ValueError('incorrect separator %s in query "%s"' %
 | 
			
		||||
                             ('(should be ";")', q))
 | 
			
		||||
        if len(frag) == 0:
 | 
			
		||||
            raise ValueError('invalid query %s' % q)
 | 
			
		||||
        query = frag[0]
 | 
			
		||||
        query = split_by_op(q)
 | 
			
		||||
        opt = {}
 | 
			
		||||
        opt['field'] = query[0]
 | 
			
		||||
        opt['op'] = op_lookup[query[1]]
 | 
			
		||||
        opt['op'] = OP_LOOKUP[query[1]]
 | 
			
		||||
 | 
			
		||||
        # Allow the data type of the value to be specified via <type>::<value>,
 | 
			
		||||
        # where type can be one of integer, string, float, datetime, boolean
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ from ceilometerclient.v2 import samples
 | 
			
		||||
class QueryManager(base.Manager):
 | 
			
		||||
    path_suffix = None
 | 
			
		||||
 | 
			
		||||
    def query(self, filter, orderby, limit):
 | 
			
		||||
    def query(self, filter=None, orderby=None, limit=None):
 | 
			
		||||
        query = {}
 | 
			
		||||
        if filter:
 | 
			
		||||
            query["filter"] = filter
 | 
			
		||||
@@ -33,9 +33,9 @@ class QueryManager(base.Manager):
 | 
			
		||||
            query["limit"] = limit
 | 
			
		||||
 | 
			
		||||
        url = '/v2/query%s' % self.path_suffix
 | 
			
		||||
        resp, body = self.api.json_request('POST',
 | 
			
		||||
                                           url,
 | 
			
		||||
                                           body=query)
 | 
			
		||||
 | 
			
		||||
        body = self.api.post(url, json=query).json()
 | 
			
		||||
 | 
			
		||||
        if body:
 | 
			
		||||
            return [self.resource_class(self, b) for b in body]
 | 
			
		||||
        else:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
# Copyright © 2013 Red Hat, Inc
 | 
			
		||||
# Copyright 2013 Red Hat, Inc
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
@@ -22,6 +21,11 @@ class Resource(base.Resource):
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return "<Resource %s>" % self._info
 | 
			
		||||
 | 
			
		||||
    def __getattr__(self, k):
 | 
			
		||||
        if k == 'id':
 | 
			
		||||
            return self.resource_id
 | 
			
		||||
        return super(Resource, self).__getattr__(k)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceManager(base.Manager):
 | 
			
		||||
    resource_class = Resource
 | 
			
		||||
 
 | 
			
		||||
@@ -26,13 +26,18 @@ CREATION_ATTRIBUTES = ('source',
 | 
			
		||||
                       'resource_metadata')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Sample(base.Resource):
 | 
			
		||||
class OldSample(base.Resource):
 | 
			
		||||
    """Represents API v2 OldSample object.
 | 
			
		||||
 | 
			
		||||
    Model definition:
 | 
			
		||||
    http://docs.openstack.org/developer/ceilometer/webapi/v2.html#OldSample
 | 
			
		||||
    """
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return "<Sample %s>" % self._info
 | 
			
		||||
        return "<OldSample %s>" % self._info
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SampleManager(base.Manager):
 | 
			
		||||
    resource_class = Sample
 | 
			
		||||
class OldSampleManager(base.Manager):
 | 
			
		||||
    resource_class = OldSample
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _path(counter_name=None):
 | 
			
		||||
@@ -47,8 +52,31 @@ class SampleManager(base.Manager):
 | 
			
		||||
        new = dict((key, value) for (key, value) in kwargs.items()
 | 
			
		||||
                   if key in CREATION_ATTRIBUTES)
 | 
			
		||||
        url = self._path(counter_name=kwargs['counter_name'])
 | 
			
		||||
        resp, body = self.api.json_request('POST',
 | 
			
		||||
                                           url,
 | 
			
		||||
                                           body=[new])
 | 
			
		||||
        body = self.api.post(url, json=[new]).json()
 | 
			
		||||
        if body:
 | 
			
		||||
            return [Sample(self, b) for b in body]
 | 
			
		||||
            return [OldSample(self, b) for b in body]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Sample(base.Resource):
 | 
			
		||||
    """Represents API v2 Sample object.
 | 
			
		||||
 | 
			
		||||
    Model definition:
 | 
			
		||||
    http://docs.openstack.org/developer/ceilometer/webapi/v2.html#Sample
 | 
			
		||||
    """
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return "<Sample %s>" % self._info
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SampleManager(base.Manager):
 | 
			
		||||
    resource_class = Sample
 | 
			
		||||
 | 
			
		||||
    def list(self, q=None, limit=None):
 | 
			
		||||
        params = ['limit=%s' % str(limit)] if limit else None
 | 
			
		||||
        return self._list(options.build_url("/v2/samples", q, params))
 | 
			
		||||
 | 
			
		||||
    def get(self, sample_id):
 | 
			
		||||
        path = "/v2/samples/" + sample_id
 | 
			
		||||
        try:
 | 
			
		||||
            return self._list(path, expect_single=True)[0]
 | 
			
		||||
        except IndexError:
 | 
			
		||||
            return None
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
# Copyright © 2013 Red Hat, Inc
 | 
			
		||||
# Copyright 2013 Red Hat, Inc
 | 
			
		||||
# Copyright Ericsson AB 2014. All rights reserved
 | 
			
		||||
#
 | 
			
		||||
# Authors: Angus Salkeld <asalkeld@redhat.com>
 | 
			
		||||
@@ -19,17 +18,20 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import functools
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
from oslo.utils import strutils
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.common import utils
 | 
			
		||||
from ceilometerclient import exc
 | 
			
		||||
from ceilometerclient.openstack.common import strutils
 | 
			
		||||
from ceilometerclient.v2 import options
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ALARM_STATES = ['ok', 'alarm', 'insufficient_data']
 | 
			
		||||
ALARM_STATES = ['ok', 'alarm', 'insufficient data']
 | 
			
		||||
ALARM_SEVERITY = ['low', 'moderate', 'critical']
 | 
			
		||||
ALARM_OPERATORS = ['lt', 'le', 'eq', 'ne', 'ge', 'gt']
 | 
			
		||||
ALARM_COMBINATION_OPERATORS = ['and', 'or']
 | 
			
		||||
STATISTICS = ['max', 'min', 'avg', 'sum', 'count']
 | 
			
		||||
@@ -48,11 +50,28 @@ COMPLEX_OPERATORS = ['and', 'or']
 | 
			
		||||
SIMPLE_OPERATORS = ["=", "!=", "<", "<=", '>', '>=']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NotEmptyAction(argparse.Action):
 | 
			
		||||
    def __call__(self, parser, namespace, values, option_string=None):
 | 
			
		||||
        values = values or getattr(namespace, self.dest)
 | 
			
		||||
        if not values or values.isspace():
 | 
			
		||||
            raise exc.CommandError('%s should not be empty' % self.dest)
 | 
			
		||||
        setattr(namespace, self.dest, values)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def obsoleted_by(new_dest):
 | 
			
		||||
    class ObsoletedByAction(argparse.Action):
 | 
			
		||||
        def __call__(self, parser, namespace, values, option_string=None):
 | 
			
		||||
            old_dest = option_string or self.dest
 | 
			
		||||
            print('%s is obsolete! See help for more details.' % old_dest)
 | 
			
		||||
            setattr(namespace, new_dest, values)
 | 
			
		||||
    return ObsoletedByAction
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-q', '--query', metavar='<QUERY>',
 | 
			
		||||
           help='key[op]data_type::value; list. data_type is optional, '
 | 
			
		||||
                'but if supplied must be string, integer, float, or boolean.')
 | 
			
		||||
@utils.arg('-m', '--meter', metavar='<NAME>', required=True,
 | 
			
		||||
           help='Name of meter to show samples for.')
 | 
			
		||||
           action=NotEmptyAction, help='Name of meter to list statistics for.')
 | 
			
		||||
@utils.arg('-p', '--period', metavar='<PERIOD>',
 | 
			
		||||
           help='Period in seconds over which to group samples.')
 | 
			
		||||
@utils.arg('-g', '--groupby', metavar='<FIELD>', action='append',
 | 
			
		||||
@@ -62,7 +81,7 @@ SIMPLE_OPERATORS = ["=", "!=", "<", "<=", '>', '>=']
 | 
			
		||||
                             'Available aggregates are: '
 | 
			
		||||
                             '%s.' % ", ".join(AGGREGATES.keys())))
 | 
			
		||||
def do_statistics(cc, args):
 | 
			
		||||
    '''List the statistics for a meter.'''
 | 
			
		||||
    """List the statistics for a meter."""
 | 
			
		||||
    aggregates = []
 | 
			
		||||
    for a in args.aggregate:
 | 
			
		||||
        aggregates.append(dict(zip(('func', 'param'), a.split("<-"))))
 | 
			
		||||
@@ -105,12 +124,19 @@ def do_statistics(cc, args):
 | 
			
		||||
@utils.arg('-q', '--query', metavar='<QUERY>',
 | 
			
		||||
           help='key[op]data_type::value; list. data_type is optional, '
 | 
			
		||||
                'but if supplied must be string, integer, float, or boolean.')
 | 
			
		||||
@utils.arg('-m', '--meter', metavar='<NAME>', required=True,
 | 
			
		||||
           help='Name of meter to show samples for.')
 | 
			
		||||
@utils.arg('-m', '--meter', metavar='<NAME>',
 | 
			
		||||
           action=NotEmptyAction, help='Name of meter to show samples for.')
 | 
			
		||||
@utils.arg('-l', '--limit', metavar='<NUMBER>',
 | 
			
		||||
           help='Maximum number of samples to return.')
 | 
			
		||||
def do_sample_list(cc, args):
 | 
			
		||||
    '''List the samples for a meter.'''
 | 
			
		||||
    """List the samples (return OldSample objects if -m/--meter is set)."""
 | 
			
		||||
    if not args.meter:
 | 
			
		||||
        return _do_sample_list(cc, args)
 | 
			
		||||
    else:
 | 
			
		||||
        return _do_old_sample_list(cc, args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _do_old_sample_list(cc, args):
 | 
			
		||||
    fields = {'meter_name': args.meter,
 | 
			
		||||
              'q': options.cli_to_array(args.query),
 | 
			
		||||
              'limit': args.limit}
 | 
			
		||||
@@ -123,8 +149,36 @@ def do_sample_list(cc, args):
 | 
			
		||||
                        'Timestamp']
 | 
			
		||||
        fields = ['resource_id', 'counter_name', 'counter_type',
 | 
			
		||||
                  'counter_volume', 'counter_unit', 'timestamp']
 | 
			
		||||
        utils.print_list(samples, fields, field_labels,
 | 
			
		||||
                         sortby=None)
 | 
			
		||||
        utils.print_list(samples, fields, field_labels, sortby=None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _do_sample_list(cc, args):
 | 
			
		||||
    fields = {
 | 
			
		||||
        'q': options.cli_to_array(args.query),
 | 
			
		||||
        'limit': args.limit
 | 
			
		||||
    }
 | 
			
		||||
    samples = cc.new_samples.list(**fields)
 | 
			
		||||
    field_labels = ['ID', 'Resource ID', 'Name', 'Type', 'Volume', 'Unit',
 | 
			
		||||
                    'Timestamp']
 | 
			
		||||
    fields = ['id', 'resource_id', 'meter', 'type', 'volume', 'unit',
 | 
			
		||||
              'timestamp']
 | 
			
		||||
    utils.print_list(samples, fields, field_labels, sortby=None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('sample_id', metavar='<SAMPLE_ID>', action=NotEmptyAction,
 | 
			
		||||
           help='ID (aka message ID) of the sample to show.')
 | 
			
		||||
def do_sample_show(cc, args):
 | 
			
		||||
    '''Show an sample.'''
 | 
			
		||||
    sample = cc.new_samples.get(args.sample_id)
 | 
			
		||||
 | 
			
		||||
    if sample is None:
 | 
			
		||||
        raise exc.CommandError('Sample not found: %s' % args.sample_id)
 | 
			
		||||
 | 
			
		||||
    fields = ['id', 'meter', 'volume', 'type', 'unit', 'source',
 | 
			
		||||
              'resource_id', 'user_id', 'project_id',
 | 
			
		||||
              'timestamp', 'recorded_at', 'metadata']
 | 
			
		||||
    data = dict((f, getattr(sample, f, '')) for f in fields)
 | 
			
		||||
    utils.print_dict(data, wrap=72)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('--project-id', metavar='<PROJECT_ID>',
 | 
			
		||||
@@ -136,7 +190,7 @@ def do_sample_list(cc, args):
 | 
			
		||||
@utils.arg('-r', '--resource-id', metavar='<RESOURCE_ID>', required=True,
 | 
			
		||||
           help='ID of the resource.')
 | 
			
		||||
@utils.arg('-m', '--meter-name', metavar='<METER_NAME>', required=True,
 | 
			
		||||
           help='The meter name.')
 | 
			
		||||
           action=NotEmptyAction, help='The meter name.')
 | 
			
		||||
@utils.arg('--meter-type', metavar='<METER_TYPE>', required=True,
 | 
			
		||||
           help='The meter type.')
 | 
			
		||||
@utils.arg('--meter-unit', metavar='<METER_UNIT>', required=True,
 | 
			
		||||
@@ -144,11 +198,12 @@ def do_sample_list(cc, args):
 | 
			
		||||
@utils.arg('--sample-volume', metavar='<SAMPLE_VOLUME>', required=True,
 | 
			
		||||
           help='The sample volume.')
 | 
			
		||||
@utils.arg('--resource-metadata', metavar='<RESOURCE_METADATA>',
 | 
			
		||||
           help='Resource metadata.')
 | 
			
		||||
           help='Resource metadata. Provided value should be a set of '
 | 
			
		||||
                'key-value pairs e.g. {"key":"value"}.')
 | 
			
		||||
@utils.arg('--timestamp', metavar='<TIMESTAMP>',
 | 
			
		||||
           help='The sample timestamp.')
 | 
			
		||||
def do_sample_create(cc, args={}):
 | 
			
		||||
    '''Create a sample.'''
 | 
			
		||||
    """Create a sample."""
 | 
			
		||||
    arg_to_field_mapping = {'meter_name': 'counter_name',
 | 
			
		||||
                            'meter_unit': 'counter_unit',
 | 
			
		||||
                            'meter_type': 'counter_type',
 | 
			
		||||
@@ -175,7 +230,7 @@ def do_sample_create(cc, args={}):
 | 
			
		||||
           help='key[op]data_type::value; list. data_type is optional, '
 | 
			
		||||
                'but if supplied must be string, integer, float, or boolean.')
 | 
			
		||||
def do_meter_list(cc, args={}):
 | 
			
		||||
    '''List the user's meters.'''
 | 
			
		||||
    """List the user's meters."""
 | 
			
		||||
    meters = cc.meters.list(q=options.cli_to_array(args.query))
 | 
			
		||||
    field_labels = ['Name', 'Type', 'Unit', 'Resource ID', 'User ID',
 | 
			
		||||
                    'Project ID']
 | 
			
		||||
@@ -185,6 +240,20 @@ def do_meter_list(cc, args={}):
 | 
			
		||||
                     sortby=0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _display_alarm_list(alarms, sortby=None):
 | 
			
		||||
    # omit action initially to keep output width sane
 | 
			
		||||
    # (can switch over to vertical formatting when available from CLIFF)
 | 
			
		||||
    field_labels = ['Alarm ID', 'Name', 'State', 'Severity', 'Enabled',
 | 
			
		||||
                    'Continuous', 'Alarm condition', 'Time constraints']
 | 
			
		||||
    fields = ['alarm_id', 'name', 'state', 'severity', 'enabled',
 | 
			
		||||
              'repeat_actions', 'rule', 'time_constraints']
 | 
			
		||||
    utils.print_list(
 | 
			
		||||
        alarms, fields, field_labels,
 | 
			
		||||
        formatters={'rule': alarm_rule_formatter,
 | 
			
		||||
                    'time_constraints': time_constraints_formatter_brief},
 | 
			
		||||
        sortby=sortby)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _display_rule(type, rule):
 | 
			
		||||
    if type == 'threshold':
 | 
			
		||||
        return ('%(meter_name)s %(comparison_operator)s '
 | 
			
		||||
@@ -204,14 +273,14 @@ def _display_rule(type, rule):
 | 
			
		||||
    else:
 | 
			
		||||
        # just dump all
 | 
			
		||||
        return "\n".join(["%s: %s" % (f, v)
 | 
			
		||||
                          for f, v in rule.iteritems()])
 | 
			
		||||
                          for f, v in six.iteritems(rule)])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def alarm_rule_formatter(alarm):
 | 
			
		||||
    return _display_rule(alarm.type, alarm.rule)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _display_time_constraints(time_constraints):
 | 
			
		||||
def _display_time_constraints_brief(time_constraints):
 | 
			
		||||
    if time_constraints:
 | 
			
		||||
        return ', '.join('%(name)s at %(start)s %(timezone)s for %(duration)ss'
 | 
			
		||||
                         % {
 | 
			
		||||
@@ -225,8 +294,10 @@ def _display_time_constraints(time_constraints):
 | 
			
		||||
        return 'None'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def time_constraints_formatter(alarm):
 | 
			
		||||
    return _display_time_constraints(alarm.time_constraints)
 | 
			
		||||
def time_constraints_formatter_brief(alarm):
 | 
			
		||||
    return _display_time_constraints_brief(getattr(alarm,
 | 
			
		||||
                                                   'time_constraints',
 | 
			
		||||
                                                   None))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _infer_type(detail):
 | 
			
		||||
@@ -254,7 +325,7 @@ def alarm_change_detail_formatter(change):
 | 
			
		||||
                fields.append('%s: %s' % (k, detail[k]))
 | 
			
		||||
        if 'time_constraints' in detail:
 | 
			
		||||
            fields.append('time_constraints: %s' %
 | 
			
		||||
                          _display_time_constraints(
 | 
			
		||||
                          _display_time_constraints_brief(
 | 
			
		||||
                              detail['time_constraints']))
 | 
			
		||||
    elif change.type == 'rule change':
 | 
			
		||||
        for k, v in six.iteritems(detail):
 | 
			
		||||
@@ -270,18 +341,9 @@ def alarm_change_detail_formatter(change):
 | 
			
		||||
           help='key[op]data_type::value; list. data_type is optional, '
 | 
			
		||||
                'but if supplied must be string, integer, float, or boolean.')
 | 
			
		||||
def do_alarm_list(cc, args={}):
 | 
			
		||||
    '''List the user's alarms.'''
 | 
			
		||||
    """List the user's alarms."""
 | 
			
		||||
    alarms = cc.alarms.list(q=options.cli_to_array(args.query))
 | 
			
		||||
    # omit action initially to keep output width sane
 | 
			
		||||
    # (can switch over to vertical formatting when available from CLIFF)
 | 
			
		||||
    field_labels = ['Alarm ID', 'Name', 'State', 'Enabled', 'Continuous',
 | 
			
		||||
                    'Alarm condition', 'Time constraints']
 | 
			
		||||
    fields = ['alarm_id', 'name', 'state', 'enabled', 'repeat_actions',
 | 
			
		||||
              'rule', 'time_constraints']
 | 
			
		||||
    utils.print_list(
 | 
			
		||||
        alarms, fields, field_labels,
 | 
			
		||||
        formatters={'rule': alarm_rule_formatter,
 | 
			
		||||
                    'time_constraints': time_constraints_formatter}, sortby=0)
 | 
			
		||||
    _display_alarm_list(alarms, sortby=0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def alarm_query_formater(alarm):
 | 
			
		||||
@@ -292,7 +354,7 @@ def alarm_query_formater(alarm):
 | 
			
		||||
    return r' AND\n'.join(qs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def alarm_time_constraints_formatter(alarm):
 | 
			
		||||
def time_constraints_formatter_full(alarm):
 | 
			
		||||
    time_constraints = []
 | 
			
		||||
    for tc in alarm.time_constraints:
 | 
			
		||||
        lines = []
 | 
			
		||||
@@ -305,25 +367,27 @@ def alarm_time_constraints_formatter(alarm):
 | 
			
		||||
 | 
			
		||||
def _display_alarm(alarm):
 | 
			
		||||
    fields = ['name', 'description', 'type',
 | 
			
		||||
              'state', 'enabled', 'alarm_id', 'user_id', 'project_id',
 | 
			
		||||
              'alarm_actions', 'ok_actions', 'insufficient_data_actions',
 | 
			
		||||
              'repeat_actions']
 | 
			
		||||
              'state', 'severity', 'enabled', 'alarm_id', 'user_id',
 | 
			
		||||
              'project_id', 'alarm_actions', 'ok_actions',
 | 
			
		||||
              'insufficient_data_actions', 'repeat_actions']
 | 
			
		||||
    data = dict([(f, getattr(alarm, f, '')) for f in fields])
 | 
			
		||||
    data.update(alarm.rule)
 | 
			
		||||
    if alarm.type == 'threshold':
 | 
			
		||||
        data['query'] = alarm_query_formater(alarm)
 | 
			
		||||
    if alarm.time_constraints:
 | 
			
		||||
        data['time_constraints'] = alarm_time_constraints_formatter(alarm)
 | 
			
		||||
        data['time_constraints'] = time_constraints_formatter_full(alarm)
 | 
			
		||||
    utils.print_dict(data, wrap=72)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
 | 
			
		||||
           help='ID of the alarm to show.')
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
 | 
			
		||||
           action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
 | 
			
		||||
           dest='alarm_id_deprecated')
 | 
			
		||||
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
 | 
			
		||||
           action=NotEmptyAction, help='ID of the alarm to show.')
 | 
			
		||||
def do_alarm_show(cc, args={}):
 | 
			
		||||
    '''Show an alarm.'''
 | 
			
		||||
    try:
 | 
			
		||||
    """Show an alarm."""
 | 
			
		||||
    alarm = cc.alarms.get(args.alarm_id)
 | 
			
		||||
    except exc.HTTPNotFound:
 | 
			
		||||
    if alarm is None:
 | 
			
		||||
        raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
 | 
			
		||||
    else:
 | 
			
		||||
        _display_alarm(alarm)
 | 
			
		||||
@@ -343,6 +407,9 @@ def common_alarm_arguments(create=False):
 | 
			
		||||
                   help='Free text description of the alarm.')
 | 
			
		||||
        @utils.arg('--state', metavar='<STATE>',
 | 
			
		||||
                   help='State of the alarm, one of: ' + str(ALARM_STATES))
 | 
			
		||||
        @utils.arg('--severity', metavar='<SEVERITY>',
 | 
			
		||||
                   help='Severity of the alarm, one of: '
 | 
			
		||||
                        + str(ALARM_SEVERITY))
 | 
			
		||||
        @utils.arg('--enabled', type=strutils.bool_from_string,
 | 
			
		||||
                   metavar='{True|False}',
 | 
			
		||||
                   help='True if alarm evaluation/actioning is enabled.')
 | 
			
		||||
@@ -358,7 +425,7 @@ def common_alarm_arguments(create=False):
 | 
			
		||||
                   dest='insufficient_data_actions',
 | 
			
		||||
                   metavar='<Webhook URL>', action='append', default=None,
 | 
			
		||||
                   help=('URL to invoke when state transitions to '
 | 
			
		||||
                         'insufficient_data. May be used multiple times.'))
 | 
			
		||||
                         'insufficient data. May be used multiple times.'))
 | 
			
		||||
        @utils.arg('--time-constraint', dest='time_constraints',
 | 
			
		||||
                   metavar='<Time Constraint>', action='append',
 | 
			
		||||
                   default=None,
 | 
			
		||||
@@ -378,6 +445,73 @@ def common_alarm_arguments(create=False):
 | 
			
		||||
    return _wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def common_alarm_gnocchi_arguments(rule_namespace, create=False):
 | 
			
		||||
    def _wrapper(func):
 | 
			
		||||
        @utils.arg('--granularity', type=int, metavar='<GRANULARITY>',
 | 
			
		||||
                   dest=rule_namespace + '/granularity',
 | 
			
		||||
                   help='Length of each period (seconds) to evaluate over.')
 | 
			
		||||
        @utils.arg('--evaluation-periods', type=int, metavar='<COUNT>',
 | 
			
		||||
                   dest=rule_namespace + '/evaluation_periods',
 | 
			
		||||
                   help='Number of periods to evaluate over.')
 | 
			
		||||
        @utils.arg('--aggregation-method', metavar='<AGGREATION>',
 | 
			
		||||
                   dest=rule_namespace + '/aggregation_method',
 | 
			
		||||
                   help=('Aggregation method to use, one of: ' +
 | 
			
		||||
                         str(STATISTICS) + '.'))
 | 
			
		||||
        @utils.arg('--comparison-operator', metavar='<OPERATOR>',
 | 
			
		||||
                   dest=rule_namespace + '/comparison_operator',
 | 
			
		||||
                   help=('Operator to compare with, one of: ' +
 | 
			
		||||
                         str(ALARM_OPERATORS) + '.'))
 | 
			
		||||
        @utils.arg('--threshold', type=float, metavar='<THRESHOLD>',
 | 
			
		||||
                   dest=rule_namespace + '/threshold',
 | 
			
		||||
                   required=create,
 | 
			
		||||
                   help='Threshold to evaluate against.')
 | 
			
		||||
        @utils.arg('--repeat-actions', dest='repeat_actions',
 | 
			
		||||
                   metavar='{True|False}', type=strutils.bool_from_string,
 | 
			
		||||
                   default=False,
 | 
			
		||||
                   help=('True if actions should be repeatedly notified '
 | 
			
		||||
                         'while alarm remains in target state.'))
 | 
			
		||||
        @functools.wraps(func)
 | 
			
		||||
        def _wrapped(*args, **kwargs):
 | 
			
		||||
            return func(*args, **kwargs)
 | 
			
		||||
        return _wrapped
 | 
			
		||||
    return _wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def common_alarm_gnocchi_metrics_arguments(create=False):
 | 
			
		||||
    def _wrapper(func):
 | 
			
		||||
        @utils.arg('-m', '--metrics', metavar='<METRICS>',
 | 
			
		||||
                   dest='gnocchi_metrics_threshold_rule/meter_name',
 | 
			
		||||
                   action='append', required=create,
 | 
			
		||||
                   help='Metric to evaluate against.')
 | 
			
		||||
        @functools.wraps(func)
 | 
			
		||||
        def _wrapped(*args, **kwargs):
 | 
			
		||||
            return func(*args, **kwargs)
 | 
			
		||||
        return _wrapped
 | 
			
		||||
    return _wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def common_alarm_gnocchi_resources_arguments(create=False):
 | 
			
		||||
    def _wrapper(func):
 | 
			
		||||
        @utils.arg('-m', '--metric', metavar='<METRIC>',
 | 
			
		||||
                   dest='gnocchi_resources_threshold_rule/metric',
 | 
			
		||||
                   required=create,
 | 
			
		||||
                   help='Metric to evaluate against.')
 | 
			
		||||
        @utils.arg('--resource-type', metavar='<RESOURCE_TYPE>',
 | 
			
		||||
                   dest='gnocchi_resources_threshold_rule/resource_type',
 | 
			
		||||
                   required=create,
 | 
			
		||||
                   help='Resource_type to evaluate against.')
 | 
			
		||||
        @utils.arg('--resource-constraint', metavar='<RESOURCE_CONSTRAINT>',
 | 
			
		||||
                   dest='gnocchi_resources_threshold_rule/resource_constraint',
 | 
			
		||||
                   required=create,
 | 
			
		||||
                   help=('Resources to evaluate against or a expression '
 | 
			
		||||
                         'to select multiple resources.'))
 | 
			
		||||
        @functools.wraps(func)
 | 
			
		||||
        def _wrapped(*args, **kwargs):
 | 
			
		||||
            return func(*args, **kwargs)
 | 
			
		||||
        return _wrapped
 | 
			
		||||
    return _wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@common_alarm_arguments(create=True)
 | 
			
		||||
@utils.arg('--period', type=int, metavar='<PERIOD>',
 | 
			
		||||
           help='Length of each period (seconds) to evaluate over.')
 | 
			
		||||
@@ -402,7 +536,7 @@ def common_alarm_arguments(create=False):
 | 
			
		||||
           help=('True if actions should be repeatedly notified '
 | 
			
		||||
                 'while alarm remains in target state.'))
 | 
			
		||||
def do_alarm_create(cc, args={}):
 | 
			
		||||
    '''Create a new alarm (Deprecated). Use alarm-threshold-create instead.'''
 | 
			
		||||
    """Create a new alarm (Deprecated). Use alarm-threshold-create instead."""
 | 
			
		||||
    fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
 | 
			
		||||
    fields = utils.args_array_to_list_of_dicts(fields, "time_constraints")
 | 
			
		||||
    fields = utils.args_array_to_dict(fields, "matching_metadata")
 | 
			
		||||
@@ -410,6 +544,33 @@ def do_alarm_create(cc, args={}):
 | 
			
		||||
    _display_alarm(alarm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@common_alarm_arguments(create=True)
 | 
			
		||||
@common_alarm_gnocchi_arguments('gnocchi_resources_threshold_rule',
 | 
			
		||||
                                create=True)
 | 
			
		||||
@common_alarm_gnocchi_resources_arguments(create=True)
 | 
			
		||||
def do_alarm_gnocchi_resources_threshold_create(cc, args={}):
 | 
			
		||||
    """Create a new alarm based on computed statistics."""
 | 
			
		||||
    fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
 | 
			
		||||
    fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints')
 | 
			
		||||
    fields = utils.key_with_slash_to_nested_dict(fields)
 | 
			
		||||
    fields['type'] = 'gnocchi_resources_threshold'
 | 
			
		||||
    alarm = cc.alarms.create(**fields)
 | 
			
		||||
    _display_alarm(alarm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@common_alarm_arguments(create=True)
 | 
			
		||||
@common_alarm_gnocchi_arguments('gnocchi_metrics_threshold_rule', create=True)
 | 
			
		||||
@common_alarm_gnocchi_metrics_arguments(create=True)
 | 
			
		||||
def do_alarm_gnocchi_metrics_threshold_create(cc, args={}):
 | 
			
		||||
    """Create a new alarm based on computed statistics."""
 | 
			
		||||
    fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
 | 
			
		||||
    fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints')
 | 
			
		||||
    fields = utils.key_with_slash_to_nested_dict(fields)
 | 
			
		||||
    fields['type'] = 'gnocchi_metrics_threshold'
 | 
			
		||||
    alarm = cc.alarms.create(**fields)
 | 
			
		||||
    _display_alarm(alarm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@common_alarm_arguments(create=True)
 | 
			
		||||
@utils.arg('-m', '--meter-name', metavar='<METRIC>', required=True,
 | 
			
		||||
           dest='threshold_rule/meter_name',
 | 
			
		||||
@@ -440,7 +601,7 @@ def do_alarm_create(cc, args={}):
 | 
			
		||||
           help=('True if actions should be repeatedly notified '
 | 
			
		||||
                 'while alarm remains in target state.'))
 | 
			
		||||
def do_alarm_threshold_create(cc, args={}):
 | 
			
		||||
    '''Create a new alarm based on computed statistics.'''
 | 
			
		||||
    """Create a new alarm based on computed statistics."""
 | 
			
		||||
    fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
 | 
			
		||||
    fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints')
 | 
			
		||||
    fields = utils.key_with_slash_to_nested_dict(fields)
 | 
			
		||||
@@ -455,7 +616,7 @@ def do_alarm_threshold_create(cc, args={}):
 | 
			
		||||
@common_alarm_arguments(create=True)
 | 
			
		||||
@utils.arg('--alarm_ids', action='append', metavar='<ALARM IDS>',
 | 
			
		||||
           required=True, dest='combination_rule/alarm_ids',
 | 
			
		||||
           help='List of alarm ids.')
 | 
			
		||||
           help='List of alarm IDs.')
 | 
			
		||||
@utils.arg('--operator', metavar='<OPERATOR>',
 | 
			
		||||
           dest='combination_rule/operator',
 | 
			
		||||
           help='Operator to compare with, one of: ' + str(
 | 
			
		||||
@@ -466,7 +627,7 @@ def do_alarm_threshold_create(cc, args={}):
 | 
			
		||||
           help=('True if actions should be repeatedly notified '
 | 
			
		||||
                 'while alarm remains in target state.'))
 | 
			
		||||
def do_alarm_combination_create(cc, args={}):
 | 
			
		||||
    '''Create a new alarm based on state of other alarms.'''
 | 
			
		||||
    """Create a new alarm based on state of other alarms."""
 | 
			
		||||
    fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
 | 
			
		||||
    fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints')
 | 
			
		||||
    fields = utils.key_with_slash_to_nested_dict(fields)
 | 
			
		||||
@@ -475,8 +636,11 @@ def do_alarm_combination_create(cc, args={}):
 | 
			
		||||
    _display_alarm(alarm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
 | 
			
		||||
           help='ID of the alarm to update.')
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
 | 
			
		||||
           action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
 | 
			
		||||
           dest='alarm_id_deprecated')
 | 
			
		||||
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
 | 
			
		||||
           action=NotEmptyAction, help='ID of the alarm to update.')
 | 
			
		||||
@common_alarm_arguments()
 | 
			
		||||
@utils.arg('--remove-time-constraint', action='append',
 | 
			
		||||
           metavar='<Constraint names>',
 | 
			
		||||
@@ -504,7 +668,7 @@ def do_alarm_combination_create(cc, args={}):
 | 
			
		||||
           help=('True if actions should be repeatedly notified '
 | 
			
		||||
                 'while alarm remains in target state.'))
 | 
			
		||||
def do_alarm_update(cc, args={}):
 | 
			
		||||
    '''Update an existing alarm (Deprecated).'''
 | 
			
		||||
    """Update an existing alarm (Deprecated)."""
 | 
			
		||||
    fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
 | 
			
		||||
    fields = utils.args_array_to_list_of_dicts(fields, "time_constraints")
 | 
			
		||||
    fields = utils.args_array_to_dict(fields, "matching_metadata")
 | 
			
		||||
@@ -516,8 +680,11 @@ def do_alarm_update(cc, args={}):
 | 
			
		||||
    _display_alarm(alarm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
 | 
			
		||||
           help='ID of the alarm to update.')
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
 | 
			
		||||
           action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
 | 
			
		||||
           dest='alarm_id_deprecated')
 | 
			
		||||
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
 | 
			
		||||
           action=NotEmptyAction, help='ID of the alarm to update.')
 | 
			
		||||
@common_alarm_arguments()
 | 
			
		||||
@utils.arg('--remove-time-constraint', action='append',
 | 
			
		||||
           metavar='<Constraint names>',
 | 
			
		||||
@@ -544,6 +711,7 @@ def do_alarm_update(cc, args={}):
 | 
			
		||||
           dest='threshold_rule/threshold',
 | 
			
		||||
           help='Threshold to evaluate against.')
 | 
			
		||||
@utils.arg('-q', '--query', metavar='<QUERY>',
 | 
			
		||||
           dest='threshold_rule/query',
 | 
			
		||||
           help='key[op]data_type::value; list. data_type is optional, '
 | 
			
		||||
                'but if supplied must be string, integer, float, or boolean.')
 | 
			
		||||
@utils.arg('--repeat-actions', dest='repeat_actions',
 | 
			
		||||
@@ -551,7 +719,7 @@ def do_alarm_update(cc, args={}):
 | 
			
		||||
           help=('True if actions should be repeatedly notified '
 | 
			
		||||
                 'while alarm remains in target state.'))
 | 
			
		||||
def do_alarm_threshold_update(cc, args={}):
 | 
			
		||||
    '''Update an existing alarm based on computed statistics.'''
 | 
			
		||||
    """Update an existing alarm based on computed statistics."""
 | 
			
		||||
    fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
 | 
			
		||||
    fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints')
 | 
			
		||||
    fields = utils.key_with_slash_to_nested_dict(fields)
 | 
			
		||||
@@ -567,8 +735,63 @@ def do_alarm_threshold_update(cc, args={}):
 | 
			
		||||
    _display_alarm(alarm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
 | 
			
		||||
           help='ID of the alarm to update.')
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
 | 
			
		||||
           action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
 | 
			
		||||
           dest='alarm_id_deprecated')
 | 
			
		||||
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
 | 
			
		||||
           action=NotEmptyAction, help='ID of the alarm to update.')
 | 
			
		||||
@common_alarm_arguments()
 | 
			
		||||
@common_alarm_gnocchi_arguments('gnocchi_resources_threshold')
 | 
			
		||||
@common_alarm_gnocchi_resources_arguments()
 | 
			
		||||
@utils.arg('--remove-time-constraint', action='append',
 | 
			
		||||
           metavar='<Constraint names>',
 | 
			
		||||
           dest='remove_time_constraints',
 | 
			
		||||
           help='Name or list of names of the time constraints to remove.')
 | 
			
		||||
def do_alarm_gnocchi_resources_threshold_update(cc, args={}):
 | 
			
		||||
    """Update an existing alarm based on computed statistics."""
 | 
			
		||||
    fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
 | 
			
		||||
    fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints')
 | 
			
		||||
    fields = utils.key_with_slash_to_nested_dict(fields)
 | 
			
		||||
    fields.pop('alarm_id')
 | 
			
		||||
    fields['type'] = 'gnocchi_resources_threshold'
 | 
			
		||||
    try:
 | 
			
		||||
        alarm = cc.alarms.update(args.alarm_id, **fields)
 | 
			
		||||
    except exc.HTTPNotFound:
 | 
			
		||||
        raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
 | 
			
		||||
    _display_alarm(alarm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
 | 
			
		||||
           action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
 | 
			
		||||
           dest='alarm_id_deprecated')
 | 
			
		||||
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
 | 
			
		||||
           action=NotEmptyAction, help='ID of the alarm to update.')
 | 
			
		||||
@common_alarm_arguments()
 | 
			
		||||
@common_alarm_gnocchi_arguments('gnocchi_metrics_threshold')
 | 
			
		||||
@common_alarm_gnocchi_metrics_arguments()
 | 
			
		||||
@utils.arg('--remove-time-constraint', action='append',
 | 
			
		||||
           metavar='<Constraint names>',
 | 
			
		||||
           dest='remove_time_constraints',
 | 
			
		||||
           help='Name or list of names of the time constraints to remove.')
 | 
			
		||||
def do_alarm_gnocchi_metrics_threshold_update(cc, args={}):
 | 
			
		||||
    """Update an existing alarm based on computed statistics."""
 | 
			
		||||
    fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
 | 
			
		||||
    fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints')
 | 
			
		||||
    fields = utils.key_with_slash_to_nested_dict(fields)
 | 
			
		||||
    fields.pop('alarm_id')
 | 
			
		||||
    fields['type'] = 'gnocchi_metrics_threshold'
 | 
			
		||||
    try:
 | 
			
		||||
        alarm = cc.alarms.update(args.alarm_id, **fields)
 | 
			
		||||
    except exc.HTTPNotFound:
 | 
			
		||||
        raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
 | 
			
		||||
    _display_alarm(alarm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
 | 
			
		||||
           action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
 | 
			
		||||
           dest='alarm_id_deprecated')
 | 
			
		||||
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
 | 
			
		||||
           action=NotEmptyAction, help='ID of the alarm to update.')
 | 
			
		||||
@common_alarm_arguments()
 | 
			
		||||
@utils.arg('--remove-time-constraint', action='append',
 | 
			
		||||
           metavar='<Constraint names>',
 | 
			
		||||
@@ -576,7 +799,7 @@ def do_alarm_threshold_update(cc, args={}):
 | 
			
		||||
           help='Name or list of names of the time constraints to remove.')
 | 
			
		||||
@utils.arg('--alarm_ids', action='append', metavar='<ALARM IDS>',
 | 
			
		||||
           dest='combination_rule/alarm_ids',
 | 
			
		||||
           help='List of alarm id.')
 | 
			
		||||
           help='List of alarm IDs.')
 | 
			
		||||
@utils.arg('--operator', metavar='<OPERATOR>',
 | 
			
		||||
           dest='combination_rule/operator',
 | 
			
		||||
           help='Operator to compare with, one of: ' + str(
 | 
			
		||||
@@ -586,7 +809,7 @@ def do_alarm_threshold_update(cc, args={}):
 | 
			
		||||
           help=('True if actions should be repeatedly notified '
 | 
			
		||||
                 'while alarm remains in target state.'))
 | 
			
		||||
def do_alarm_combination_update(cc, args={}):
 | 
			
		||||
    '''Update an existing alarm based on state of other alarms.'''
 | 
			
		||||
    """Update an existing alarm based on state of other alarms."""
 | 
			
		||||
    fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
 | 
			
		||||
    fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints')
 | 
			
		||||
    fields = utils.key_with_slash_to_nested_dict(fields)
 | 
			
		||||
@@ -599,23 +822,29 @@ def do_alarm_combination_update(cc, args={}):
 | 
			
		||||
    _display_alarm(alarm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
 | 
			
		||||
           help='ID of the alarm to delete.')
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
 | 
			
		||||
           action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
 | 
			
		||||
           dest='alarm_id_deprecated')
 | 
			
		||||
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
 | 
			
		||||
           action=NotEmptyAction, help='ID of the alarm to delete.')
 | 
			
		||||
def do_alarm_delete(cc, args={}):
 | 
			
		||||
    '''Delete an alarm.'''
 | 
			
		||||
    """Delete an alarm."""
 | 
			
		||||
    try:
 | 
			
		||||
        cc.alarms.delete(args.alarm_id)
 | 
			
		||||
    except exc.HTTPNotFound:
 | 
			
		||||
        raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
 | 
			
		||||
           help='ID of the alarm state to set.')
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
 | 
			
		||||
           action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
 | 
			
		||||
           dest='alarm_id_deprecated')
 | 
			
		||||
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
 | 
			
		||||
           action=NotEmptyAction, help='ID of the alarm state to set.')
 | 
			
		||||
@utils.arg('--state', metavar='<STATE>', required=True,
 | 
			
		||||
           help='State of the alarm, one of: ' + str(ALARM_STATES) +
 | 
			
		||||
           '.')
 | 
			
		||||
def do_alarm_state_set(cc, args={}):
 | 
			
		||||
    '''Set the state of an alarm.'''
 | 
			
		||||
    """Set the state of an alarm."""
 | 
			
		||||
    try:
 | 
			
		||||
        state = cc.alarms.set_state(args.alarm_id, args.state)
 | 
			
		||||
    except exc.HTTPNotFound:
 | 
			
		||||
@@ -623,10 +852,13 @@ def do_alarm_state_set(cc, args={}):
 | 
			
		||||
    utils.print_dict({'state': state}, wrap=72)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
 | 
			
		||||
           help='ID of the alarm state to show.')
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
 | 
			
		||||
           action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
 | 
			
		||||
           dest='alarm_id_deprecated')
 | 
			
		||||
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
 | 
			
		||||
           action=NotEmptyAction, help='ID of the alarm state to show.')
 | 
			
		||||
def do_alarm_state_get(cc, args={}):
 | 
			
		||||
    '''Get the state of an alarm.'''
 | 
			
		||||
    """Get the state of an alarm."""
 | 
			
		||||
    try:
 | 
			
		||||
        state = cc.alarms.get_state(args.alarm_id)
 | 
			
		||||
    except exc.HTTPNotFound:
 | 
			
		||||
@@ -634,13 +866,16 @@ def do_alarm_state_get(cc, args={}):
 | 
			
		||||
    utils.print_dict({'state': state}, wrap=72)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
 | 
			
		||||
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
 | 
			
		||||
           action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
 | 
			
		||||
           dest='alarm_id_deprecated')
 | 
			
		||||
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', action=NotEmptyAction,
 | 
			
		||||
           help='ID of the alarm for which history is shown.')
 | 
			
		||||
@utils.arg('-q', '--query', metavar='<QUERY>',
 | 
			
		||||
           help='key[op]data_type::value; list. data_type is optional, '
 | 
			
		||||
                'but if supplied must be string, integer, float, or boolean.')
 | 
			
		||||
def do_alarm_history(cc, args={}):
 | 
			
		||||
    '''Display the change history of an alarm.'''
 | 
			
		||||
    """Display the change history of an alarm."""
 | 
			
		||||
    kwargs = dict(alarm_id=args.alarm_id,
 | 
			
		||||
                  q=options.cli_to_array(args.query))
 | 
			
		||||
    try:
 | 
			
		||||
@@ -649,16 +884,20 @@ def do_alarm_history(cc, args={}):
 | 
			
		||||
        raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
 | 
			
		||||
    field_labels = ['Type', 'Timestamp', 'Detail']
 | 
			
		||||
    fields = ['type', 'timestamp', 'detail']
 | 
			
		||||
    # We're using sortby=None as the alarm history returned from the Ceilometer
 | 
			
		||||
    # is already sorted in the "the newer state is the earlier one in the
 | 
			
		||||
    # list". If we'll pass any field as a sortby param, it'll be sorted in the
 | 
			
		||||
    # ASC way by the PrettyTable
 | 
			
		||||
    utils.print_list(history, fields, field_labels,
 | 
			
		||||
                     formatters={'detail': alarm_change_detail_formatter},
 | 
			
		||||
                     sortby=1)
 | 
			
		||||
                     sortby=None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-q', '--query', metavar='<QUERY>',
 | 
			
		||||
           help='key[op]data_type::value; list. data_type is optional, '
 | 
			
		||||
                'but if supplied must be string, integer, float, or boolean.')
 | 
			
		||||
def do_resource_list(cc, args={}):
 | 
			
		||||
    '''List the resources.'''
 | 
			
		||||
    """List the resources."""
 | 
			
		||||
    resources = cc.resources.list(q=options.cli_to_array(args.query))
 | 
			
		||||
 | 
			
		||||
    field_labels = ['Resource ID', 'Source', 'User ID', 'Project ID']
 | 
			
		||||
@@ -667,10 +906,10 @@ def do_resource_list(cc, args={}):
 | 
			
		||||
                     sortby=1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-r', '--resource_id', metavar='<RESOURCE_ID>', required=True,
 | 
			
		||||
           help='ID of the resource to show.')
 | 
			
		||||
@utils.arg('resource_id', metavar='<RESOURCE_ID>',
 | 
			
		||||
           action=NotEmptyAction, help='ID of the resource to show.')
 | 
			
		||||
def do_resource_show(cc, args={}):
 | 
			
		||||
    '''Show the resource.'''
 | 
			
		||||
    """Show the resource."""
 | 
			
		||||
    try:
 | 
			
		||||
        resource = cc.resources.get(args.resource_id)
 | 
			
		||||
    except exc.HTTPNotFound:
 | 
			
		||||
@@ -686,24 +925,28 @@ def do_resource_show(cc, args={}):
 | 
			
		||||
           help='key[op]data_type::value; list. data_type is optional, '
 | 
			
		||||
                'but if supplied must be string, integer, float'
 | 
			
		||||
                'or datetime.')
 | 
			
		||||
@utils.arg('--no-traits', dest='no_traits', action='store_true',
 | 
			
		||||
           help='If specified, traits will not be printed.')
 | 
			
		||||
def do_event_list(cc, args={}):
 | 
			
		||||
    '''List events.'''
 | 
			
		||||
    """List events."""
 | 
			
		||||
    events = cc.events.list(q=options.cli_to_array(args.query))
 | 
			
		||||
    field_labels = ['Message ID', 'Event Type', 'Generated', 'Traits']
 | 
			
		||||
    fields = ['message_id', 'event_type', 'generated', 'traits']
 | 
			
		||||
    if args.no_traits:
 | 
			
		||||
        field_labels.pop()
 | 
			
		||||
        fields.pop()
 | 
			
		||||
    utils.print_list(events, fields, field_labels,
 | 
			
		||||
                     formatters={
 | 
			
		||||
                     'traits': utils.nested_list_of_dict_formatter('traits',
 | 
			
		||||
                                                                   ['name',
 | 
			
		||||
                                                                    'type',
 | 
			
		||||
                                                                    'value'])})
 | 
			
		||||
                         'traits': utils.nested_list_of_dict_formatter(
 | 
			
		||||
                             'traits', ['name', 'type', 'value']
 | 
			
		||||
                         )},
 | 
			
		||||
                     sortby=None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-m', '--message_id', metavar='<message_id>',
 | 
			
		||||
           help='The id of the event. Should be a UUID',
 | 
			
		||||
           required=True)
 | 
			
		||||
@utils.arg('message_id', metavar='<message_id>', action=NotEmptyAction,
 | 
			
		||||
           help='The ID of the event. Should be a UUID.')
 | 
			
		||||
def do_event_show(cc, args={}):
 | 
			
		||||
    '''Show a particular event.'''
 | 
			
		||||
    """Show a particular event."""
 | 
			
		||||
    event = cc.events.get(args.message_id)
 | 
			
		||||
    fields = ['event_type', 'generated', 'traits']
 | 
			
		||||
    data = dict([(f, getattr(event, f, '')) for f in fields])
 | 
			
		||||
@@ -711,16 +954,16 @@ def do_event_show(cc, args={}):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def do_event_type_list(cc, args={}):
 | 
			
		||||
    '''List event types.'''
 | 
			
		||||
    """List event types."""
 | 
			
		||||
    event_types = cc.event_types.list()
 | 
			
		||||
    utils.print_list(event_types, ['event_type'], ['Event Type'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-e', '--event_type', metavar='<EVENT_TYPE>',
 | 
			
		||||
           help='Type of the event for which traits will be shown.',
 | 
			
		||||
           required=True)
 | 
			
		||||
           required=True, action=NotEmptyAction)
 | 
			
		||||
def do_trait_description_list(cc, args={}):
 | 
			
		||||
    '''List trait info for an event type.'''
 | 
			
		||||
    """List trait info for an event type."""
 | 
			
		||||
    trait_descriptions = cc.trait_descriptions.list(args.event_type)
 | 
			
		||||
    field_labels = ['Trait Name', 'Data Type']
 | 
			
		||||
    fields = ['name', 'type']
 | 
			
		||||
@@ -729,14 +972,12 @@ def do_trait_description_list(cc, args={}):
 | 
			
		||||
 | 
			
		||||
@utils.arg('-e', '--event_type', metavar='<EVENT_TYPE>',
 | 
			
		||||
           help='Type of the event for which traits will listed.',
 | 
			
		||||
           required=True)
 | 
			
		||||
           required=True, action=NotEmptyAction)
 | 
			
		||||
@utils.arg('-t', '--trait_name', metavar='<TRAIT_NAME>',
 | 
			
		||||
           help='The name of the trait to list.',
 | 
			
		||||
           required=True)
 | 
			
		||||
           required=True, action=NotEmptyAction)
 | 
			
		||||
def do_trait_list(cc, args={}):
 | 
			
		||||
    '''List trait all traits with name <trait_name> for Event Type
 | 
			
		||||
    <event_type>.
 | 
			
		||||
    '''
 | 
			
		||||
    """List all traits with name <trait_name> for Event Type <event_type>."""
 | 
			
		||||
    traits = cc.traits.list(args.event_type, args.trait_name)
 | 
			
		||||
    field_labels = ['Trait Name', 'Value', 'Data Type']
 | 
			
		||||
    fields = ['name', 'value', 'type']
 | 
			
		||||
@@ -753,7 +994,7 @@ def do_trait_list(cc, args={}):
 | 
			
		||||
@utils.arg('-l', '--limit', metavar='<LIMIT>',
 | 
			
		||||
           help='Maximum number of samples to return.')
 | 
			
		||||
def do_query_samples(cc, args):
 | 
			
		||||
    '''Query samples.'''
 | 
			
		||||
    """Query samples."""
 | 
			
		||||
    fields = {'filter': args.filter,
 | 
			
		||||
              'orderby': args.orderby,
 | 
			
		||||
              'limit': args.limit}
 | 
			
		||||
@@ -780,7 +1021,7 @@ def do_query_samples(cc, args):
 | 
			
		||||
@utils.arg('-l', '--limit', metavar='<LIMIT>',
 | 
			
		||||
           help='Maximum number of alarms to return.')
 | 
			
		||||
def do_query_alarms(cc, args):
 | 
			
		||||
    '''Query Alarms.'''
 | 
			
		||||
    """Query Alarms."""
 | 
			
		||||
    fields = {'filter': args.filter,
 | 
			
		||||
              'orderby': args.orderby,
 | 
			
		||||
              'limit': args.limit}
 | 
			
		||||
@@ -789,13 +1030,7 @@ def do_query_alarms(cc, args):
 | 
			
		||||
    except exc.HTTPNotFound:
 | 
			
		||||
        raise exc.CommandError('Alarms not found')
 | 
			
		||||
    else:
 | 
			
		||||
        field_labels = ['Alarm ID', 'Name', 'State', 'Enabled', 'Continuous',
 | 
			
		||||
                        'Alarm condition']
 | 
			
		||||
        fields = ['alarm_id', 'name', 'state', 'enabled', 'repeat_actions',
 | 
			
		||||
                  'rule']
 | 
			
		||||
        utils.print_list(alarms, fields, field_labels,
 | 
			
		||||
                         formatters={'rule': alarm_rule_formatter},
 | 
			
		||||
                         sortby=None)
 | 
			
		||||
        _display_alarm_list(alarms, sortby=None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.arg('-f', '--filter', metavar='<FILTER>',
 | 
			
		||||
@@ -808,7 +1043,7 @@ def do_query_alarms(cc, args):
 | 
			
		||||
@utils.arg('-l', '--limit', metavar='<LIMIT>',
 | 
			
		||||
           help='Maximum number of alarm history items to return.')
 | 
			
		||||
def do_query_alarm_history(cc, args):
 | 
			
		||||
    '''Query Alarm History.'''
 | 
			
		||||
    """Query Alarm History."""
 | 
			
		||||
    fields = {'filter': args.filter,
 | 
			
		||||
              'orderby': args.orderby,
 | 
			
		||||
              'limit': args.limit}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,8 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from ceilometerclient.common import base
 | 
			
		||||
from ceilometerclient.v2 import options
 | 
			
		||||
 | 
			
		||||
@@ -23,7 +25,8 @@ class Statistics(base.Resource):
 | 
			
		||||
class StatisticsManager(base.Manager):
 | 
			
		||||
    resource_class = Statistics
 | 
			
		||||
 | 
			
		||||
    def _build_aggregates(self, aggregates):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _build_aggregates(aggregates):
 | 
			
		||||
        url_aggregates = []
 | 
			
		||||
        for aggregate in aggregates:
 | 
			
		||||
            if 'param' in aggregate:
 | 
			
		||||
@@ -43,6 +46,8 @@ class StatisticsManager(base.Manager):
 | 
			
		||||
 | 
			
		||||
    def list(self, meter_name, q=None, period=None, groupby=[], aggregates=[]):
 | 
			
		||||
        p = ['period=%s' % period] if period else []
 | 
			
		||||
        if isinstance(groupby, six.string_types):
 | 
			
		||||
            groupby = [groupby]
 | 
			
		||||
        p.extend(['groupby=%s' % g for g in groupby] if groupby else [])
 | 
			
		||||
        p.extend(self._build_aggregates(aggregates))
 | 
			
		||||
        return self._list(options.build_url(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
# -*- encoding: utf-8 -*-
 | 
			
		||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								doc/ext/gen_ref.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								doc/ext/gen_ref.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
# not use this file except in compliance with the License. You may obtain
 | 
			
		||||
# a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
# License for the specific language governing permissions and limitations
 | 
			
		||||
# under the License.
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 | 
			
		||||
ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..", ".."))
 | 
			
		||||
 | 
			
		||||
sys.path.insert(0, ROOT)
 | 
			
		||||
sys.path.insert(0, BASE_DIR)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gen_ref(ver, title, names):
 | 
			
		||||
    refdir = os.path.join(BASE_DIR, "ref")
 | 
			
		||||
    pkg = "ceilometerclient"
 | 
			
		||||
    if ver:
 | 
			
		||||
        pkg = "%s.%s" % (pkg, ver)
 | 
			
		||||
        refdir = os.path.join(refdir, ver)
 | 
			
		||||
    if not os.path.exists(refdir):
 | 
			
		||||
        os.makedirs(refdir)
 | 
			
		||||
    idxpath = os.path.join(refdir, "index.rst")
 | 
			
		||||
    with open(idxpath, "w") as idx:
 | 
			
		||||
        idx.write(("%(title)s\n"
 | 
			
		||||
                   "%(signs)s\n"
 | 
			
		||||
                   "\n"
 | 
			
		||||
                   ".. toctree::\n"
 | 
			
		||||
                   "   :maxdepth: 1\n"
 | 
			
		||||
                   "\n") % {"title": title, "signs": "=" * len(title)})
 | 
			
		||||
        for name in names:
 | 
			
		||||
            idx.write("   %s\n" % name)
 | 
			
		||||
            rstpath = os.path.join(refdir, "%s.rst" % name)
 | 
			
		||||
            with open(rstpath, "w") as rst:
 | 
			
		||||
                rst.write(("%(title)s\n"
 | 
			
		||||
                           "%(signs)s\n"
 | 
			
		||||
                           "\n"
 | 
			
		||||
                           ".. automodule:: %(pkg)s.%(name)s\n"
 | 
			
		||||
                           "   :members:\n"
 | 
			
		||||
                           "   :undoc-members:\n"
 | 
			
		||||
                           "   :show-inheritance:\n"
 | 
			
		||||
                           "   :noindex:\n")
 | 
			
		||||
                          % {"title": name.capitalize(),
 | 
			
		||||
                             "signs": "=" * len(name),
 | 
			
		||||
                             "pkg": pkg, "name": name})
 | 
			
		||||
 | 
			
		||||
gen_ref("", "Client Reference", ["client", "exc"])
 | 
			
		||||
gen_ref("v1", "Version 1 API Reference",
 | 
			
		||||
        ["stacks", "resources", "events", "actions",
 | 
			
		||||
         "software_configs", "software_deployments"])
 | 
			
		||||
							
								
								
									
										48
									
								
								doc/source/api.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								doc/source/api.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
The :mod:`ceilometerclient` Python API
 | 
			
		||||
======================================
 | 
			
		||||
 | 
			
		||||
.. module:: ceilometerclient
 | 
			
		||||
   :synopsis: A client for the OpenStack Ceilometer API.
 | 
			
		||||
 | 
			
		||||
.. currentmodule:: ceilometerclient
 | 
			
		||||
 | 
			
		||||
Usage
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
First create a client instance with your credentials::
 | 
			
		||||
 | 
			
		||||
    >>> import ceilometerclient.client
 | 
			
		||||
    >>> cclient = ceilometerclient.client.get_client(VERSION, os_username=USERNAME, os_password=PASSWORD, os_tenant_name=PROJECT_NAME, os_auth_url=AUTH_URL)
 | 
			
		||||
 | 
			
		||||
Here ``VERSION`` can be: ``1`` and ``2``.
 | 
			
		||||
 | 
			
		||||
Then call methods on its managers::
 | 
			
		||||
 | 
			
		||||
    >>> cclient.meters.list()
 | 
			
		||||
    [<Meter ...>, ...]
 | 
			
		||||
 | 
			
		||||
    >>> cclient.samples.list()
 | 
			
		||||
    [<Sample ...>, ...]
 | 
			
		||||
 | 
			
		||||
V2 client tips
 | 
			
		||||
++++++++++++++
 | 
			
		||||
 | 
			
		||||
Use queries to narrow your search (more info at `Ceilometer V2 API reference`__)::
 | 
			
		||||
 | 
			
		||||
    >>> query = [dict(field='resource_id', op='eq', value='5a301761-f78b-46e2-8900-8b4f6fe6675a')]
 | 
			
		||||
    >>> ceilometer.samples.list(meter_name='cpu_util', limit=10, q=query)
 | 
			
		||||
    [<Sample ...>, ...]
 | 
			
		||||
 | 
			
		||||
__  http://docs.openstack.org/developer/ceilometer/webapi/v2.html#Query
 | 
			
		||||
 | 
			
		||||
Reference
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
For more information, see the reference:
 | 
			
		||||
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :maxdepth: 2
 | 
			
		||||
 | 
			
		||||
   ref/index
 | 
			
		||||
   ref/v1/index
 | 
			
		||||
   ref/v2/index
 | 
			
		||||
@@ -1,13 +1,32 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
# not use this file except in compliance with the License. You may obtain
 | 
			
		||||
# a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
# License for the specific language governing permissions and limitations
 | 
			
		||||
# under the License.
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
execfile(os.path.join("..", "ext", "gen_ref.py"))
 | 
			
		||||
 | 
			
		||||
project = 'python-ceilometerclient'
 | 
			
		||||
 | 
			
		||||
gen_ref("", "Client Reference", ["client", "exc"])
 | 
			
		||||
gen_ref("v1", "Version 1 API Reference",
 | 
			
		||||
        ["meters"])
 | 
			
		||||
gen_ref("v2", "Version 2 API Reference",
 | 
			
		||||
        ["meters", "samples", "statistics", "resources", "query", "alarms",
 | 
			
		||||
         "events", "event_types", "traits", "trait_descriptions"])
 | 
			
		||||
 | 
			
		||||
# -- General configuration ----------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Add any Sphinx extension module names here, as strings. They can be
 | 
			
		||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 | 
			
		||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
 | 
			
		||||
extensions = ['sphinx.ext.autodoc', 'oslosphinx' ]
 | 
			
		||||
 | 
			
		||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
 | 
			
		||||
# text edit cycles.
 | 
			
		||||
@@ -39,7 +58,7 @@ pygments_style = 'sphinx'
 | 
			
		||||
 | 
			
		||||
# The theme to use for HTML and HTML Help pages.  Major themes that come with
 | 
			
		||||
# Sphinx are currently 'default' and 'sphinxdoc'.
 | 
			
		||||
html_theme = 'nature'
 | 
			
		||||
#html_theme = 'nature'
 | 
			
		||||
 | 
			
		||||
# Output file base name for HTML help builder.
 | 
			
		||||
htmlhelp_basename = '%sdoc' % project
 | 
			
		||||
@@ -57,6 +76,3 @@ latex_documents = [
 | 
			
		||||
        'manual'
 | 
			
		||||
    ),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# Example configuration for intersphinx: refer to the Python standard library.
 | 
			
		||||
intersphinx_mapping = {'http://docs.python.org/': None}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,32 +1,47 @@
 | 
			
		||||
Python API
 | 
			
		||||
==========
 | 
			
		||||
In order to use the python api directly, you must first obtain an auth token and identify which endpoint you wish to speak to. Once you have done so, you can use the API like so::
 | 
			
		||||
Python bindings to the OpenStack Ceilometer API
 | 
			
		||||
==================================================
 | 
			
		||||
 | 
			
		||||
    >>> from ceilometerclient import Client
 | 
			
		||||
    >>> ceilometer = Client('1', endpoint=OS_IMAGE_ENDPOINT, token=OS_AUTH_TOKEN)
 | 
			
		||||
...
 | 
			
		||||
This is a client for OpenStack Ceilometer API. There's :doc:`a Python API
 | 
			
		||||
<api>` (the :mod:`ceilometerclient` module), and a :doc:`command-line script
 | 
			
		||||
<shell>` (installed as :program:`ceilometer`). Each implements the entire
 | 
			
		||||
OpenStack Ceilometer API.
 | 
			
		||||
 | 
			
		||||
.. seealso::
 | 
			
		||||
 | 
			
		||||
Command-line Tool
 | 
			
		||||
=================
 | 
			
		||||
In order to use the CLI, you must provide your OpenStack username, password, tenant, and auth endpoint. Use the corresponding configuration options (``--os-username``, ``--os-password``, ``--os-tenant-id``, and ``--os-auth-url``) or set them in environment variables::
 | 
			
		||||
    You may want to read the `OpenStack Ceilometer Developer Guide`__  -- the overview, at
 | 
			
		||||
    least -- to get an idea of the concepts. By understanding the concepts
 | 
			
		||||
    this library should make more sense.
 | 
			
		||||
 | 
			
		||||
    export OS_USERNAME=user
 | 
			
		||||
    export OS_PASSWORD=pass
 | 
			
		||||
    export OS_TENANT_ID=b363706f891f48019483f8bd6503c54b
 | 
			
		||||
    export OS_AUTH_URL=http://auth.example.com:5000/v2.0
 | 
			
		||||
    __ http://docs.openstack.org/developer/ceilometer/
 | 
			
		||||
 | 
			
		||||
The command line tool will attempt to reauthenticate using your provided credentials for every request. You can override this behavior by manually supplying an auth token using ``--os-image-url`` and ``--os-auth-token``. You can alternatively set these environment variables::
 | 
			
		||||
Contents:
 | 
			
		||||
 | 
			
		||||
    export OS_IMAGE_URL=http://ceilometer.example.org:8004/
 | 
			
		||||
    export OS_AUTH_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :maxdepth: 2
 | 
			
		||||
 | 
			
		||||
Once you've configured your authentication parameters, you can run ``ceilometer help`` to see a complete listing of available commands.
 | 
			
		||||
   shell
 | 
			
		||||
   api
 | 
			
		||||
   ref/index
 | 
			
		||||
   ref/v1/index
 | 
			
		||||
   ref/v2/index
 | 
			
		||||
   releases
 | 
			
		||||
 | 
			
		||||
Contributing
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
Release Notes
 | 
			
		||||
=============
 | 
			
		||||
Code is hosted at `git.openstack.org`_. Submit bugs to the Ceilometer project on
 | 
			
		||||
`Launchpad`_. Submit code to the openstack/python-ceilometerclient project using
 | 
			
		||||
`Gerrit`_.
 | 
			
		||||
 | 
			
		||||
0.1.0
 | 
			
		||||
-----
 | 
			
		||||
* Initial release
 | 
			
		||||
.. _git.openstack.org: https://git.openstack.org/cgit/openstack/python-ceilometerclient
 | 
			
		||||
.. _Launchpad: https://launchpad.net/ceilometer
 | 
			
		||||
.. _Gerrit: http://docs.openstack.org/infra/manual/developers.html#development-workflow
 | 
			
		||||
 | 
			
		||||
Run tests with ``python setup.py test``.
 | 
			
		||||
 | 
			
		||||
Indices and tables
 | 
			
		||||
==================
 | 
			
		||||
 | 
			
		||||
* :ref:`genindex`
 | 
			
		||||
* :ref:`modindex`
 | 
			
		||||
* :ref:`search`
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										61
									
								
								doc/source/shell.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								doc/source/shell.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
The :program:`ceilometer` shell utility
 | 
			
		||||
=========================================
 | 
			
		||||
 | 
			
		||||
.. program:: ceilometer
 | 
			
		||||
.. highlight:: bash
 | 
			
		||||
 | 
			
		||||
The :program:`ceilometer` shell utility interacts with OpenStack Ceilometer API
 | 
			
		||||
from the command line. It supports the entirety of the OpenStack Ceilometer API.
 | 
			
		||||
 | 
			
		||||
You'll need to provide :program:`ceilometer` with your OpenStack credentials.
 | 
			
		||||
You can do this with the :option:`--os-username`, :option:`--os-password`,
 | 
			
		||||
:option:`--os-tenant-id` and :option:`--os-auth-url` options, but it's easier to
 | 
			
		||||
just set them as environment variables:
 | 
			
		||||
 | 
			
		||||
.. envvar:: OS_USERNAME
 | 
			
		||||
 | 
			
		||||
    Your OpenStack username.
 | 
			
		||||
 | 
			
		||||
.. envvar:: OS_PASSWORD
 | 
			
		||||
 | 
			
		||||
    Your password.
 | 
			
		||||
 | 
			
		||||
.. envvar:: OS_TENANT_NAME
 | 
			
		||||
 | 
			
		||||
    Project to work on.
 | 
			
		||||
 | 
			
		||||
.. envvar:: OS_AUTH_URL
 | 
			
		||||
 | 
			
		||||
    The OpenStack auth server URL (keystone).
 | 
			
		||||
 | 
			
		||||
For example, in Bash you would use::
 | 
			
		||||
 | 
			
		||||
    export OS_USERNAME=user
 | 
			
		||||
    export OS_PASSWORD=pass
 | 
			
		||||
    export OS_TENANT_NAME=myproject
 | 
			
		||||
    export OS_AUTH_URL=http://auth.example.com:5000/v2.0
 | 
			
		||||
 | 
			
		||||
The command line tool will attempt to reauthenticate using your provided credentials
 | 
			
		||||
for every request. You can override this behavior by manually supplying an auth
 | 
			
		||||
token using :option:`--os-ceilometer-url` and :option:`--os-auth-token`. You can alternatively
 | 
			
		||||
set these environment variables::
 | 
			
		||||
 | 
			
		||||
    export OS_CEILOMETER_URL=http://ceilometer.example.org:8777
 | 
			
		||||
    export OS_AUTH_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155
 | 
			
		||||
 | 
			
		||||
From there, all shell commands take the form::
 | 
			
		||||
 | 
			
		||||
    ceilometer <command> [arguments...]
 | 
			
		||||
 | 
			
		||||
Run :program:`ceilometer help` to get a full list of all possible commands,
 | 
			
		||||
and run :program:`ceilometer help <command>` to get detailed help for that
 | 
			
		||||
command.
 | 
			
		||||
 | 
			
		||||
V2 client tips
 | 
			
		||||
++++++++++++++
 | 
			
		||||
 | 
			
		||||
Use queries to narrow your search (more info at `Ceilometer V2 API reference`__)::
 | 
			
		||||
 | 
			
		||||
    ceilometer sample-list --meter cpu_util --query 'resource_id=5a301761-f78b-46e2-8900-8b4f6fe6675a' --limit 10
 | 
			
		||||
 | 
			
		||||
__  http://docs.openstack.org/developer/ceilometer/webapi/v2.html#Query
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
[DEFAULT]
 | 
			
		||||
 | 
			
		||||
# The list of modules to copy from openstack-common
 | 
			
		||||
module=apiclient
 | 
			
		||||
module=cliutils
 | 
			
		||||
module=importutils
 | 
			
		||||
module=install_venv_common
 | 
			
		||||
 | 
			
		||||
# The base module to hold the copy of openstack.common
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,14 @@
 | 
			
		||||
pbr>=0.6,<1.0
 | 
			
		||||
# The order of packages is significant, because pip processes them in the order
 | 
			
		||||
# of appearance. Changing the order has an impact on the overall integration
 | 
			
		||||
# process, which may cause wedges in the gate later.
 | 
			
		||||
pbr>=0.6,!=0.7,<1.0
 | 
			
		||||
argparse
 | 
			
		||||
iso8601>=0.1.9
 | 
			
		||||
oslo.i18n>=1.3.0  # Apache-2.0
 | 
			
		||||
oslo.serialization>=1.2.0               # Apache-2.0
 | 
			
		||||
oslo.utils>=1.2.0                       # Apache-2.0
 | 
			
		||||
PrettyTable>=0.7,<0.8
 | 
			
		||||
python-keystoneclient>=0.6.0
 | 
			
		||||
six>=1.5.2
 | 
			
		||||
python-keystoneclient>=1.1.0
 | 
			
		||||
requests>=2.2.0,!=2.4.0
 | 
			
		||||
six>=1.7.0
 | 
			
		||||
stevedore>=1.1.0  # Apache-2.0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								setup.py
									
									
									
									
									
								
							@@ -17,6 +17,14 @@
 | 
			
		||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
 | 
			
		||||
import setuptools
 | 
			
		||||
 | 
			
		||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
 | 
			
		||||
# setuptools if some other modules registered functions in `atexit`.
 | 
			
		||||
# solution from: http://bugs.python.org/issue15881#msg170215
 | 
			
		||||
try:
 | 
			
		||||
    import multiprocessing  # noqa
 | 
			
		||||
except ImportError:
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
setuptools.setup(
 | 
			
		||||
    setup_requires=['pbr'],
 | 
			
		||||
    pbr=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,14 @@
 | 
			
		||||
# The order of packages is significant, because pip processes them in the order
 | 
			
		||||
# of appearance. Changing the order has an impact on the overall integration
 | 
			
		||||
# process, which may cause wedges in the gate later.
 | 
			
		||||
# Hacking already pins down pep8, pyflakes and flake8
 | 
			
		||||
hacking>=0.8.0,<0.9
 | 
			
		||||
hacking>=0.10.0,<0.11
 | 
			
		||||
coverage>=3.6
 | 
			
		||||
discover
 | 
			
		||||
fixtures>=0.3.14
 | 
			
		||||
mock>=1.0
 | 
			
		||||
oslosphinx>=2.2.0  # Apache-2.0
 | 
			
		||||
python-subunit>=0.0.18
 | 
			
		||||
sphinx>=1.1.2,<1.2
 | 
			
		||||
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
 | 
			
		||||
testrepository>=0.0.18
 | 
			
		||||
testtools>=0.9.34
 | 
			
		||||
testtools>=0.9.36,!=1.2.0
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
			
		||||
 | 
			
		||||
# Copyright 2010 United States Government as represented by the
 | 
			
		||||
# Administrator of the National Aeronautics and Space Administration.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
@@ -67,7 +65,6 @@ def main(argv):
 | 
			
		||||
    install.check_dependencies()
 | 
			
		||||
    install.create_virtualenv(no_site_packages=options.no_site_packages)
 | 
			
		||||
    install.install_dependencies()
 | 
			
		||||
    install.post_process()
 | 
			
		||||
    print_help(venv, root)
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
 
 | 
			
		||||
@@ -125,7 +125,7 @@ class InstallVenv(object):
 | 
			
		||||
        parser.add_option('-n', '--no-site-packages',
 | 
			
		||||
                          action='store_true',
 | 
			
		||||
                          help="Do not inherit packages from global Python "
 | 
			
		||||
                               "install")
 | 
			
		||||
                               "install.")
 | 
			
		||||
        return parser.parse_args(argv[1:])[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								tox.ini
									
									
									
									
									
								
							@@ -10,16 +10,11 @@ setenv = VIRTUAL_ENV={envdir}
 | 
			
		||||
deps = -r{toxinidir}/requirements.txt
 | 
			
		||||
       -r{toxinidir}/test-requirements.txt
 | 
			
		||||
commands =
 | 
			
		||||
  python setup.py testr --testr-args='{posargs}'
 | 
			
		||||
  python setup.py testr --slowest --testr-args='{posargs}'
 | 
			
		||||
 | 
			
		||||
[tox:jenkins]
 | 
			
		||||
downloadcache = ~/cache/pip
 | 
			
		||||
 | 
			
		||||
[testenv:pypy]
 | 
			
		||||
deps = setuptools<3.2
 | 
			
		||||
       -r{toxinidir}/requirements.txt
 | 
			
		||||
       -r{toxinidir}/test-requirements.txt
 | 
			
		||||
 | 
			
		||||
[testenv:pep8]
 | 
			
		||||
commands = flake8
 | 
			
		||||
 | 
			
		||||
@@ -29,7 +24,10 @@ commands = python setup.py testr --coverage --testr-args='{posargs}'
 | 
			
		||||
[testenv:venv]
 | 
			
		||||
commands = {posargs}
 | 
			
		||||
 | 
			
		||||
[testenv:docs]
 | 
			
		||||
commands=
 | 
			
		||||
    python setup.py build_sphinx
 | 
			
		||||
 | 
			
		||||
[flake8]
 | 
			
		||||
ignore = None
 | 
			
		||||
show-source = True
 | 
			
		||||
exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,tools
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user