cinder.services.list() fails with KeyError: service

Bug #1314018 reported by John Stanford on 2014-04-29
12
This bug affects 2 people
Affects Status Importance Assigned to Milestone
python-cinderclient
Undecided
Unassigned

Bug Description

I have python-cinderclient 1.0.9 installed, and cinder 2014.1 on CentOS 6.5. Performing cinder service list from the CLI succeeds, but using the python API fails with a KeyError: service.

Here is the output of CLI:
(goldstone)[~/devel/solinea/goldstone]$ cinder service-list
+------------------+-------------------------------+------+---------+-------+----------------------------+-----------------+
| Binary | Host | Zone | Status | State | Updated_at | Disabled Reason |
+------------------+-------------------------------+------+---------+-------+----------------------------+-----------------+
| cinder-scheduler | controller-01.lab.solinea.com | nova | enabled | up | 2014-04-29T04:59:54.000000 | None |
| cinder-volume | controller-01.lab.solinea.com | nova | enabled | up | 2014-04-29T04:59:55.000000 | None |
| cinder-volume | volume-01.lab.solinea.com | nova | enabled | up | 2014-04-29T04:59:55.000000 | None |
+------------------+-------------------------------+------+---------+-------+----------------------------+-----------------+

Here is the
In [1]: from cinderclient.v1 import client

In [2]: x = client.Client('admin', 'xyz', 'admin', 'http://redacted:35357/v2.0', service_type='volume')
In [3]: x.services.list()
Out[3]: ---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-242ae4da0abf> in <module>()
----> 1 x.services.list()

/Users/stanford/.virtualenvs/goldstone/lib/python2.7/site-packages/IPython/core/displayhook.pyc in __call__(self, result)
    245 self.start_displayhook()
    246 self.write_output_prompt()
--> 247 format_dict, md_dict = self.compute_format_data(result)
    248 self.write_format_data(format_dict, md_dict)
    249 self.update_user_ns(result)

