| #!/usr/bin/env python | 
 |  | 
 | # Contributors Listed Below - COPYRIGHT 2015 | 
 | # [+] 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.service | 
 | import dbus.mainloop.glib | 
 | import gobject | 
 | from OpenBMCMapper import Path, IntrospectionParser, PathTree | 
 | import OpenBMCMapper | 
 |  | 
 | class ObjectMapper(dbus.service.Object): | 
 | 	def __init__(self, bus, path, | 
 | 			name_match = OpenBMCMapper.org_dot_openbmc_match, | 
 | 			intf_match = OpenBMCMapper.org_dot_openbmc_match): | 
 | 		super(ObjectMapper, self).__init__(bus.dbus, path) | 
 | 		self.cache = PathTree() | 
 | 		self.bus = bus | 
 | 		self.name_match = name_match | 
 | 		self.intf_match = intf_match | 
 | 		self.tag_match = OpenBMCMapper.ListMatch(['children', 'interface']) | 
 | 		self.service = None | 
 |  | 
 | 		gobject.idle_add(self.discover) | 
 | 		self.bus.dbus.add_signal_receiver(self.bus_handler, | 
 | 			dbus_interface = dbus.BUS_DAEMON_IFACE, | 
 | 			signal_name = 'NameOwnerChanged') | 
 | 		self.bus.dbus.add_signal_receiver(self.interfaces_added_handler, | 
 | 			dbus_interface = dbus.BUS_DAEMON_IFACE + '.ObjectManager', | 
 | 			signal_name = 'InterfaceesAdded', | 
 | 			sender_keyword = 'sender') | 
 | 		self.bus.dbus.add_signal_receiver(self.interfaces_removed_handler, | 
 | 			dbus_interface = dbus.BUS_DAEMON_IFACE + '.ObjectManager', | 
 | 			signal_name = 'InterfacesRemoved', | 
 | 			sender_keyword = 'sender') | 
 |  | 
 | 	def bus_match(self, name): | 
 | 		if name == OpenBMCMapper.MAPPER_NAME: | 
 | 			return False | 
 | 		return self.name_match(name) | 
 |  | 
 | 	def discovery_pending(self): | 
 | 		return not bool(self.service) | 
 |  | 
 | 	def interfaces_added_handler(self, path, iprops, **kw): | 
 | 		if self.discovery_pending() or \ | 
 | 				not self.bus_match(kw['sender']): | 
 | 			return | 
 | 		matches = [ x for x in iprops.iterkeys() if self.intf_match(x) ] | 
 | 		d = self.cache.setdefault(path, {}) | 
 | 		d[path].setdefault(kw['sender'], []).extend(matches) | 
 | 		self.cache[path] = d | 
 |  | 
 | 	def interfaces_removed_handler(self, path, interfaces, **kw): | 
 | 		if self.discovery_pending() or \ | 
 | 				not self.bus_match(kw['sender']): | 
 | 			return | 
 | 		item = self.cache[path] | 
 | 		name = kw['sender'] | 
 | 		for x in interfaces: | 
 | 			item[name].remove(x) | 
 |  | 
 | 		# remove the owner if there aren't any interfaces left | 
 | 		if not item[name]: | 
 | 			del item[name] | 
 |  | 
 | 		# update if interfaces remain | 
 | 		if item: | 
 | 			self.cache[path] = item | 
 | 		# mark for removal if no interfaces remain | 
 | 		elif self.cache.get_children(item): | 
 | 			self.cache.demote(item) | 
 | 		# delete the entire path if everything is gone | 
 | 		else: | 
 | 			del self.cache[path] | 
 |  | 
 | 	def process_new_owner(self, name): | 
 | 		# unique name | 
 | 		return self.discover([ IntrospectionParser(name, | 
 | 			self.bus.dbus, self.tag_match, self.intf_match) ]) | 
 |  | 
 | 	def process_old_owner(self, name): | 
 | 		for x,y in self.cache.dataitems(): | 
 | 			if name not in y: | 
 | 				continue | 
 | 			del y[name] | 
 | 			if y: | 
 | 				self.cache[x] = y | 
 | 			elif self.cache.get_children(x): | 
 | 				self.cache.demote(x) | 
 | 			else: | 
 | 				del self.cache[x] | 
 |  | 
 | 	def bus_handler(self, service, old, new): | 
 | 		if self.discovery_pending() or \ | 
 | 				not self.bus_match(service): | 
 | 			return | 
 |  | 
 | 		if new: | 
 | 			self.process_new_owner(new) | 
 | 		if old: | 
 | 			self.process_old_owner(old) | 
 |  | 
 | 	def add_interfaces(self, path, owner, interfaces): | 
 | 		d = self.cache.setdefault(path, { }) | 
 | 		d.setdefault(owner, []).extend(interfaces) | 
 | 		self.cache[path] = d | 
 |  | 
 | 	def add_items(self, owner, bus_items): | 
 | 		for x,y in bus_items.iteritems(): | 
 | 			self.add_interfaces(x, owner, y['interfaces']) | 
 |  | 
 | 	def discover(self, owners = None): | 
 | 		if not owners: | 
 | 			owners = [ IntrospectionParser(x, self.bus.dbus, | 
 | 				self.tag_match, self.intf_match) \ | 
 | 						for x in self.bus.get_owner_names(self.bus_match) ] | 
 | 		for o in owners: | 
 | 			self.add_items(o.name, o.introspect()) | 
 |  | 
 | 		if self.discovery_pending(): | 
 | 			print "ObjectMapper discovery complete..." | 
 | 			self.service = dbus.service.BusName( | 
 | 					OpenBMCMapper.MAPPER_NAME, self.bus.dbus) | 
 |  | 
 | 	@dbus.service.method(OpenBMCMapper.MAPPER_IFACE, 's', 'a{sas}') | 
 | 	def GetObject(self, path): | 
 | 		o = self.cache.get(path) | 
 | 		if not o: | 
 | 			raise MapperNotFoundException(path) | 
 | 		return o | 
 |  | 
 | 	@dbus.service.method(OpenBMCMapper.MAPPER_IFACE, 'si', 'as') | 
 | 	def GetSubTreePaths(self, path, depth): | 
 | 		try: | 
 | 			return self.cache.iterkeys(path, depth) | 
 | 		except KeyError: | 
 | 			raise MapperNotFoundException(path) | 
 |  | 
 | 	@dbus.service.method(OpenBMCMapper.MAPPER_IFACE, 'si', 'a{sa{sas}}') | 
 | 	def GetSubTree(self, path, depth): | 
 | 		try: | 
 | 			return { x:y for x, y in self.cache.dataitems(path, depth) } | 
 | 		except KeyError: | 
 | 			raise MapperNotFoundException(path) | 
 |  | 
 | class BusWrapper: | 
 | 	def __init__(self, bus): | 
 | 		self.dbus = bus | 
 |  | 
 | 	def get_service_names(self, match): | 
 | 		# these are well known names | 
 | 		return [ x for x in self.dbus.list_names() \ | 
 | 				if match(x) ] | 
 |  | 
 | 	def get_owner_names(self, match): | 
 | 		# these are unique connection names | 
 | 		return list( set( [ self.dbus.get_name_owner(x) \ | 
 | 				for x in self.get_service_names(match) ] ) ) | 
 |  | 
 | if __name__ == '__main__': | 
 | 	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) | 
 | 	bus = dbus.SystemBus() | 
 | 	o = ObjectMapper(BusWrapper(bus), OpenBMCMapper.MAPPER_PATH) | 
 | 	loop = gobject.MainLoop() | 
 |  | 
 | 	loop.run() |