| # Contributors Listed Below - COPYRIGHT 2016 | 
 | # [+] International Business Machines Corp. | 
 | # | 
 | # | 
 | # 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 sys | 
 | import dbus | 
 | import dbus.mainloop.glib | 
 | # TODO: openbmc/openbmc#2994 remove python 2 support | 
 | try:  # python 2 | 
 |     import gobject | 
 | except ImportError:  # python 3 | 
 |     from gi.repository import GObject as gobject | 
 | import obmc.mapper | 
 |  | 
 |  | 
 | class Wait(object): | 
 |     def __init__(self, bus, waitlist, *a, **kw): | 
 |         self.bus = bus | 
 |         self.waitlist = dict(list(zip(waitlist, [None] * len(waitlist)))) | 
 |         mapper = bus.get_object( | 
 |             obmc.mapper.MAPPER_NAME, | 
 |             obmc.mapper.MAPPER_PATH, | 
 |             introspect=False) | 
 |         self.iface = dbus.Interface( | 
 |             mapper, dbus_interface=obmc.mapper.MAPPER_IFACE) | 
 |         self.done = False | 
 |         self.callback = kw.pop('callback', None) | 
 |         self.error_callback = kw.pop('error_callback', self.default_error) | 
 |         self.busy_retries = kw.pop('busy_retries', 20) | 
 |         self.busy_retry_delay_milliseconds = kw.pop( | 
 |             'busy_retry_delay_milliseconds', 500) | 
 |         self.waitlist_keyword = kw.pop('waitlist_keyword', None) | 
 |  | 
 |         self.bus.add_signal_receiver( | 
 |             self.introspection_handler, | 
 |             dbus_interface=obmc.mapper.MAPPER_IFACE + '.Private', | 
 |             signal_name='IntrospectionComplete') | 
 |         self.bus.add_signal_receiver( | 
 |             self.introspection_handler, | 
 |             dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager') | 
 |  | 
 |         self.introspection_handler() | 
 |  | 
 |     @staticmethod | 
 |     def default_error(e): | 
 |         raise e | 
 |  | 
 |     def force_done(self): | 
 |         if self.done: | 
 |             return | 
 |  | 
 |         self.done = True | 
 |         self.bus.remove_signal_receiver( | 
 |             self.introspection_handler, | 
 |             dbus_interface=obmc.mapper.MAPPER_IFACE + '.Private', | 
 |             signal_name='IntrospectionComplete') | 
 |         self.bus.remove_signal_receiver( | 
 |             self.introspection_handler, | 
 |             dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager', | 
 |             signal_name='InterfacesAdded') | 
 |  | 
 |     def check_done(self): | 
 |         if not all(self.waitlist.values()) or self.done: | 
 |             return | 
 |  | 
 |         self.force_done() | 
 |  | 
 |         if self.callback: | 
 |             kwargs = {} | 
 |             if self.waitlist_keyword: | 
 |                 kwargs[waitlist_keyword] = self.waitlist | 
 |             self.callback(**kwargs) | 
 |  | 
 |     def get_object_async(self, path, retry): | 
 |         method = getattr(self.iface, 'GetObject') | 
 |         method.call_async( | 
 |             path, | 
 |             [], | 
 |             signature='sas', | 
 |             reply_handler=lambda x: self.get_object_callback( | 
 |                 path, x), | 
 |             error_handler=lambda x: self.get_object_error( | 
 |                 path, retry, x)) | 
 |         return False | 
 |  | 
 |     def get_object_error(self, path, retry, e): | 
 |         if self.done: | 
 |             return | 
 |  | 
 |         if e.get_dbus_name() == 'org.freedesktop.DBus.Error.FileNotFound': | 
 |             pass | 
 |         elif e.get_dbus_name() in \ | 
 |             ['org.freedesktop.DBus.Error.ObjectPathInUse', | 
 |              'org.freedesktop.DBus.Error.LimitsExceeded']: | 
 |             if retry > self.busy_retries: | 
 |                 self.force_done() | 
 |                 self.error_callback(e) | 
 |             else: | 
 |                 gobject.timeout_add( | 
 |                     self.busy_retry_delay_milliseconds, | 
 |                     self.get_object_async, | 
 |                     path, | 
 |                     retry + 1) | 
 |         else: | 
 |             self.force_done() | 
 |             self.error_callback(e) | 
 |  | 
 |     def get_object_callback(self, path, info): | 
 |         self.waitlist[path] = list(info)[0] | 
 |         self.check_done() | 
 |  | 
 |     def introspection_handler(self, *a, **kw): | 
 |         if self.done: | 
 |             return | 
 |  | 
 |         for path in [ | 
 |                 x for x in list(self.waitlist.keys()) if not self.waitlist[x]]: | 
 |             self.get_object_async(path, 0) |