def serve(self): # Add event processor amqp = PluginRegistry.getInstance('AMQPHandler') EventConsumer(self.env, amqp.getConnection(), xquery=""" declare namespace f='http://www.gonicus.de/Events'; let $e := ./f:Event return $e/f:ClientAnnounce or $e/f:ClientSignature or $e/f:ClientPing or $e/f:ClientLeave or $e/f:UserSession """, callback=self.__eventProcessor) # Get registry - we need it later on self.__cr = PluginRegistry.getInstance("CommandRegistry") # Start maintainence with a delay of 5 seconds timer = Timer(5.0, self.__refresh) timer.start() self.env.threads.append(timer) # Register scheduler task to remove outdated clients sched = PluginRegistry.getInstance('SchedulerService').getScheduler() sched.add_interval_job(self.__gc, minutes=1, tag='_internal', jobstore="ram")
def serve(self): # Establish the database connection self.db = self.env.get_mongo_db("clacks").inventory # Eventually add index if not 'HardwareUUID' in self.db.index_information().values(): self.db.ensure_index('HardwareUUID') self.db.ensure_index('Checksum') # Create event consumer amqp = PluginRegistry.getInstance('AMQPHandler') EventConsumer(self.env, amqp.getConnection(), xquery=""" declare namespace f='http://www.gonicus.de/Events'; let $e := ./f:Event return $e/f:Inventory """, callback=self.process) self.log.info("listening for incoming inventory notifications") # Register a listener for potential client registries cs = PluginRegistry.getInstance("ClientService") cs.register_listener('request_inventory', self.client_listener)
def addClient(self, device_uuid): cfg_file = os.path.join(self.__repo_path, "config") config = self._git_get_config(cfg_file) section = 'remote "%s"' % device_uuid if not config.has_section(section): cs = PluginRegistry.getInstance("ClientService") key = self._get_public_key() if not key[1] in [p["data"] for p in cs.clientDispatch(device_uuid, "puppetListKeys").values()]: cs.clientDispatch(device_uuid, "puppetAddKey", [key]) # Update git if needed release = self.__get_client_release(device_uuid) self.gitPush(self.getBaseDir(release)) # Update nodes.pp self.__update_node(device_uuid) # Add git configuration config.add_section(section) # Build push URL for client, it needs to be online try: url = cs.clientDispatch(device_uuid, "puppetGetPushPath") config.set(section, "url", url) with open(cfg_file, "w") as f: config.write(f) except: return False # Reset "P" flag for client cs = PluginRegistry.getInstance("ClientService") cs.systemSetStatus(device_uuid, "-P") return True
def serve(self): self.log.info("listening for asterisk events...") amqp = PluginRegistry.getInstance('AMQPHandler') EventConsumer(self.env, amqp.getConnection(), xquery=""" declare namespace f='http://www.gonicus.de/Events'; let $e := ./f:Event/f:AMINotification return $e/f:Call """, callback=self.process) self.__cr = PluginRegistry.getInstance("CommandRegistry")
def shutdown(a=None, b=None): """ Function to shut down the client. """ global loop env = Environment.getInstance() env.log.info("Clacks DBUS is shutting down") # Shutdown plugins PluginRegistry.shutdown() if loop: loop.quit() logging.shutdown() exit(0)
def notifyUser(self, users, title, message, timeout=10, level='normal', icon="dialog-information"): """ Send a notification request to the user client. """ if icon is None: icon = "_no_icon_" if users: # Notify a single / group of users if type(users) != types.ListType: users = [users] for user in users: clients = self.getUserClients(user) if clients: for client in clients: try: self.clientDispatch(client, "notify", user, title, message, timeout, icon) #pylint: disable=W0141 except Exception as e: import traceback traceback.print_exc() self.log.error("sending message failed: %s" % str(e)) else: self.log.error("sending message failed: no client found for user '%s'" % user) # Notify websession user if available jsrpc = PluginRegistry.getInstance("JSONRPCService") if jsrpc.user_sessions_available(user): amqp = PluginRegistry.getInstance("AMQPHandler") amqp.sendEvent(self.notification2event(user, title, message, timeout, icon)) else: # Notify all users for client in self.__client.keys(): try: self.clientDispatch(client, "notify_all", title, message, timeout, icon) #pylint: disable=W0141 except Exception: pass # Notify all websession users if any jsrpc = PluginRegistry.getInstance("JSONRPCService") if jsrpc.user_sessions_available(): amqp = PluginRegistry.getInstance("AMQPHandler") amqp.sendEvent(self.notification2event("*", title, message, timeout, icon))
def _handleClientAnnounce(self, data): data = data.ClientAnnounce client = data.Id.text self.log.info("client '%s' is joining us" % client) self.systemSetStatus(client, "+O") # Assemble network information network = {} for interface in data.NetworkInformation.NetworkDevice: network[interface.Name.text] = { 'IPAddress': interface.IPAddress.text, 'IPv6Address': interface.IPv6Address.text, 'MAC': interface.MAC.text, 'Netmask': interface.Netmask.text, 'Broadcast': interface.Broadcast.text} # Add recieve time to be able to sort out dead nodes t = datetime.datetime.utcnow() info = { 'name': data.Name.text, 'last-seen': t, 'online': True, 'caps': {}, 'network': network } self.__client[data.Id.text] = info # Handle pending "P"repare actions for that client if "P" in self.systemGetStatus(client): try: rm = PluginRegistry.getInstance("RepositoryManager") rm.prepareClient(client) except ValueError: pass
def process_IN_CREATE(self, event): self.log.debug("logwatch detected change for '%s'" % event.pathname) if event.pathname.endswith(".yaml"): sleep(1) amqp = PluginRegistry.getInstance("AMQPClientHandler") self.log.debug("puppet logwatch detected change for '%s', producing event" % event.pathname) amqp.sendEvent(self.report_to_event(event.pathname))
def ping(): while self.env.active: e = EventMaker() amqp = PluginRegistry.getInstance("AMQPClientHandler") info = e.Event(e.ClientPing(e.Id(uuid))) amqp.sendEvent(info) time.sleep(timeout)
def report_to_event(self, file_name): e = EventMaker() PluginRegistry.getInstance("AMQPClientService") with open(file_name, "r") as f: yml = yaml.load(f) logs = [e.Id(self.env.id)] for log in yml.logs: data = [ e.Timestamp(str(mktime(log.time.timetuple()))), e.Level(log.level), e.Message(log.message), ] # Append <Tag> tag try: tags = e.Tag() for tag in log.tags: tags.append(e.value(tag)) data.append(tags) except: pass # Add optional tags try: data.append[e.Source(log.source)] except: pass try: data.append[e.Line(log.line)] except: pass try: data.append[e.File(log.file)] except: pass try: data.append[e.Version(log.version)] except: pass logs.append(e.PuppetLog(*data)) return e.Event(e.PuppetReport(*logs))
def shutdown(a=None, b=None): global dr env = Environment.getInstance() log = logging.getLogger(__name__) # Function to shut down the client. Do some clean up and close sockets. amqp = PluginRegistry.getInstance("AMQPClientHandler") # Tell others that we're away now e = EventMaker() goodbye = e.Event(e.ClientLeave(e.Id(env.uuid))) if amqp: amqp.sendEvent(goodbye) amqp.close() # Shutdown plugins PluginRegistry.shutdown() #TODO: remove this hack wait = 1 for t in env.threads: if t.isAlive(): log.warning("thread %s still alive" % t.getName()) if hasattr(t, 'stop'): log.warning("calling 'stop' for thread %s" % t.getName()) t.stop() if hasattr(t, 'cancel'): log.warning("calling 'cancel' for thread %s" % t.getName()) t.cancel() t.join(wait) if t.isAlive(): try: log.warning("calling built in 'stop' for thread %s" % t.getName()) t._Thread__stop() except: log.error("could not stop thread %s" % t.getName()) dr.stop() log.info("shut down") logging.shutdown()
def client_listener(self, client, method, mode): if mode: # Send an inventory information with the current checksum self.log.info("requesting inventory from client %s" % client) cs = PluginRegistry.getInstance("ClientService") entry = self.db.find_one({'ClientUUID': client}, {'Checksum': 1}) if entry: cs.clientDispatch(client, "request_inventory", entry['Checksum']) else: cs.clientDispatch(client, "request_inventory")
def sendSessionNotification(self): # Build event amqp = PluginRegistry.getInstance("AMQPClientHandler") e = EventMaker() more = set([x['uid'] for x in self.__sessions.values()]) more = map(e.Name, more) info = e.Event( e.UserSession( e.Id(self.env.uuid), e.User(*more))) amqp.sendEvent(info)
def __init__(self): super(DebianPreseed, self).__init__() self.env = Environment.getInstance() self.path = self.env.config.get('libinst.path', default="/preseed") try: # Get http service instance self.__http = PluginRegistry.getInstance('HTTPService') # Register ourselves self.__http.register(self.path, self) except: pass
def sendEvent(self, data, user=None): """ Send and validate an event thru AMQP service. =============== ============ Parameter Description =============== ============ data XML string or etree object representing the event. =============== ============ ``Return:`` Bool, success or failure """ try: event = u"<?xml version='1.0'?>\n" if isinstance(data, basestring): event += data else: event += etree.tostring(data, pretty_print=True) # Validate event if hasattr(self, '_parser'): xml = objectify.fromstring(event, self._parser) else: #TODO: retrieve schema from server xml = objectify.fromstring(event) # If a user was supplied, check if she's authorized... if user: acl = PluginRegistry.getInstance("ACLResolver") topic = ".".join([self.env.domain, 'event', xml.__dict__.keys()[0]]) if not acl.check(user, topic, "x"): raise EventNotAuthorized("sending the event '%s' is not permitted" % topic) return self._eventProvider.send(event) except etree.XMLSyntaxError as e: if not isinstance(data, basestring): data = data.content if self.env: self.log.error("event rejected (%s): %s" % (str(e), data)) raise
def netactivity(online): global netstate if online: netstate = True else: env = Environment.getInstance() netstate = False # Function to shut down the client. Do some clean up and close sockets. amqp = PluginRegistry.getInstance("AMQPClientHandler") # Tell others that we're away now e = EventMaker() goodbye = e.Event(e.ClientLeave(e.Id(env.uuid))) if amqp: amqp.sendEvent(goodbye) amqp.close() env.reset_requested = True env.active = False
def __refresh(self): # Initially check if we need to ask for client caps or if there's someone # who knows... if not self.__client: nodes = self.__cr.getNodes() if not nodes: return # If we're alone, please clients to announce themselves if len(nodes) == 1 and self.env.id in nodes: amqp = PluginRegistry.getInstance('AMQPHandler') e = EventMaker() amqp.sendEvent(e.Event(e.ClientPoll())) # Elseways, ask other servers for more client info else: #TODO: get from host #take a random node and: # ... for all clients # ... load client capabilities and store them localy raise NotImplementedError("getting client information from other nodes is not implemented!")
def clientDispatch(self, client, method, *arg, **larg): """ Dispatch a method on the client. ========= ================================ Parameter Description ========= ================================ client Device UUID of the client method Method name to call * Method arguments ========= ================================ ``Return:`` varies """ # Bail out if the client is not available if not client in self.__client: raise JSONRPCException("client '%s' not available" % client) if not self.__client[client]['online']: raise JSONRPCException("client '%s' is offline" % client) if not method in self.__client[client]['caps']: raise JSONRPCException("client '%s' has no method '%s' exported" % (client, method)) # Generate tage queue name queue = '%s.client.%s' % (self.env.domain, client) self.log.debug("got client dispatch: '%s(%s)', sending to %s" % (method, arg, queue)) # client queue -> amqp rpc proxy if not client in self.__proxy: amqp = PluginRegistry.getInstance("AMQPHandler") self.__proxy[client] = AMQPServiceProxy(amqp.url['source'], queue) # Call her to the moon... methodCall = getattr(self.__proxy[client], method) # Do the call res = methodCall(*arg, **larg) return res
def __init__(self): env = Environment.getInstance() self.log = logging.getLogger(__name__) self.log.debug("initializing AMQP handler") self.env = env self.config = env.config # Initialize parser schema_root = etree.XML(PluginRegistry.getEventSchema()) schema = etree.XMLSchema(schema_root) self._parser = objectify.makeparser(schema=schema) # Evaluate username user = self.env.uuid password = self.config.get("amqp.key") # Load configuration self.url = parseURL(makeAuthURL(self.config.get('amqp.url'), user, password)) self.reconnect = self.config.get('amqp.reconnect', True) self.reconnect_interval = self.config.get('amqp.reconnect_interval', 3) self.reconnect_limit = self.config.get('amqp.reconnect_limit', 0) # Go for it self.start()
def setConfigParameters(self, device_uuid, data, current_data=None): super(PuppetInstallMethod, self).setConfigParameters(device_uuid, data, current_data) if not self.addClient(device_uuid): cs = PluginRegistry.getInstance("ClientService") cs.systemSetStatus(device_uuid, "+P")
def SvcDoRun(self): pythoncom.CoInitialize() servicemanager.LogMsg( servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '')) # Start main loop thread here Environment.config = "C:/gosa-client.conf" Environment.noargs = True self.env = Environment.getInstance() self.env.log.info("Clacks client is starting up") env = self.env try: # Load plugins PluginRegistry(component='client.module') amqp = PluginRegistry.getInstance("AMQPClientHandler") #@UnusedVariable #TODO: # Check if we're a client # -> no: shutdown, client should be joined by administrator before # calling the client # Sleep and slice wait = 2 while True: # Threading doesn't seem to work well with python... for p in env.threads: # Bail out if we're active in the meanwhile if not env.active: break p.join(wait) # No break, go to main loop else: continue # Break, leave main loop break except Exception as detail: env.log.critical("unexpected error in mainLoop") env.log.exception(detail) env.log.debug(traceback.format_exc()) finally: pythoncom.CoUninitialize() # Signalize main thread to shut down win32api.Sleep(500) # Pull down system amqp = PluginRegistry.getInstance("AMQPClientHandler") amqp_service = PluginRegistry.getInstance("AMQPClientService") # Tell others that we're away now e = EventMaker() goodbye = e.Event(e.ClientLeave(e.Id(amqp_service.id))) amqp.sendEvent(goodbye) # Shutdown plugins PluginRegistry.shutdown() # Write another event log record. servicemanager.LogMsg( servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, ''))
def __announce(self, initial=False): amqp = PluginRegistry.getInstance("AMQPClientHandler") e = EventMaker() # Assemble network information more = [] netinfo = [] for interface in netifaces.interfaces(): i_info = netifaces.ifaddresses(interface) # Skip lo interfaces if not netifaces.AF_INET in i_info: continue # Skip lo interfaces if not netifaces.AF_LINK in i_info: continue if i_info[netifaces.AF_LINK][0]["addr"] == "00:00:00:00:00:00": continue # Assemble ipv6 information ip6 = "" if netifaces.AF_INET6 in i_info: ip = IPNetwork( "%s/%s" % (i_info[netifaces.AF_INET6][0]["addr"].split("%", 1)[0], i_info[netifaces.AF_INET6][0]["netmask"]) ) ip6 = str(ip) netinfo.append( e.NetworkDevice( e.Name(interface), e.IPAddress(i_info[netifaces.AF_INET][0]["addr"]), e.IPv6Address(ip6), e.MAC(i_info[netifaces.AF_LINK][0]["addr"]), e.Netmask(i_info[netifaces.AF_INET][0]["netmask"]), e.Broadcast(i_info[netifaces.AF_INET][0]["broadcast"]), ) ) more.append(e.NetworkInformation(*netinfo)) # Build event if initial: info = e.Event(e.ClientAnnounce(e.Id(self.env.uuid), e.Name(self.env.id), *more)) amqp.sendEvent(info) # Assemble capabilities more = [] caps = [] for command, dsc in self.__cr.commands.iteritems(): caps.append( e.ClientMethod( e.Name(command), e.Path(dsc["path"]), e.Signature(",".join(dsc["sig"])), e.Documentation(dsc["doc"]) ) ) more.append(e.ClientCapabilities(*caps)) info = e.Event(e.ClientSignature(e.Id(self.env.uuid), e.Name(self.env.id), *more)) amqp.sendEvent(info) if not initial: try: sk = PluginRegistry.getInstance("SessionKeeper") sk.sendSessionNotification() except: pass
def serve(self): """ Start AMQP service for this clacks service provider. """ # Load AMQP and Command registry instances amqp = PluginRegistry.getInstance("AMQPClientHandler") self.__cr = PluginRegistry.getInstance("ClientCommandRegistry") # Add private processor for client queue # pylint: disable=E1101 self.__cmdWorker = AMQPWorker( self.env, connection=amqp.getConnection(), r_address="""%s.client.%s; { create:always, node:{ type:queue, durable: False, x-declare: { exclusive: True, auto-delete: True }, x-bindings:[ { exchange:"amq.direct", queue:"%s.client.%s" } ] } }""" % (self.env.domain, self.env.uuid, self.env.domain, self.env.uuid), workers=self.env.config.get("amqp.worker", default=1), callback=self.commandReceived, ) # Add event processor try: EventConsumer( self.env, amqp.getConnection(), xquery=""" declare namespace f='http://www.gonicus.de/Events'; let $e := ./f:Event return $e/f:ClientPoll """, callback=self.__handleClientPoll, ) # Gather interface information self.__announce(True) except NotFound as e: self.env.log.critical("queue gone: %s" % str(e)) # Send a ping on a regular base uuid = self.env.uuid timeout = float(self.env.config.get("client.ping-interval", default=600)) def ping(): while self.env.active: e = EventMaker() amqp = PluginRegistry.getInstance("AMQPClientHandler") info = e.Event(e.ClientPing(e.Id(uuid))) amqp.sendEvent(info) time.sleep(timeout) pinger = Timer(10.0, ping) pinger.start() self.env.threads.append(pinger)
def process(self, data): dat = data.AMINotification['Call'] # Load information into dict event = {} for t in dat.iterchildren(): tag = re.sub(r"^\{.*\}(.*)$", r"\1", t.tag) if t.tag == 'From': event[tag] = t.text.split(" ")[0] else: event[tag] = str(t.text) # Simple debouncing if self.last_event and self.last_event['From'] == event['From'] and self.last_event['To'] == event['To'] and self.last_event['Type'] == event['Type'] and (float(event['Timestamp']) - float(self.last_event['Timestamp'])) < 1: return self.last_event = event #TODO: wrong place here - workaround if event['From'] == "+49": event['From'] = "Unbekannter Teilnehmer" if event['To'] == "+49": event['To'] = "Unbekannter Teilnehmer" # Resolve numbers with all resolvers, sorted by priority i_from = None i_to = None for mod, info in sorted(self.resolver.iteritems(), key=lambda k: k[1]['priority']): if not i_from: i_from = info['object'].resolve(event['From']) if not i_to: i_to = info['object'].resolve(event['To']) if i_from and i_to: break # Fallback to original number if nothing has been found if not i_from: i_from = {'contact_phone': event['From'], 'contact_name': event['From'], 'company_name': None, 'resource': 'none'} if not i_to: i_to = {'contact_phone': event['To'], 'contact_name': event['To'], 'company_name': None, 'resource': 'none'} # Render messages to_msg = from_msg = "" for mod, info in sorted(self.renderer.iteritems(), key=lambda k: k[1]['priority']): if 'ldap_uid' in i_to and i_to['ldap_uid']: to_msg += info['object'].getHTML(i_from, i_to, event) to_msg += "\n\n" if 'ldap_uid' in i_from and i_from['ldap_uid'] and event['Type'] == 'CallEnded': from_msg += info['object'].getHTML(i_to, i_from, event) from_msg += "\n\n" # encode as ASCII with hexadecimal HTML entities for non-latin1 chars to_msg = to_msg.encode('ascii', 'xmlcharrefreplace') from_msg = from_msg.encode('ascii', 'xmlcharrefreplace') # Define avatar view if 'avatar' in i_from and i_from['avatar']: f_img = Image.open(StringIO(i_from['avatar'])) else: f_img = self.__default_image if 'avatar' in i_to and i_to['avatar']: t_img = Image.open(StringIO(i_to['avatar'])) else: t_img = self.__default_image # Scale image to a reasonable size and convert it to base64 mw = int(self.env.config.get("amires.avatar-size", default="96")) sx, sy = f_img.size if sx >= sy: f_img.thumbnail((mw, int(sy * mw / sx))) else: f_img.thumbnail((int(sx * mw / sy), mw)) sx, sy = t_img.size if sx >= sy: t_img.thumbnail((mw, int(sy * mw / sx))) else: t_img.thumbnail((int(sx * mw / sy), mw)) out = StringIO() f_img.save(out, format="PNG") out.seek(0) f_image_data = "base64:" + base64.b64encode(out.read()) out = StringIO() t_img.save(out, format="PNG") out.seek(0) t_image_data = "base64:" + base64.b64encode(out.read()) # Send from/to messages as needed amqp = PluginRegistry.getInstance('AMQPHandler') if from_msg: self.__cr.call("notifyUser", i_from['ldap_uid'], self.TYPE_MAP[event['Type']], from_msg.strip(), timeout=10, level='normal', icon=t_image_data) if to_msg: self.__cr.call("notifyUser", i_to['ldap_uid'], self.TYPE_MAP[event['Type']], to_msg.strip(), timeout=10, level='normal', icon=f_image_data)
def mainLoop(env): global netstate, dr # Enable DBus runner dr = DBusRunner() dr.start() # Do network monitoring nm = Monitor(netactivity) netstate = nm.is_online() """ Main event loop which will process all registerd threads in a loop. It will run as long env.active is set to True.""" try: log = logging.getLogger(__name__) while True: # Check netstate and wait until we're back online if not netstate: log.info("waiting for network connectivity") while not netstate: time.sleep(1) # Load plugins PluginRegistry(component='client.module') # Sleep and slice wait = 2 while True: # Threading doesn't seem to work well with python... for p in env.threads: # Bail out if we're active in the meanwhile if not env.active: break p.join(wait) # No break, go to main loop else: continue # Break, leave main loop break # Break, leave main loop if not env.reset_requested: break # Wait for threads to shut down for t in env.threads: if hasattr(t, 'stop'): t.stop() if hasattr(t, 'cancel'): t.cancel() t.join(wait) #TODO: remove me if t.isAlive(): try: t._Thread__stop() except: print(str(t.getName()) + ' could not be terminated') # Lets do an environment reset now PluginRegistry.shutdown() # Make us active and loop from the beginning env.reset_requested = False env.active = True if not netstate: log.info("waiting for network connectivity") while not netstate: time.sleep(1) sleep = randint(30, 60) env.log.info("waiting %s seconds to try an AMQP connection recovery" % sleep) time.sleep(sleep) except Exception as detail: log.critical("unexpected error in mainLoop") log.exception(detail) log.debug(traceback.format_exc()) except KeyboardInterrupt: log.info("console requested shutdown") finally: shutdown()