/Users/stanford/.virtualenvs/goldstone/lib/python2.7/site-packages/IPython/core/displayhook.pyc in compute_format_data(self, result)
    155
    156 """
--> 157 return self.shell.display_formatter.format(result)
    158
    159 def write_format_data(self, format_dict, md_dict=None):

/Users/stanford/.virtualenvs/goldstone/lib/python2.7/site-packages/IPython/core/formatters.pyc in format(self, obj, include, exclude)
    150 md = None
    151 try:
--> 152 data = formatter(obj)
    153 except:
    154 # FIXME: log the exception

/Users/stanford/.virtualenvs/goldstone/lib/python2.7/site-packages/IPython/core/formatters.pyc in __call__(self, obj)
    479 type_pprinters=self.type_printers,
    480 deferred_pprinters=self.deferred_printers)
--> 481 printer.pretty(obj)
    482 printer.flush()
    483 return stream.getvalue()

/Users/stanford/.virtualenvs/goldstone/lib/python2.7/site-packages/IPython/lib/pretty.pyc in pretty(self, obj)
    345 if cls in self.type_pprinters:
    346 # printer registered in self.type_pprinters
--> 347 return self.type_pprinters[cls](obj, self, cycle)
    348 else:
    349 # deferred printer

/Users/stanford/.virtualenvs/goldstone/lib/python2.7/site-packages/IPython/lib/pretty.pyc in inner(obj, p, cycle)
    529 p.text(',')
    530 p.breakable()
--> 531 p.pretty(x)
    532 if len(obj) == 1 and type(obj) is tuple:
    533 # Special case for 1-item tuples.

/Users/stanford/.virtualenvs/goldstone/lib/python2.7/site-packages/IPython/lib/pretty.pyc in pretty(self, obj)
    360 if callable(meth):
    361 return meth(obj, self, cycle)
--> 362 return _default_pprint(obj, self, cycle)
    363 finally:
    364 self.end_group()

/Users/stanford/.virtualenvs/goldstone/lib/python2.7/site-packages/IPython/lib/pretty.pyc in _default_pprint(obj, p, cycle)
    480 if getattr(klass, '__repr__', None) not in _baseclass_reprs:
    481 # A user-provided repr.
--> 482 p.text(repr(obj))
    483 return
    484 p.begin_group(1, '<')

/Users/stanford/.virtualenvs/goldstone/lib/python2.7/site-packages/cinderclient/v1/services.pyc in __repr__(self)
     23
     24 def __repr__(self):
---> 25 return "<Service: %s>" % self.service
     26
     27

/Users/stanford/.virtualenvs/goldstone/lib/python2.7/site-packages/cinderclient/base.pyc in __getattr__(self, k)
    269 return self.__getattr__(k)
    270
--> 271 raise AttributeError(k)
    272 else:
    273 return self.__dict__[k]

AttributeError: service

Juan Manuel Ollé (juan-m-olle) wrote :

Makes the service list call cinder return the list of services but the client tries to shows an attribute that is not contained in __repr__

IMO, a concatenation of binary@host could be used to give a name to the service

Example of list call

{u'services': [{u'binary': u'cinder-scheduler',
                u'disabled_reason': None,
                u'host': u'jmolle-Controller',
                u'state': u'up',
                u'status': u'enabled',
                u'updated_at': u'2014-05-06T13:06:54.000000',
                u'zone': u'nova'},
               {u'binary': u'cinder-backup',
                u'disabled_reason': None,
                u'host': u'jmolle-Controller',
                u'state': u'up',
                u'status': u'enabled',
                u'updated_at': u'2014-05-06T13:06:48.000000',
                u'zone': u'nova'},
               {u'binary': u'cinder-volume',
                u'disabled_reason': None,
                u'host': u'jmolle-Controller',
                u'state': u'up',
                u'status': u'enabled',
                u'updated_at': u'2014-05-06T13:06:49.000000',
                u'zone': u'nova'}]}

Any thought?

John Stanford (jxstanford) wrote :

My tendency would be to keep the __repr__ simple, and just use the name. This aligns with the pattern used by nova:

In [64]: c.services.list()
Out[64]:
[<Service: nova-consoleauth>,
 <Service: nova-conductor>,
 <Service: nova-scheduler>,
 <Service: nova-compute>,
 <Service: nova-cert>,
 <Service: nova-console>,
 <Service: nova-compute>]

Keystone has gone exactly the opposite way and uses what looks like the entire dict. I think that's way overkill. If you want the detail, process the list:

In [69]: c.services.list()
Out[69]:
[<Service {u'type': u'image', u'description': u'Openstack Image Service', u'enabled': True, u'id': u'0b0fe44b60564da492070941db2f160f', u'name': u'glance'}>,
 <Service {u'type': u'metering', u'description': u'Openstack Metering Service', u'enabled': True, u'id': u'3417cbf8ca7c40c69a191f9610d0ef30', u'name': u'ceilometer'}>,
 <Service {u'type': u'volume', u'description': u'Cinder Service', u'enabled': True, u'id': u'3833e9efab5d413e9588f1a33f61a11d', u'name': u'cinder'}>,
 <Service {u'type': u'ec2', u'description': u'EC2 Service', u'enabled': True, u'id': u'39130798096b4214980bf8aa8bc66e5a', u'name': u'nova_ec2'}>,
 <Service {u'type': u'object-store', u'description': u'Openstack Object-Store Service', u'enabled': True, u'id': u'6fc024ea4317407f8e32b80847e3bbff', u'name': u'swift'}>,
 <Service {u'type': u'network', u'description': u'Neutron Networking Service', u'enabled': True, u'id': u'7b8eb446fff84f11b6bb648738eee5c1', u'name': u'neutron'}>,
 <Service {u'type': u's3', u'description': u'Openstack S3 Service', u'enabled': True, u'id': u'99e6ab7404a34974af0b61cb1bed9e57', u'name': u'swift_s3'}>,
 <Service {u'type': u'orchestration', u'description': u'Openstack Orchestration Service', u'enabled': True, u'id': u'c8d574752dfa4c9e979e73aa99c9fdf6', u'name': u'heat'}>,
 <Service {u'type': u'identity', u'description': u'OpenStack Identity Service', u'enabled': True, u'id': u'd931233edecd460fb7e988803062271e', u'name': u'keystone'}>,
 <Service {u'type': u'compute', u'description': u'Openstack Compute Service', u'enabled': True, u'id': u'efb8e2617e8c4ac1bed2aa1072aa6e42', u'name': u'nova'}>]

I guess the question I would ask is are you going to use the repr for decision making via the API, and the answer I lean toward is no. In that case, does the addition of a hostname in __repr__ provide any value over something like this in your code which doesn't rely on a magic method directly, rather uses the API as intended:

[str(s['name'] + "@" + s['host'] for s in c.services.list()]

I have a patch submitted that just uses the binary field on https://bugs.launchpad.net/python-cinderclient/+bug/1315606. I'm curious why this got a new bug number. I'm relatively new to the process, so if anyone could help me understand, I would appreciate. Should one of them be marked as a duplicate?

To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers