def proxy_device(self):
        '''
        Return this component's corresponding proxy device, creating
        it in the proxy_deviceclass if it does not exist.

        Default assumes that the names must match.
        '''

        device = GUIDManager(self.dmd).getObject(getattr(self, 'openstackProxyDeviceUUID', None))
        if device:
            guid = IGlobalIdentifier(self).getGUID()

            # this shouldn't happen, but if we've somehow become half-connected
            # (we know about the device, it doesn't know about us), reconnect.
            if getattr(device, 'openstackProxyComponentUUID', None) != guid:
                LOG.info("%s component '%s' linkage to device '%s' is broken.  Re-claiming it." % (self.meta_type, self.name(), device.name()))
                self.claim_proxy_device(device)

            return device

        # Does a device with a matching name exist?  Claim that one.
        device = self.dmd.Devices.findDevice(self.name())
        if device:
            self_pdc_path = self.proxy_deviceclass().getPrimaryPath()
            device_path = device.getPrimaryPath()[:len(self_pdc_path)]
            if device_path == self_pdc_path and device.id != self.device().id:
                self.claim_proxy_device(device)
                return device
            else:
                return self.create_proxy_device()
        else:
            return self.create_proxy_device()
    def apply(self, eventProxy, dmd):
        device = dmd.Devices.findDeviceByIdExact(eventProxy.device)

        if device and hasattr(device, 'openstackProxyComponentUUID'):
            LOG.debug("tagging event on %s with openstack proxy component component uuid %s",
                      eventProxy.device, device.openstackProxyComponentUUID)

            tags = [device.openstackProxyComponentUUID]

            # Also tag it with the openstack endpoint that the component is part of,
            # if possible.
            try:
                component = GUIDManager(dmd).getObject(device.openstackProxyComponentUUID)
                if component:
                    endpoint = component.device()
                    tags.append(IGlobalIdentifier(endpoint).getGUID())
            except Exception:
                LOG.debug("Unable to determine endpoint for proxy component uuid %s",
                          device.openstackProxyComponentUUID)

            # Get OSProcess component, if the event has one
            for brain in ICatalogTool(dmd).search('Products.ZenModel.OSProcess.OSProcess', query=Eq('id', eventProxy.component)):
                osprocess = brain.getObject()

                # Figure out if we have a corresponding software component:
                try:
                    for software in component.hostedSoftware():
                        if software.binary == osprocess.osProcessClass().id:
                            # Matches!
                            tags.append(IGlobalIdentifier(software).getGUID())
                except Exception:
                    LOG.debug("Unable to append event for OSProcess %s",
                              osprocess.osProcessClass().id)

            eventProxy.tags.addAll('ZenPacks.zenoss.OpenStackInfrastructure.DeviceProxyComponent', tags)
    def release_proxy_device(self):
        device = GUIDManager(self.dmd).getObject(getattr(self, 'openstackProxyDeviceUUID', None))
        if device:
            LOG.debug("device %s is now detached from %s component '%s'" % (device.name(), self.meta_type, self.name()))
            device.openstackProxyComponentUUID = None

        self.openstackProxyDeviceUUID = None
        LOG.debug("%s component '%s' is now detached from any devices" % (self.meta_type, self.name()))
示例#4
0
    def release_proxy_device(self):
        device = GUIDManager(self.dmd).getObject(
            getattr(self, 'openstackProxyDeviceUUID', None))
        if device:
            LOG.debug("device %s is now detached from %s component '%s'" %
                      (device.name(), self.meta_type, self.name()))
            device.openstackProxyComponentUUID = None

        self.openstackProxyDeviceUUID = None
        LOG.debug("%s component '%s' is now detached from any devices" %
                  (self.meta_type, self.name()))
def onDeviceDeleted(object, event):
    '''
    Clean up the dangling reference to a device if that device has been removed.
    (Note: we may re-create the device automatically next time someone calls
    self.ensure_proxy_device, though)
    '''
    if not IObjectWillBeAddedEvent.providedBy(event) and not IObjectWillBeMovedEvent.providedBy(event):
        if hasattr(object, 'openstackProxyComponentUUID'):
            component = GUIDManager(object.dmd).getObject(getattr(object, 'openstackProxyComponentUUID', None))
            if component:
                component.release_proxy_device()
            object.openstackProxyComponentUUID = None
 def setupAction(self, dmd):
     self.guidManager = GUIDManager(dmd)
     self.dmd = dmd
     self.zep = getFacade("zep", self.dmd)
     self.statuses = {
         "0": "New",
         "1": "Acknowledged",
         "2": "Suppressed",
         "3": "Closed",
         "4": "Cleared",
         "5": "Dropped",
         "6": "Aged",
     }
    def apply(self, eventProxy, dmd):

        # See ZPS-1677 for explanation.  This workaround will hopefully be
        # removed in the future (ZPS-1685)
        if eventProxy.eventClass == '/Status/Ping':
            return

        device = dmd.Devices.findDeviceByIdExact(eventProxy.device)

        if device and hasattr(device, 'openstackProxyComponentUUID'):
            LOG.debug("tagging event on %s with openstack proxy component component uuid %s",
                      eventProxy.device, device.openstackProxyComponentUUID)

            tags = []

            try:
                component = GUIDManager(dmd).getObject(device.openstackProxyComponentUUID)
                if component:

                    # Tag the event with the corresponding openstack component.
                    tags.append(device.openstackProxyComponentUUID)

                    # Also tag it with the openstack endpoint that the
                    # component is part of, if possible.
                    endpoint = component.device()
                    tags.append(IGlobalIdentifier(endpoint).getGUID())
            except Exception:
                LOG.debug("Unable to determine endpoint for proxy component uuid %s",
                          device.openstackProxyComponentUUID)

            # Get OSProcess component, if the event has one
            if eventProxy.component:
                for brain in ICatalogTool(dmd).search('Products.ZenModel.OSProcess.OSProcess', query=Eq('id', eventProxy.component)):
                    try:
                        osprocess = brain.getObject()
                    except Exception:
                        # ignore a stale entry
                        pass
                    else:
                        # Figure out if we have a corresponding software component:
                        try:
                            for software in component.hostedSoftware():
                                if software.binary == osprocess.osProcessClass().id:
                                    # Matches!
                                    tags.append(IGlobalIdentifier(software).getGUID())
                        except Exception:
                            LOG.debug("Unable to append event for OSProcess %s",
                                      osprocess.osProcessClass().id)

            if tags:
                eventProxy.tags.addAll('ZenPacks.zenoss.OpenStackInfrastructure.DeviceProxyComponent', tags)
示例#8
0
def onDeviceDeleted(object, event):
    '''
    Clean up the dangling reference to a device if that device has been removed.
    (Note: we may re-create the device automatically next time someone calls
    self.ensure_proxy_device, though)
    '''
    if not IObjectWillBeAddedEvent.providedBy(
            event) and not IObjectWillBeMovedEvent.providedBy(event):
        if hasattr(object, 'openstackProxyComponentUUID'):
            component = GUIDManager(object.dmd).getObject(
                getattr(object, 'openstackProxyComponentUUID', None))
            if component:
                component.release_proxy_device()
            object.openstackProxyComponentUUID = None
示例#9
0
    def proxy_device(self):
        '''
        Return this component's corresponding proxy device, or None if
        there isn't one at this time.   This method will not attempt to
        create a new device.
        '''

        # A claimed device already exists.
        device = GUIDManager(self.dmd).getObject(
            getattr(self, 'openstackProxyDeviceUUID', None))
        if device:
            # Make sure that the GUID is correct in the reverse direction,
            # then return it.
            return self.ensure_valid_claim(device)

        # Look for device that matches our requirements
        device = self.find_claimable_device()

        # Claim it.
        if device:
            self.claim_proxy_device(device)
            return device

        # We found nothing to claim, so return None.
        return None
示例#10
0
 def need_maintenance(self):
     guid = IGlobalIdentifier(self).getGUID()
     device = GUIDManager(self.dmd).getObject(
         getattr(self, 'openstackProxyDeviceUUID', None))
     if device and getattr(device, 'openstackProxyComponentUUID', None) \
             and device.openstackProxyComponentUUID == guid:
         return False
     return True
示例#11
0
    def component_for_proxy_device(cls, device):
        '''
        Given any device in the system, check if it has a DeviceProxyComponent
        associated with it, and if it does, return that component.
        '''

        uuid = getattr(device, 'openstackProxyComponentUUID', None)
        if uuid:
            component = GUIDManager(device.dmd).getObject(uuid)

            # ensure that the component is also linked back to this device-
            # a uni-directional linkage (device to component, but not
            # component to device) is not valid.
            component_device_uuid = getattr(component,
                                            'openstackProxyDeviceUUID', None)
            if component_device_uuid == IGlobalIdentifier(device).getGUID():
                return component
            else:
                LOG.warning(
                    "Device %s is linked to component %s, but it is not linked back.  Disregarding linkage.",
                    device, component)
        return None
示例#12
0
class CommandAction(IActionBase, TargetableAction):
    implements(IAction)

    id = "command"
    name = "Command"
    actionContentInfo = ICommandActionContentInfo

    shouldExecuteInBatch = False

    def configure(self, options):
        super(CommandAction, self).configure(options)
        self.processQueue = ProcessQueue(options.get("maxCommands", 10))
        self.processQueue.start()

    def setupAction(self, dmd):
        self.guidManager = GUIDManager(dmd)
        self.dmd = dmd

    def execute(self, notification, signal):
        # check to see if we have any targets
        if notification.recipients:
            return super(CommandAction, self).execute(notification, signal)
        else:
            self._execute(notification, signal)

    def executeOnTarget(self, notification, signal, target):
        log.debug("Executing command action: %s on %s", self.name, target)
        environ = {}
        environ["user"] = getattr(self.dmd.ZenUsers, target, None)
        self._execute(notification, signal, environ)

    def _execute(self, notification, signal, extra_env={}):
        self.setupAction(notification.dmd)
        log.debug("Executing command action: %s", self.name)

        if signal.clear:
            command = notification.content["clear_body_format"]
        else:
            command = notification.content["body_format"]

        log.debug("Executing this command: %s", command)

        actor = signal.event.occurrence[0].actor
        device = None
        if actor.element_uuid:
            device = self.guidManager.getObject(actor.element_uuid)

        component = None
        if actor.element_sub_uuid:
            component = self.guidManager.getObject(actor.element_sub_uuid)

        user_env_format = notification.content.get("user_env_format", "") or ""
        env = dict(envvar.split("=") for envvar in user_env_format.split(";") if "=" in envvar)
        environ = {"dev": device, "component": component, "dmd": notification.dmd, "env": env}
        data = self._signalToContextDict(signal, notification)
        environ.update(data)

        if environ.get("evt", None):
            environ["evt"] = self._escapeEvent(environ["evt"])
        if environ.get("clearEvt", None):
            environ["clearEvt"] = self._escapeEvent(environ["clearEvt"])
        environ.update(extra_env)

        # Get the proper command
        command = processTalSource(command, **environ)

        log.debug('Executing this compiled command: "%s"' % command)
        _protocol = EventCommandProtocol(command, notification)

        log.debug("Queueing up command action process.")
        self.processQueue.queueProcess(
            "/bin/sh",
            ("/bin/sh", "-c", command),
            env=environ["env"],
            processProtocol=_protocol,
            timeout=int(notification.content["action_timeout"]),
            timeout_callback=_protocol.timedOut,
        )

    def getActionableTargets(self, target):
        ids = [target.id]
        if isinstance(target, GroupSettings):
            ids = [x.id for x in target.getMemberUserSettings()]
        return ids

    def _escapeEvent(self, evt):
        """
        Escapes the relavent fields of an event context for event commands.
        """
        if evt.message:
            evt.message = self._wrapInQuotes(evt.message)
        if evt.summary:
            evt.summary = self._wrapInQuotes(evt.summary)
        return evt

    def _wrapInQuotes(self, msg):
        """
        Wraps the message in quotes, escaping any existing quote.

        Before:  How do you pronounce "Zenoss"?
        After:  "How do you pronounce \"Zenoss\"?"
        """
        QUOTE = '"'
        BACKSLASH = "\\"
        return "".join((QUOTE, msg.replace(QUOTE, BACKSLASH + QUOTE), QUOTE))
示例#13
0
class sendGCM(IActionBase):
    implements(IAction)

    id = 'sendgcm'
    name = 'Send Alert to Rhybudd'
    actionContentInfo = IConfigurableGCMActionContentInfo


    def setupAction(self, dmd):
        self.guidManager = GUIDManager(dmd)
        self.dmd = dmd

#    def executeBatch(self, notification, signal, targets):
#	log.debug("Executing %s action for targets: %s", self.name, targets)
#
#	self.setupAction(notification.dmd)
#
#        data = _signalToContextDict(signal, self.options.get('zopeurl'), notification, self.guidManager)


    def execute(self, notification, signal):
        self.setupAction(notification.dmd)
        
        data = _signalToContextDict(signal, self.options.get('zopeurl'), notification, self.guidManager)

        #if notification.content['gcmdeviceid'] == "AABBCCDDEEFF00112233":
        #    raise ActionExecutionException("Cannot send a Rhybudd GCM message with the default GCM Key")
        
	#if signal.clear and data['clearEventSummary'].uuid:
	#    log.info('------------------------------------ This is a clear message')
        #else:
        #    log.info('------------------------------------ This is an alert')

        #log.info(data['eventSummary'].summary)
        #log.info(data['eventSummary'].status)
        #log.info(data['eventSummary'].count)
        #log.info(data['eventSummary'].severity)
        #log.info(data['eventSummary'].event_class)
        #log.info(data['eventSummary'].event_class_key) 

        actor = signal.event.occurrence[0].actor
        device = None
        if actor.element_uuid:
            device = self.guidManager.getObject(actor.element_uuid)

	#------------------------------------
	#The CURL Way of doing things
        #curlCall = "curl -H \"Content-Type: application/json\" -X POST -d '{\"gcm_id\": \"%s\",\"evid\": \"%s\",\"device\": \"%s\",\"summary\": \"%s\",\"status\": \"%s\",\"count\": \"%s\",\"severity\": \"%s\",\"event_class\": \"%s\",\"event_class_key\": \"%s\"}' http://api.coldstart.io/1/zenoss.php" % (notification.content['gcmdeviceid'], signal.event.uuid,device,data['eventSummary'].summary,data['eventSummary'].status,data['eventSummary'].count,data['eventSummary'].severity,data['eventSummary'].event_class,data['eventSummary'].event_class_key)
        #call(curlCall, shell=True)

	#------------------------------------
	#The URL Lib way of doing things

	prodstate = ""
	try:
		prodstate = "%s" % data.prodstate  		
	except Exception:
		prodstate = "" 
  		pass

        payload = {
        'filter_key': notification.content['gcmdeviceid'],
"evid": "%s" % signal.event.uuid,
"device": "%s" % device,
"summary": "%s" % data['eventSummary'].summary,
"status": data['eventSummary'].status,
"count": data['eventSummary'].count,
"severity": data['eventSummary'].severity,
"event_class": "%s" % data['eventSummary'].event_class,
"event_class_key": "%s" % data['eventSummary'].event_class_key,

"prodstate": "%s" % prodstate,
"firsttime": "%s" % data['eventSummary'].first_seen_time,
#"componenttext": "%s" % data['eventSummary'].component.text,
"ownerid": "%s" % data['eventSummary'].current_user_name
}

	gcm_details = getattr(self.dmd, 'rhybudd_gcm', Gcm("", ""))
	log.info("%s",gcm_details.gcm_api_key)
	stored_regids = getattr(self.dmd, 'rhybudd_regids', [])
	reg_ids = []

	for regDetails in stored_regids:
          #log.info('Found a GCM ID: %s',regDetails.gcm_reg_id)
	  reg_ids.append(regDetails.gcm_reg_id)

	if gcm_details.gcm_api_key == "":
		#------------------------------------
		#No GCM Key specified so we'll proxy through ColdStart.io so as to not expose our GCM API Key
		log.info('------------------------------------ Sending a coldstart GCM Request')
        	
		coldstart_payload = {'payload': payload, 'regids': reg_ids} 
		data = "json=%s" % json.dumps(coldstart_payload)
		
        	h = httplib.HTTPSConnection('api.coldstart.io')
        	headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
        	h.request('POST', '/1/zenoss', data, headers)
    
	else:
		#------------------------------------
		# Direct to GCM
		log.info('------------------------------------ Sending a direct GCM Request')
		gcm = GCMSERVER(gcm_details.gcm_api_key)
		#reg_ids = ['APA91bEPJ_Pf7k5KgpZxbNBpq9snGjYyQn6Q21w_JYl-_4FADgNH54kzcQxGb6Wjb1PkWGiEaVQE0MXhMw7q-jTOvDN_smiaSa96F9sEOLd1xYt4yd7PiYVCYsVULiFoN_isvz1AcN-HXjZVfipBLBIzN5ohqN_MM2tpmBj9JFpdwjFgM6ZNhPU']
		response = gcm.json_request(registration_ids=reg_ids, data=payload, collapse_key=signal.event.uuid, time_to_live=0)
		log.info("%s",json.dumps(response))
		log.info('------------------------------------ Sent a direct GCM Request')	

    def updateContent(self, content=None, data=None):
        content['gcmdeviceid'] = data.get('gcmdeviceid') 
示例#14
0
 def setupAction(self, dmd):
     self.guidManager = GUIDManager(dmd)
     self.dmd = dmd
     self.zenossHostname = dmd.zenossHostname
示例#15
0
 def setupAction(self, dmd):
     log.debug('[research] setup : %s' % (self.name))
     self.guidManager = GUIDManager(dmd)
     self.dmd = dmd
示例#16
0
class WinCommandAction(IActionBase):
    """
    Derived class to execute an arbitrary command on a remote windows machine
    when a notification is triggered.
    """
    implements(IAction)

    id = 'wincommand'
    name = 'WinCommand'
    actionContentInfo = IWinCommandActionContentInfo

    def setupAction(self, dmd):
        """
        Configure the action with properties form the dmd.
        """
        self.guidManager = GUIDManager(dmd)
        self.dmd = dmd

    def updateContent(self, content=None, data=None):
        """
        Update notification content.
        Called when changes are submitted in the 'Edit Notification' form.
        """
        updates = dict()

        for k in ('wincmd_command', 'clear_wincmd_command'):
            updates[k] = data.get(k)

        content.update(updates)

    def execute(self, notification, signal):
        """
        Set up the execution environment and run a CMD command.
        """
        self.setupAction(notification.dmd)
        log.debug('Executing action: {0}'.format(self.name))

        if signal.clear:
            command = notification.content['clear_wincmd_command']
        else:
            command = notification.content['wincmd_command']

        environ = self._get_environ(notification, signal)

        if not command:
            log.debug("The CMD command was not set")
            return

        if not hasattr(environ['dev'], 'windows_servername'):
            log.debug("The target device is non-Windows device")
            return

        try:
            command = processTalSource(command, **environ)
        except Exception:
            log.error('Unable to perform TALES evaluation on "{0}" '
                      '-- is there an unescaped $?'.format(command))

        log.debug('Executing this compiled command "{0}"'.format(command))
        self._execute_command(environ['dev'], command)

    def _get_environ(self, notification, signal):
        """
        Set up TALES environment for the action.
        """
        actor = signal.event.occurrence[0].actor
        device = None
        if actor.element_uuid:
            device = self.guidManager.getObject(actor.element_uuid)

        component = None
        if actor.element_sub_uuid:
            component = self.guidManager.getObject(actor.element_sub_uuid)

        environ = dict(dev=device, component=component, dmd=notification.dmd)
        data = _signalToContextDict(signal, self.options.get('zopeurl'),
                                    notification, self.guidManager)
        environ.update(data)
        return environ

    def _conn_info(self, device):
        """
        Return a ConnectionInfo object with device credentials.
        """
        service = device.zWinScheme
        if hasattr(device, 'zWinUseWsmanSPN') and device.zWinUseWsmanSPN:
            service = 'wsman'
        envelope_size = getattr(device, 'zWinRMEnvelopeSize', 512000)
        locale = getattr(device, 'zWinRMLocale', 'en-US')
        code_page = getattr(device, 'zWinRSCodePage', 65001)
        include_dir = getattr(device, 'zWinRMKrb5includedir', None)
        disable_rdns = getattr(device, 'kerberos_rdns', False)
        connect_timeout = getattr(device, 'zWinRMConnectTimeout', 60)
        return ConnectionInfo(
            hostname=device.windows_servername() or device.manageIp,
            auth_type='kerberos' if '@' in device.zWinRMUser else 'basic',
            username=device.zWinRMUser,
            password=device.zWinRMPassword,
            scheme=device.zWinScheme,
            port=int(device.zWinRMPort),
            connectiontype='Keep-Alive',
            keytab=device.zWinKeyTabFilePath,
            dcip=device.zWinKDC,
            trusted_realm=device.zWinTrustedRealm,
            trusted_kdc=device.zWinTrustedKDC,
            ipaddress=device.manageIp,
            service=service,
            envelope_size=envelope_size,
            locale=locale,
            code_page=code_page,
            include_dir=include_dir,
            disable_rdns=disable_rdns,
            connect_timeout=connect_timeout)

    def _execute_command(self, device, command):
        """
        Create WinRS client and run the command remotely.
        """
        winrs = SingleCommandClient(self._conn_info(device))
        result = winrs.run_command(str(command))

        result.addCallback(lambda res: self._on_success(device, res, command))
        result.addErrback(lambda err: self._on_error(device, err))

    def _on_success(self, device, result, command):
        """
        Called after the command was successfully executed and
        CommandResponse instance was received.
        """
        if result.exit_code != 0:
            log.error("Command '{0}' failed on host {1}. Reason: {2}".format(
                command, device.manageIp, ' '.join(result.stderr)))
        else:
            log.debug("Command '{0}' was executed on host {1}: {2}".format(
                command, device.manageIp, ' '.join(result.stdout)))

    def _on_error(self, device, error):
        """
        Called if an error occured when connecting to the remote host.
        """
        if hasattr(error, 'value'):
            log.error(error.value)
        else:
            log.error(error)  # Not twisted failure.
class WinCommandAction(IActionBase):
    """
    Derived class to execute an arbitrary command on a remote windows machine
    when a notification is triggered.
    """
    implements(IAction)

    id = 'wincommand'
    name = 'WinCommand'
    actionContentInfo = IWinCommandActionContentInfo

    def setupAction(self, dmd):
        """
        Configure the action with properties form the dmd.
        """
        self.guidManager = GUIDManager(dmd)
        self.dmd = dmd

    def updateContent(self, content=None, data=None):
        """
        Update notification content.
        Called when changes are submitted in the 'Edit Notification' form.
        """
        updates = dict()

        for k in ('wincmd_command', 'clear_wincmd_command'):
            updates[k] = data.get(k)

        content.update(updates)

    def execute(self, notification, signal):
        """
        Set up the execution environment and run a CMD command.
        """
        self.setupAction(notification.dmd)
        log.debug('Executing action: {0}'.format(self.name))

        if signal.clear:
            command = notification.content['clear_wincmd_command']
        else:
            command = notification.content['wincmd_command']

        environ = self._get_environ(notification, signal)

        if not command:
            log.debug("The CMD command was not set")
            return

        if not hasattr(environ['dev'], 'windows_servername'):
            log.debug("The target device is non-Windows device")
            return

        try:
            command = processTalSource(command, **environ)
        except Exception:
            log.error('Unable to perform TALES evaluation on "{0}" '
                '-- is there an unescaped $?'.format(command))

        log.debug('Executing this compiled command "{0}"'.format(command))
        self._execute_command(environ['dev'], command)

    def _get_environ(self, notification, signal):
        """
        Set up TALES environment for the action.
        """
        actor = signal.event.occurrence[0].actor
        device = None
        if actor.element_uuid:
            device = self.guidManager.getObject(actor.element_uuid)

        component = None
        if actor.element_sub_uuid:
            component = self.guidManager.getObject(actor.element_sub_uuid)

        environ = dict(dev=device, component=component, dmd=notification.dmd)
        data = _signalToContextDict(signal, self.options.get('zopeurl'),
                                    notification, self.guidManager)
        environ.update(data)
        return environ

    def _conn_info(self, device):
        """
        Return a ConnectionInfo object with device credentials.
        """
        return ConnectionInfo(
            hostname=device.windows_servername() or device.manageIp,
            auth_type='kerberos' if '@' in device.zWinRMUser else 'basic',
            username=device.zWinRMUser,
            password=device.zWinRMPassword,
            scheme=device.zWinScheme,
            port=int(device.zWinRMPort),
            connectiontype='Keep-Alive',
            keytab=device.zWinKeyTabFilePath,
            dcip=device.zWinKDC)

    def _execute_command(self, device, command):
        """
        Create WinRS client and run the command remotely.
        """
        winrs = create_single_shot_command(self._conn_info(device))
        result = winrs.run_command(str(command))

        result.addCallback(lambda res: self._on_success(device, res, command))
        result.addErrback(lambda err: self._on_error(device, err))

    def _on_success(self, device, result, command):
        """
        Called after the command was successfully executed and
        CommandResponse instance was received.
        """
        if result.exit_code != 0:
            log.error("Command '{0}' failed on host {1}. Reason: {2}".format(
                command, device.manageIp, ' '.join(result.stderr)))
        else:
            log.debug("Command '{0}' was executed on host {1}: {2}".format(
                command, device.manageIp, ' '.join(result.stdout)))

    def _on_error(self, device, error):
        """
        Called if an error occured when connecting to the remote host.
        """
        if hasattr(error, 'value'):
            log.error(error.value)
        else:
            log.error(error)  # Not twisted failure.
class PagerDutyEventsAPIAction(IActionBase):
    """
    Derived class to contact PagerDuty's events API when a notification is
    triggered.
    """
    implements(IAction)

    id = 'pagerduty'
    name = 'PagerDuty'
    actionContentInfo = IPagerDutyEventsAPIActionContentInfo

    shouldExecuteInBatch = False

    def __init__(self):
        super(PagerDutyEventsAPIAction, self).__init__()

    def setupAction(self, dmd):
        self.guidManager = GUIDManager(dmd)
        self.dmd = dmd

    def execute(self, notification, signal):
        """
        Sets up the execution environment and POSTs to PagerDuty's Event API.
        """
        log.debug('Executing Pagerduty Events API action: %s', self.name)
        self.setupAction(notification.dmd)

        if signal.clear:
            eventType = EventType.RESOLVE
        elif signal.event.status == STATUS_ACKNOWLEDGED:
            eventType = EventType.ACKNOWLEDGE
        else:
            eventType = EventType.TRIGGER

        # Set up the TALES environment
        environ = {'dmd': notification.dmd, 'env':None}

        actor = signal.event.occurrence[0].actor

        device = None
        if actor.element_uuid:
            device = self.guidManager.getObject(actor.element_uuid)
        environ.update({'dev': device})

        component = None
        if actor.element_sub_uuid:
            component = self.guidManager.getObject(actor.element_sub_uuid)
        environ.update({'component': component})

        data = _signalToContextDict(signal, self.options.get('zopeurl'), notification, self.guidManager)
        environ.update(data)

        try:
            details_list = json.loads(notification.content['details'])
        except ValueError:
            raise ActionExecutionException('Invalid JSON string in details')

        details = dict()
        for kv in details_list:
            details[kv['key']] = kv['value']

        details['zenoss'] = {
            'version'        : ZENOSS_VERSION,
            'zenpack_version': zenpack_version()
        }
        body = {'event_type': eventType,
                'client'    : 'Zenoss',
                'client_url': '${urls/eventUrl}',
                'details'   : details}

        for prop in REQUIRED_PROPERTIES:
            if prop in notification.content:
                body[prop] = notification.content[prop]
            else:
                raise ActionExecutionException("Required property '%s' not found" % (prop))

        self._performRequest(body, environ)

    def _performRequest(self, body, environ):
        """
        Actually performs the request to PagerDuty's Event API.

        Raises:
            ActionExecutionException: Some error occurred while contacting
            PagerDuty's Event API (e.g., API down, invalid service key).
        """
        request_body = json.dumps(self._processTalExpressions(body, environ))

        headers = {'Content-Type' : 'application/json'}
        req = urllib2.Request(EVENT_API_URI, request_body, headers)
        try:
            f = urllib2.urlopen(req, None, API_TIMEOUT_SECONDS)
        except urllib2.URLError as e:
            if hasattr(e, 'reason'):
                msg = 'Failed to contact the PagerDuty server: %s' % (e.reason)
                raise ActionExecutionException(msg)
            elif hasattr(e, 'code'):
                msg = 'The PagerDuty server couldn\'t fulfill the request: HTTP %d (%s)' % (e.code, e.msg)
                raise ActionExecutionException(msg)
            else:
                raise ActionExecutionException('Unknown URLError occurred')

        response = f.read()
        f.close()

    def _processTalExpressions(self, data, environ):
        if type(data) is str or type(data) is unicode:
            try:
                return processTalSource(data, **environ)
            except Exception:
                raise ActionExecutionException(
                    'Unable to perform TALES evaluation on "%s" -- is there an unescaped $?' % data)
        elif type(data) is list:
            return [self._processTalExpressions(e, environ) for e in data]
        elif type(data) is dict:
            return dict([(k, self._processTalExpressions(v, environ)) for (k, v) in data.iteritems()])
        else:
            return data

    def updateContent(self, content=None, data=None):
        updates = dict()

        for k in NotificationProperties.ALL:
            updates[k] = data.get(k)

        content.update(updates)
class HttpBinAction(IActionBase):
    implements(IAction)

    id = "HttpBin"
    name = "HttpBin"
    skipfails = True

    actionContentInfo = IHttpBinActionContentInfo

    shouldExecuteInBatch = False

    def logEventAction(self, notificationId, eventId, status, payload=None):
        logPayload = "Notification {} sent event {} as {} to HttpBin with payload: {}".format(
            notificationId,
            eventId,
            status,
            payload,
        )
        LOG.info(logPayload)

    def setupAction(self, dmd):
        self.guidManager = GUIDManager(dmd)
        self.dmd = dmd
        self.zep = getFacade("zep", self.dmd)
        self.statuses = {
            "0": "New",
            "1": "Acknowledged",
            "2": "Suppressed",
            "3": "Closed",
            "4": "Cleared",
            "5": "Dropped",
            "6": "Aged",
        }

    def updateEvent(self, evid, comment=None):
        if not evid:
            LOG.warn("Received an empty evid -- ignoring")
            return

        if comment:
            tryNum = 0
            while tryNum < 3:
                try:
                    self.zep.addNote(evid, comment, 'admin')
                    return
                except (ServiceConnectionError, ZepConnectionError,
                        ZepConnectionTimeout):
                    tryNum += 1

        LOG.error("Unable to update event: %s" % evid)
        return

    def buildPayload(self, signal, notification, data):
        skipfails = notification.content.get('skipfails', False)
        event = EventSummaryProxy(signal.event)

        hbSource = processTALES(notification.content.get('hbSource'),
                                skipfails, data)
        hbEventClass = processTALES(notification.content.get('hbEventClass'),
                                    skipfails, data)
        hbDescription = processTALES(notification.content.get('hbDescription'),
                                     skipfails, data)
        hbFullDescription = processTALES(
            notification.content.get('hbFullDescription'), skipfails, data)

        severity = EventManagerBase.severities.get(
            event.severity, 'Unknown (%s)' % event.severity)
        clear = signal.clear

        payload = {
            "source": hbSource,
            "event_class": hbEventClass,
            "severity": 'Clear' if clear else severity,
            "summary": hbDescription,
            "details": hbFullDescription,
        }

        return payload

    def execute(self, notification, signal):
        url = "https://httpbin.org/post"

        headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
        }

        # Building out context data for TALES expressions
        data = self._signalToContextDict(signal, notification)
        actor = signal.event.occurrence[0].actor

        if not data.get('dev', False):
            device = None
            if actor.element_uuid:
                device = self.guidManager.getObject(actor.element_uuid)
            data['dev'] = device
            data['device'] = device

        if not data.get('component', False):
            component = None
            if actor.element_sub_uuid:
                component = self.guidManager.getObject(actor.element_sub_uuid)
            data['component'] = component

        payload = self.buildPayload(signal, notification, data)

        s = requests.Session()
        requestData = dict(
            url=url,
            headers=headers,
            data=json.dumps(payload),
        )

        try:
            request = s.post(**requestData)
        except Exception as e:
            LOG.error("Unable to send event (%s): %s" % (
                signal.event.uuid,
                e.message,
            ))

            comment = "Unable to send event (%s)" % e.message
            self.updateEvent(signal.event.uuid, comment)
            return

        if request.ok:
            LOG.info("Request to HttpBin returned: %s" % request.content)
        elif not request.ok:
            LOG.error("Unable to send event (%s): %s" % (
                signal.event.uuid,
                request.content,
            ))

            comment = "Unable to send event (%s)" % request.content
            self.updateEvent(signal.event.uuid, comment)
            return

        responseContent = request.json()
        if responseContent.get('status', False) == 'failure':
            LOG.error("Unable to send event (%s): %s" % (
                signal.event.uuid,
                request.content,
            ))

            comment = "Unable to send event (%s)" % request.content
            self.updateEvent(signal.event.uuid, comment)
            return

        status = self.statuses.get(str(signal.event.status),
                                   'Unknown (%s)' % signal.event.status)
        comment = "Sent to HttpBin by Notification: %s using Trigger: %s" % (
            notification.titleOrId(), data.get('trigger', {}).get(
                'name', 'Unknown'))

        self.logEventAction(notification.titleOrId(), signal.event.uuid,
                            status, payload)
        self.updateEvent(signal.event.uuid, comment)

        return
示例#20
0
    def apply(self, eventProxy, dmd):

        # See ZPS-1677 for explanation.  This workaround will hopefully be
        # removed in the future (ZPS-1685)
        if eventProxy.eventClass == '/Status/Ping':
            return

        device = dmd.Devices.findDeviceByIdExact(eventProxy.device)

        if device and hasattr(device, 'openstackProxyComponentUUID'):
            LOG.debug(
                "tagging event on %s with openstack proxy component component uuid %s",
                eventProxy.device, device.openstackProxyComponentUUID)

            tags = []

            try:
                component = GUIDManager(dmd).getObject(
                    device.openstackProxyComponentUUID)
                if component:

                    # Tag the event with the corresponding openstack component.
                    tags.append(device.openstackProxyComponentUUID)

                    # Also tag it with the openstack endpoint that the
                    # component is part of, if possible.
                    endpoint = component.device()
                    tags.append(IGlobalIdentifier(endpoint).getGUID())
            except Exception:
                LOG.debug(
                    "Unable to determine endpoint for proxy component uuid %s",
                    device.openstackProxyComponentUUID)

            # Get OSProcess component, if the event has one
            if eventProxy.component:
                for brain in ICatalogTool(dmd).search(
                        'Products.ZenModel.OSProcess.OSProcess',
                        query=Eq('id', eventProxy.component)):
                    try:
                        osprocess = brain.getObject()
                    except Exception:
                        # ignore a stale entry
                        pass
                    else:
                        # Figure out if we have a corresponding software component:
                        try:
                            for software in component.hostedSoftware():
                                if software.binary == osprocess.osProcessClass(
                                ).id:
                                    # Matches!
                                    tags.append(
                                        IGlobalIdentifier(software).getGUID())
                        except Exception:
                            LOG.debug(
                                "Unable to append event for OSProcess %s",
                                osprocess.osProcessClass().id)

            if tags:
                eventProxy.tags.addAll(
                    'ZenPacks.zenoss.OpenStackInfrastructure.DeviceProxyComponent',
                    tags)
示例#21
0
 def setupAction(self, dmd):
     self.guidManager = GUIDManager(dmd)
     self.page_command = dmd.pageCommand
示例#22
0
class UserCommandAction(IActionBase, TargetableAction):
    implements(IAction)

    id = 'user_command'
    name = 'User Command'
    actionContentInfo = IUserCommandActionContentInfo

    shouldExecuteInBatch = False

    def configure(self, options):
        super(UserCommandAction, self).configure(options)
        self.processQueue = ProcessQueue(options.get('maxCommands', 10))
        self.processQueue.start()

    def setupAction(self, dmd):
        self.guidManager = GUIDManager(dmd)
        self.dmd = dmd

    def executeOnTarget(self, notification, signal, target):
        self.setupAction(notification.dmd)

        log.debug('Executing action: %s on %s', self.name, target)

        if signal.clear:
            command = notification.content['clear_body_format']
        else:
            command = notification.content['body_format']

        log.debug('Executing this command: %s', command)

        actor = signal.event.occurrence[0].actor
        device = None
        if actor.element_uuid:
            device = self.guidManager.getObject(actor.element_uuid)

        component = None
        if actor.element_sub_uuid:
            component = self.guidManager.getObject(actor.element_sub_uuid)

        user_env_format = notification.content['user_env_format']
        env = dict( envvar.split('=') for envvar in user_env_format.split(';') if '=' in envvar)

        environ = {'dev': device, 'component': component, 'dmd': notification.dmd,
                   'env': env}
        data = _signalToContextDict(signal, self.options.get('zopeurl'), notification, self.guidManager)
        environ.update(data)

        if environ.get('evt', None):
            environ['evt'] = self._escapeEvent(environ['evt'])

        if environ.get('clearEvt', None):
            environ['clearEvt'] = self._escapeEvent(environ['clearEvt'])

        environ['user'] = getattr(self.dmd.ZenUsers, target, None)

        try:
            command = processTalSource(command, **environ)
        except Exception:
            raise ActionExecutionException('Unable to perform TALES evaluation on "%s" -- is there an unescaped $?' % command)

        log.debug('Executing this compiled command: "%s"' % command)
        _protocol = EventCommandProtocol(command)

        log.debug('Queueing up command action process.')
        self.processQueue.queueProcess(
            '/bin/sh',
                ('/bin/sh', '-c', command),
            env=environ['env'],
            processProtocol=_protocol,
            timeout=int(notification.content['action_timeout']),
            timeout_callback=_protocol.timedOut
        )

    def getActionableTargets(self, target):
        ids = [target.id]
        if isinstance(target, GroupSettings):
            ids = [x.id for x in target.getMemberUserSettings()]
        return ids

    def updateContent(self, content=None, data=None):
        updates = dict()

        properties = ['body_format', 'clear_body_format', 'action_timeout', 
                      'user_env_format']
        for k in properties:
            updates[k] = data.get(k)

        content.update(updates)

    def _escapeEvent(self, evt):
        """
        Escapes the relavent fields of an event context for event commands.
        """
        if evt.message:
            evt.message = self._wrapInQuotes(evt.message)
        if evt.summary:
            evt.summary = self._wrapInQuotes(evt.summary)
        return evt

    def _wrapInQuotes(self, msg):
        """
        Wraps the message in quotes, escaping any existing quote.

        Before:  How do you pronounce "Zenoss"?
        After:  "How do you pronounce \"Zenoss\"?"
        """
        QUOTE = '"'
        BACKSLASH = '\\'
        return ''.join((QUOTE, msg.replace(QUOTE, BACKSLASH + QUOTE), QUOTE))
 def setupAction(self, dmd):
     self.guidManager = GUIDManager(dmd)
     self.dmd = dmd
示例#24
0
 def __init__(self, dmd):
     self.dmd = dmd
     self.notification_manager = self.dmd.getDmdRoot(
         NotificationSubscriptionManager.root)
     self.guidManager = GUIDManager(dmd)
 def setupAction(self, dmd):
     """
     Configure the action with properties form the dmd.
     """
     self.guidManager = GUIDManager(dmd)
     self.dmd = dmd
示例#26
0
 def setupAction(self, dmd):
     self.guidManager = GUIDManager(dmd)
     self.dmd = dmd
     self.page_command = dmd.pageCommand
     self.zenossHostname = dmd.zenossHostname
示例#27
0
 def setupAction(self, dmd):
     """
     Configure the action with properties form the dmd.
     """
     self.guidManager = GUIDManager(dmd)
     self.dmd = dmd
示例#28
0
class JIRAReporter(IActionBase, TargetableAction):
    implements(IAction)

    id = 'JIRAReporter'
    name = 'JIRA Issue Reporter'
    actionContentInfo = IJIRAActionContentInfo

    shouldExecuteInBatch = False

    def __init__(self):
        log.debug('[research] %s : initialized' % (self.id))
        super(JIRAReporter, self).__init__()

        self.connected = False
        self.jira = None

    def setupAction(self, dmd):
        log.debug('[research] setup : %s' % (self.name))
        self.guidManager = GUIDManager(dmd)
        self.dmd = dmd

    def executeOnTarget(self, notification, signal, target):
        self.setupAction(notification.dmd)

        log.debug('[research] execute : %s on %s' % (self.name, target))

        jiraURL = notification.content['jira_instance']
        jiraUser = notification.content['jira_user']
        jiraPass = notification.content['jira_password']

        issueProject = notification.content['issue_project']
        issueType = notification.content['issue_type']
        issuePriority = notification.content['issue_priority_key']
        customfields = notification.content['customfield_keypairs']
        eventRawData = notification.content['event_rawdata']
        serviceRoot = notification.content['service_group_root']

        summary = ''
        description = ''

        if (signal.clear):
            log.info('[research] event cleared : %s' % (target))
            description = notification.content['clear_issue_description']
        else:
            log.warn('[research] event detected : %s' % (target))
            summary = notification.content['issue_summary']
            description = notification.content['issue_description']

        actor = signal.event.occurrence[0].actor
        device = None
        if (actor.element_uuid):
            device = self.guidManager.getObject(actor.element_uuid)

        component = None
        if (actor.element_sub_uuid):
            component = self.guidManager.getObject(actor.element_sub_uuid)

        environ = {
            'dev': device,
            'component': component,
            'dmd': notification.dmd
        }

        data = _signalToContextDict(signal, self.options.get('zopeurl'),
                                    notification, self.guidManager)

        environ.update(data)

        if (environ.get('evt', None)):
            environ['evt'] = self._escapeEvent(environ['evt'])

        if (environ.get('clearEvt', None)):
            environ['clearEvt'] = self._escapeEvent(environ['clearEvt'])

        environ['user'] = getattr(self.dmd.ZenUsers, target, None)

        issueValues = {
            'summary': summary,
            'description': description,
            'eventraw': eventRawData,
            'customfields': customfields
        }

        log.debug('[research] base issue values : %s' % (issueValues))

        targetValues = {
            'project': issueProject,
            'issuetype': issueType,
            'priority': issuePriority,
            'serviceroot': serviceRoot
        }

        log.debug('[research] base target values : %s' % (targetValues))

        self.connectJIRA(jiraURL, jiraUser, jiraPass)

        if (signal.clear):
            self.clearEventIssue(environ, targetValues, issueValues)
        else:
            self.createEventIssue(environ, targetValues, issueValues)

        log.debug("[research] event update reported : %s" % (jiraURL))

    def getActionableTargets(self, target):
        ids = [target.id]
        if isinstance(target, GroupSettings):
            ids = [x.id for x in target.getMemberUserSettings()]
        return ids

    def _escapeEvent(self, evt):
        """
        Escapes the relavent fields of an event context for event commands.
        """
        if evt.message:
            evt.message = self._wrapInQuotes(evt.message)
        if evt.summary:
            evt.summary = self._wrapInQuotes(evt.summary)
        return evt

    def _wrapInQuotes(self, msg):
        """
        Wraps the message in quotes, escaping any existing quote.

        Before:  How do you pronounce "Zenoss"?
        After:  "How do you pronounce \"Zenoss\"?"
        """
        QUOTE = '"'
        BACKSLASH = '\\'
        return ''.join((QUOTE, msg.replace(QUOTE, BACKSLASH + QUOTE), QUOTE))

    def updateContent(self, content=None, data=None):
        super(JIRAReporter, self).updateContent(content, data)

        updates = dict()
        properties = [
            'jira_instance', 'jira_user', 'jira_password', 'issue_project',
            'issue_type', 'issue_priority_key', 'issue_summary',
            'issue_description', 'clear_issue_summary', 'customfield_keypairs',
            'event_rawdata', 'service_group_root'
        ]

        for k in properties:
            updates[k] = data.get(k)

        content.update(updates)

# jira client methods

    def connectJIRA(self, URL, user, password):
        log.debug('[research] : connecting to %s' % (URL))

        basicauth = (user, password)

        try:
            self.jira = JIRA(options={'server': URL}, basic_auth=basicauth)
            self.connected = True
            log.debug('[research] : connected to %s' % (URL))
        except JIRAError as jx:
            log.error('[research] jira.error : %s' % (jx))
        except Exception as ex:
            log.debug('[research] exception : %s' % (ex))
        finally:
            log.debug('[research] connection info (%s)' % (URL))

    def setIssueValues(self, data, targetValues, issueValues):
        log.debug('[research] process issue values')

        if ('project' in targetValues):
            issueValues['project'] = {'key': targetValues['project']}
        if ('issuetype' in targetValues):
            issueValues['issuetype'] = {'name': targetValues['issuetype']}

        issueValues['summary'] = self.processEventFields(
            data, issueValues['summary'], 'Summary')
        issueValues['description'] = self.processEventFields(
            data, issueValues['description'], 'Description')

        log.debug('[research] issue values : %s' % (issueValues))

        return issueValues

    def setCustomFieldValues(self, data, targetValues, issueValues):
        log.debug('[research] process customfield values')

        customfields = None
        if ('customfields' in issueValues):
            customfields = issueValues['customfields']
            del issueValues['customfields']

        if (customfields):
            customfields = json.loads(customfields)
        else:
            customfields = {}

        if ('priority' in targetValues):
            customfields['Priority'] = targetValues['priority']

        if ('serviceroot' in targetValues):
            customfields['Service'] = targetValues['serviceroot']

        if ('eventraw' in issueValues):
            customfields['Zenoss EventRAW'] = self.processEventFields(
                data, issueValues['eventraw'], 'Event Raw Data')
            del issueValues['eventraw']

        customfields = self.setZenossFields(data, customfields)

        log.debug('[research] customfield values : %s' % (customfields))

        createmeta = self.jira.createmeta(
            projectKeys=targetValues['project'],
            issuetypeNames=targetValues['issuetype'],
            expand='projects.issuetypes.fields')

        issuetype = None
        fields = None
        if (createmeta):
            if ('projects' in createmeta):
                if ('issuetypes' in createmeta['projects'][0]):
                    issuetype = createmeta['projects'][0]['issuetypes'][0]
                    log.debug('[research] createmeta issuetype : available')
            if (issuetype):
                if ('fields' in issuetype):
                    fields = issuetype['fields']
                    log.debug('[research] createmeta fields : available')
        else:
            log.debug('[research] createmeta : NOT AVAILABLE')

        if (fields):
            for fKey, fAttr in fields.iteritems():
                if ('name' in fAttr):
                    if (fAttr['name'] in customfields):
                        log.debug('[research] customfield found')
                        fieldValue = customfields[fAttr['name']]
                        if ('allowedValues' in fAttr):
                            log.debug('[research] has customfield options')
                            fieldValue = self.getCustomFieldOption(
                                fAttr['allowedValues'], fieldValue)
                        if (fieldValue):
                            log.debug('[research] cf (%s) set to %s' %
                                      (fAttr['name'], fieldValue))
                            try:
                                if (fAttr['schema']['type'] in ['array']):
                                    fieldValue = [fieldValue]
                            except:
                                pass
                            issueValues[fKey] = fieldValue

        log.debug('[research] issue customfields : %s' % (issueValues))

        return issueValues

    def setZenossFields(self, data, customfields):
        log.debug('[research] process customfield values')

        if (not customfields):
            customfields = {}

        zEventID = self.getEventID(data)
        if (zEventID):
            customfields['Zenoss ID'] = zEventID

        zDeviceID = self.getDeviceID(data)
        if (zDeviceID):
            customfields['Zenoss DevID'] = zDeviceID

        zBaseURL = self.getBaseURL(data)
        if (zBaseURL):
            customfields['Zenoss Instance'] = zBaseURL

        zEnv = self.getEnvironment(data)
        if (zEnv):
            customfields['Environment'] = zEnv

        if ('Service' in customfields):
            zSrvc = self.getServiceGroup(data, customfields['Service'])
            if (zSrvc):
                customfields['Service'] = zSrvc

        zLoc = self.getLocation(data)
        if (zLoc):
            customfields['DataCenter'] = zLoc

        log.debug('[research] Zenoss customfields : %s' % (customfields))

        return customfields

    def createEventIssue(self, data, targetValues, issueValues):
        log.debug('[research] create event issue')

        project = targetValues['project']

        eventID = self.getEventID(data)
        baseHost = self.getBaseHost(data)
        deviceID = self.getDeviceID(data)

        hasIssues = self.hasEventIssues(project, baseHost, eventID)

        if (hasIssues):
            log.warn('[research] issue exists for EventID %s' % (eventID))
        else:
            issueValues = self.setIssueValues(data, targetValues, issueValues)
            issueValues = self.setCustomFieldValues(data, targetValues,
                                                    issueValues)

            newissue = self.jira.create_issue(fields=issueValues)
            log.info('[research] issue created : %s' % (newissue.key))

    def clearEventIssue(self, data, targetValues, issueValues):
        log.debug('[research] clear event issue')

        project = targetValues['project']

        eventID = self.getEventID(data)
        baseHost = self.getBaseHost(data)

        issues = self.getEventIssues(project, baseHost, eventID)

        if (not issues):
            log.warn('[research] no issue mapped to clear : %s' % (eventID))
            return

        issueValues = self.setIssueValues(data, targetValues, issueValues)

        description = issueValues['description']

        eventCLR = self.getEventClearDate(data)

        for issue in issues:
            zenossCLR = self.getCustomFieldID(issue, 'Zenoss EventCLR')
            issuekey = issue.key
            if (zenossCLR):
                issue.update(fields={zenossCLR: eventCLR})
                log.info('[research] EventCLR updated : %s' % (issuekey))
            if (description):
                self.jira.add_comment(issue.key, description)
                log.info('[research] EventCLR commented : %s' % (issuekey))

    def hasEventIssues(self, project, eventINS, eventID):
        log.debug('[research] has event issues')

        issues = self.getEventIssues(project, eventINS, eventID)

        log.debug('[research] has event issues : %s' % (len(issues) > 0))

        return (len(issues) > 0)

    def getEventIssues(self, project, eventINS, eventID):
        log.debug('[research] get event issues')

        issues = []

        if (eventID):
            issueFilter = '(project = "%s")'
            issueFilter += ' and ("Zenoss Instance" ~ "%s")'
            issueFilter += ' and ("Zenoss ID" ~ "%s")'
            issueFilter = issueFilter % (project, eventINS, eventID)
            log.debug('[research] event issue filter : %s' % (issueFilter))

            try:
                issues = self.jira.search_issues(issueFilter)
                log.debug('[research] event issues : %s' % (len(issues)))
            except JIRAError as jx:
                log.error('[research] jira.error : %s' % (jx))
            except Exception as ex:
                log.error('[research] exception : %s' % (ex))

        return issues

    def getCustomFieldOption(self,
                             fieldOptions,
                             value,
                             defaultValue='',
                             exactMatch=False,
                             firstMatch=False):
        log.debug('[research] get customfield options')

        if (not value):
            return None

        if (not fieldOptions):
            return None

        bDefault = False
        matchValue = None

        if (value.__class__.__name__ in ('str', 'unicode')):
            value = value.split(';')
            if (len(value) > 1):
                defaultValue = value[1].strip()
                log.debug('[research] option default : %s' % (defaultValue))
            value = value[0].strip()

        if (not value):
            log.debug('[research] invalid option value : %s' % (value))

        for av in fieldOptions:
            if ('value' in av):
                valueName = av['value']
            elif ('name' in av):
                valueName = av['name']
            else:
                continue

            if (value):
                if (value.__class__.__name__ in ('str', 'unicode')):
                    if (exactMatch):
                        value = '^%s$' % (value)
                    if (re.match(value, valueName, re.IGNORECASE)):
                        if ('id' in av):
                            matchValue = {'id': av['id']}
                        else:
                            matchValue = valueName
                        if (firstMatch):
                            break

            if (not defaultValue):
                continue

            if (defaultValue.__class__.__name__ in ('str', 'unicode')):
                if (re.match(defaultValue, valueName, re.IGNORECASE)):
                    bDefault = True
                    if ('id' in av):
                        defaultValue = {'id': av['id']}
                    else:
                        defaultValue = valueName
                    if (not value):
                        break

        if (not matchValue):
            if (bDefault):
                log.debug('[research] default option : %s' % (defaultValue))
                matchValue = defaultValue

        return matchValue

    def getCustomFieldID(self, issue, fieldName):
        log.debug('[research] get issue customfield ID')

        fieldID = ''

        for field in self.jira.fields():
            if (field['name'].lower() == fieldName.lower()):
                log.debug('[research] customfield matched %s' % (fieldName))
                fieldID = field['id']
                break

        return fieldID

    def getEventID(self, data):
        log.debug('[research] get eventID')

        eventID = '${evt/evid}'
        try:
            eventID = self.processEventFields(data, eventID, 'eventID')
        except Exception:
            eventID = ''

        return eventID

    def getEventClearDate(self, data):
        log.debug('[research] get event clear date')

        eventCLR = '${evt/stateChange}'
        try:
            eventCLR = self.processEventFields(data, eventCLR, 'clear date')
        except Exception:
            eventCLR = ''

        if (eventCLR):
            try:
                eventCLR = datatime.strptime(
                    eventCLR,
                    '%Y-%m-%d %H:%M:%S').isoformat()[:19] + '.000+0000'
            except:
                try:
                    eventCLR = datatime.strptime(
                        eventCLR,
                        '%Y-%m-%d %H:%M:%S.%f').isoformat()[:19] + '.000+0000'
                except:
                    eventCLR = ''

        if (not eventCLR):
            eventCLR = datetime.now().isoformat()[:19] + '.000+0000'

        return eventCLR

    def getDeviceID(self, data):
        log.debug('[research] get deviceID')

        deviceID = '${evt/device}'
        try:
            deviceID = self.processEventFields(data, deviceID, 'deviceID')
        except Exception:
            deviceID = ''

        return deviceID

    def getBaseURL(self, data):
        log.debug('[research] get baseURL')

        baseURL = '${urls/baseUrl}'
        try:
            baseURL = self.processEventFields(data, baseURL, 'baseURL')
        except Exception:
            baseURL = ''

        if (baseURL):
            baseURL = self.getSiteURI(baseURL)

        return baseURL

    def getBaseHost(self, data):
        log.debug('[research] get baseHost')

        baseHost = ''

        baseHost = self.getBaseURL(data)

        return urlparse(baseHost).hostname

    def getEnvironment(self, data):
        log.debug('[research] get environment')

        eventENV = '${dev/getProductionStateString}'
        try:
            eventENV = self.processEventFields(data, eventENV,
                                               'Event ENV (dev)')
        except Exception:
            eventENV = '${evt/prodState}'
            try:
                eventENV = self.processEventFields(data, eventENV,
                                                   'Event ENV (evt)')
            except Exception:
                eventENV = ''

        return eventENV

    def getServiceGroup(self, data, valuePattern):
        log.debug('[research] get service group')

        srvcGRP = '${evt/DeviceGroups}'
        try:
            srvcGRP = self.processEventFields(data, srvcGRP, 'Service')
            srvcGRP = srvcGRP.split('|')
        except Exception:
            srvcGRP = []

        extendGRP = []
        defaultGRP = None

        valuePattern = valuePattern.split(';')
        if (len(valuePattern) > 1):
            defaultGRP = valuePattern[1].strip()
        valuePattern = valuePattern[0].strip()

        if (valuePattern):
            for ix in range(len(srvcGRP)):
                svcm = re.match(valuePattern, srvcGRP[ix], re.IGNORECASE)
                if (svcm):
                    valGRP = svcm.group(2)
                    if (valGRP):
                        valGRP = valGRP.split('/')
                        for ex in range(len(valGRP)):
                            extendGRP.append('\(' + '/'.join(valGRP[:ex + 1]) +
                                             '\)')

        log.debug('[research] service group patterns : %s' % (extendGRP))

        if (extendGRP):
            srvcGRP = '.*(' + '|'.join(extendGRP) + ').*'
        else:
            srvcGRP = ''

        if (defaultGRP):
            srvcGRP += ';' + defaultGRP

        log.debug('[research] service pattern : %s' % (srvcGRP))

        return srvcGRP

    def getLocation(self, data):
        log.debug('[research] get location')

        loc = '${evt/Location}'
        try:
            loc = self.processEventFields(data, loc, 'Location')
        except Exception:
            loc = ''

        for locx in loc.split('/'):
            if (locx):
                return locx

        return loc

    def getSiteURI(self, source):
        outURI = re.findall("((http|https)://[a-zA-Z0-9-\.:]*)", source)
        if (outURI.__class__.__name__ in ['list']):
            if (len(outURI) > 0):
                if (len(outURI[0]) > 0):
                    outURI = outURI[0][0]
        log.debug('[research] zenoss URL : %s' % (outURI))
        outURI = urlparse(source)
        return "%s://%s" % (outURI.scheme, outURI.netloc)

    def processEventFields(self, data, content, name):
        log.debug('[research] process TAL expressions')

        try:
            content = processTalSource(content, **data)
            log.debug('[research] %s : %s' % (name, content))
        except Exception:
            log.debug('[research] unable to process : %s' % (name))
            raise ActionExecutionException(
                '[research] failed to process TAL in %s' % (name))

        if (content == 'None'):
            content = ''

        return content

    def removeEmptyListElements(self, listObj):
        log.debug('[research] remove empty list elements')

        bDirty = True
        for lx in range(len(listObj)):
            try:
                ix = listObj.index('')
                listObj[ix:ix + 1] = []
            except Exception:
                bDirty = False

            try:
                ix = listObj.index()
                listObj[ix:ix + 1] = []
                if (not bDirty):
                    bDirty = True
            except Exception:
                if (not bDirty):
                    bDirty = False

            if (not bDirty):
                break

        return listObj

    def processServiceGroupUsingRoot(self, serviceGroups, rootPattern):
        log.debug('[research] filter service group values')

        return serviceGroups
示例#29
0
 def setupAction(self, dmd):
     self.guidManager = GUIDManager(dmd)
示例#30
0
class JIRAReporter(IActionBase, TargetableAction):
    implements(IAction)

    id = 'JIRAReporter'
    name = 'JIRA Issue Reporter'
    actionContentInfo = IJIRAActionContentInfo 

    shouldExecuteInBatch = False

    def __init__(self):
        log.debug('[research] %s : initialized' % (self.id))
        super(JIRAReporter, self).__init__()

        self.connected = False
        self.jira = None

    def setupAction(self, dmd):
        log.debug('[research] setup : %s' % (self.name))
        self.guidManager = GUIDManager(dmd)
        self.dmd = dmd

    def executeOnTarget(self, notification, signal, target):
        self.setupAction(notification.dmd)

        log.debug('[research] execute : %s on %s' % (self.name, target))

        jiraURL = notification.content['jira_instance']
        jiraUser = notification.content['jira_user']
        jiraPass = notification.content['jira_password']

        issueProject = notification.content['issue_project']
        issueType = notification.content['issue_type']
        issuePriority = notification.content['issue_priority_key']
        customfields = notification.content['customfield_keypairs']
        eventRawData = notification.content['event_rawdata']
        serviceRoot = notification.content['service_group_root']

        summary = ''
        description = ''

        if (signal.clear):
            log.info('[research] event cleared : %s' % (target))
            description = notification.content['clear_issue_description']
        else:
            log.warn('[research] event detected : %s' % (target))
            summary = notification.content['issue_summary']
            description = notification.content['issue_description']

        actor = signal.event.occurrence[0].actor
        device = None
        if (actor.element_uuid):
            device = self.guidManager.getObject(actor.element_uuid)

        component = None
        if (actor.element_sub_uuid):
            component = self.guidManager.getObject(actor.element_sub_uuid)

        environ = {
            'dev': device, 'component': component, 'dmd': notification.dmd
        }

        data = _signalToContextDict(
            signal, self.options.get('zopeurl'),
            notification, self.guidManager
        )

        environ.update(data)

        if (environ.get('evt', None)):
            environ['evt'] = self._escapeEvent(environ['evt'])

        if (environ.get('clearEvt', None)):
            environ['clearEvt'] = self._escapeEvent(environ['clearEvt'])

        environ['user'] = getattr(self.dmd.ZenUsers, target, None)

        issueValues = {
            'summary' : summary,
            'description' : description,
            'eventraw' : eventRawData,
            'customfields' : customfields
        }

        log.debug('[research] base issue values : %s' % (issueValues))

        targetValues = {
            'project' : issueProject,
            'issuetype' : issueType,
            'priority' : issuePriority,
            'serviceroot' : serviceRoot 
        }

        log.debug('[research] base target values : %s' % (targetValues))

        self.connectJIRA(jiraURL, jiraUser, jiraPass)

        if (signal.clear):
            self.clearEventIssue(environ, targetValues, issueValues)
        else:
            self.createEventIssue(environ, targetValues, issueValues)

        log.debug("[research] event update reported : %s" % (jiraURL));

    def getActionableTargets(self, target):
        ids = [target.id]
        if isinstance(target, GroupSettings):
            ids = [x.id for x in target.getMemberUserSettings()]
        return ids

    def _escapeEvent(self, evt):
        """
        Escapes the relavent fields of an event context for event commands.
        """
        if evt.message:
            evt.message = self._wrapInQuotes(evt.message)
        if evt.summary:
            evt.summary = self._wrapInQuotes(evt.summary)
        return evt

    def _wrapInQuotes(self, msg):
        """
        Wraps the message in quotes, escaping any existing quote.

        Before:  How do you pronounce "Zenoss"?
        After:  "How do you pronounce \"Zenoss\"?"
        """
        QUOTE = '"'
        BACKSLASH = '\\'
        return ''.join((QUOTE, msg.replace(QUOTE, BACKSLASH + QUOTE), QUOTE))

    def updateContent(self, content=None, data=None):
        super(JIRAReporter, self).updateContent(content, data)

        updates = dict()
        properties = [
            'jira_instance', 'jira_user', 'jira_password',
            'issue_project', 'issue_type', 'issue_priority_key',
            'issue_summary', 'issue_description', 'clear_issue_summary',
            'customfield_keypairs', 'event_rawdata', 'service_group_root'
        ]

        for k in properties:
            updates[k] = data.get(k)

        content.update(updates)

# jira client methods

    def connectJIRA(self, URL, user, password):
        log.debug('[research] : connecting to %s' % (URL))

        basicauth = (user, password)

        try:
            self.jira = JIRA(
                options = {'server' : URL},
                basic_auth = basicauth
            )
            self.connected = True
            log.debug('[research] : connected to %s' % (URL))
        except JIRAError as jx:
            log.error('[research] jira.error : %s' % (jx))
        except Exception as ex:
            log.debug('[research] exception : %s' % (ex))
        finally:
            log.debug('[research] connection info (%s)' % (URL))

    def setIssueValues(self, data, targetValues, issueValues):
        log.debug('[research] process issue values')

        if ('project' in targetValues):
            issueValues['project'] = {
                'key' : targetValues['project']
            }
        if ('issuetype' in targetValues):
            issueValues['issuetype'] = {
                'name' : targetValues['issuetype']
            }

        issueValues['summary'] = self.processEventFields(
            data, issueValues['summary'], 'Summary'
        )
        issueValues['description'] = self.processEventFields(
            data, issueValues['description'], 'Description'
        )

        log.debug('[research] issue values : %s' % (issueValues))

        return issueValues

    def setCustomFieldValues(self, data, targetValues, issueValues):
        log.debug('[research] process customfield values')

        customfields = None
        if ('customfields' in issueValues):
            customfields = issueValues['customfields']
            del issueValues['customfields']

        if (customfields):
            customfields = json.loads(customfields)
        else:
            customfields = {}

        if ('priority' in targetValues):
            customfields['Priority'] = targetValues['priority']

        if ('serviceroot' in targetValues):
            customfields['Service'] = targetValues['serviceroot'] 

        if ('eventraw' in issueValues):
            customfields['Zenoss EventRAW'] = self.processEventFields(
                data, issueValues['eventraw'], 'Event Raw Data'
            )
            del issueValues['eventraw']

        customfields = self.setZenossFields(data, customfields)

        log.debug('[research] customfield values : %s' % (customfields))

        createmeta = self.jira.createmeta(
            projectKeys = targetValues['project'],
            issuetypeNames = targetValues['issuetype'],
            expand = 'projects.issuetypes.fields'
        )

        issuetype = None
        fields = None
        if (createmeta):
            if ('projects' in createmeta):
                if ('issuetypes' in createmeta['projects'][0]):
                    issuetype = createmeta['projects'][0]['issuetypes'][0]
                    log.debug('[research] createmeta issuetype : available')
            if (issuetype):
                if ('fields' in issuetype):
                    fields = issuetype['fields']
                    log.debug('[research] createmeta fields : available')
        else:
            log.debug('[research] createmeta : NOT AVAILABLE')

        if (fields):
            for fKey, fAttr in fields.iteritems():
                if ('name' in fAttr):
                    if (fAttr['name'] in customfields):
                        log.debug('[research] customfield found')
                        fieldValue = customfields[fAttr['name']]
                        if ('allowedValues' in fAttr):
                            log.debug('[research] has customfield options')
                            fieldValue = self.getCustomFieldOption(
                                fAttr['allowedValues'], fieldValue
                            )
                        if (fieldValue):
                            log.debug('[research] cf (%s) set to %s' % (
                                fAttr['name'], fieldValue)
                            )
                            try:
                                if (fAttr['schema']['type'] in ['array']):
                                    fieldValue = [fieldValue]
                            except:
                                pass
                            issueValues[fKey] = fieldValue

        log.debug('[research] issue customfields : %s' % (issueValues))

        return issueValues

    def setZenossFields(self, data, customfields):
        log.debug('[research] process customfield values')

        if (not customfields):
            customfields = {}

        zEventID = self.getEventID(data)
        if (zEventID):
            customfields['Zenoss ID'] = zEventID

        zDeviceID = self.getDeviceID(data)
        if (zDeviceID):
            customfields['Zenoss DevID'] = zDeviceID

        zBaseURL = self.getBaseURL(data) 
        if (zBaseURL):
            customfields['Zenoss Instance'] = zBaseURL

        zEnv = self.getEnvironment(data)
        if (zEnv):
            customfields['Environment'] = zEnv 

        if ('Service' in customfields):
            zSrvc = self.getServiceGroup(data, customfields['Service'])
            if (zSrvc):
                customfields['Service'] = zSrvc

        zLoc = self.getLocation(data)
        if (zLoc):
            customfields['DataCenter'] = zLoc

        log.debug('[research] Zenoss customfields : %s' % (customfields))

        return customfields 

    def createEventIssue(self, data, targetValues, issueValues):
        log.debug('[research] create event issue')

        project = targetValues['project']

        eventID = self.getEventID(data)
        baseHost = self.getBaseHost(data) 
        deviceID = self.getDeviceID(data)

        hasIssues = self.hasEventIssues(project, baseHost, eventID)

        if (hasIssues):
            log.warn('[research] issue exists for EventID %s' % (eventID))
        else:
            issueValues = self.setIssueValues(
                data, targetValues, issueValues
            )
            issueValues = self.setCustomFieldValues(
                data, targetValues, issueValues
            )

            newissue = self.jira.create_issue(fields = issueValues)
            log.info('[research] issue created : %s' % (newissue.key))

    def clearEventIssue(self, data, targetValues, issueValues):
        log.debug('[research] clear event issue')

        project = targetValues['project']

        eventID = self.getEventID(data)
        baseHost = self.getBaseHost(data) 

        issues = self.getEventIssues(project, baseHost, eventID)

        if (not issues):
            log.warn('[research] no issue mapped to clear : %s' % (eventID))
            return

        issueValues = self.setIssueValues(
            data, targetValues, issueValues
        )

        description = issueValues['description']

        eventCLR = self.getEventClearDate(data)

        for issue in issues:
            zenossCLR = self.getCustomFieldID(issue, 'Zenoss EventCLR')
            issuekey = issue.key
            if (zenossCLR):
                issue.update(fields = {zenossCLR : eventCLR})
                log.info('[research] EventCLR updated : %s' % (issuekey))
            if (description):
                self.jira.add_comment(issue.key, description)
                log.info('[research] EventCLR commented : %s' % (issuekey))

    def hasEventIssues(self, project, eventINS, eventID):
        log.debug('[research] has event issues')

        issues = self.getEventIssues(project, eventINS, eventID)

        log.debug('[research] has event issues : %s' % (len(issues) > 0))

        return (len(issues) > 0)

    def getEventIssues(self, project, eventINS, eventID):
        log.debug('[research] get event issues')

        issues = []

        if (eventID):
            issueFilter = '(project = "%s")'
            issueFilter += ' and ("Zenoss Instance" ~ "%s")'
            issueFilter += ' and ("Zenoss ID" ~ "%s")'
            issueFilter = issueFilter % (project, eventINS, eventID)
            log.debug('[research] event issue filter : %s' % (issueFilter))

            try:
                issues = self.jira.search_issues(issueFilter)
                log.debug('[research] event issues : %s' % (len(issues)))
            except JIRAError as jx:
                log.error('[research] jira.error : %s' % (jx))
            except Exception as ex:
                log.error('[research] exception : %s' % (ex))

        return issues

    def getCustomFieldOption(
        self, fieldOptions, value, defaultValue = '',
        exactMatch = False, firstMatch = False):
        log.debug('[research] get customfield options')

        if (not value):
            return None

        if (not fieldOptions):
            return None 

        bDefault = False
        matchValue = None

        if (value.__class__.__name__ in ('str', 'unicode')):
            value = value.split(';')
            if (len(value) > 1):
                defaultValue = value[1].strip()
                log.debug('[research] option default : %s' % (defaultValue))
            value = value[0].strip()

        if (not value):
            log.debug('[research] invalid option value : %s' % (value))

        for av in fieldOptions:
            if ('value' in av):
                valueName = av['value']
            elif ('name' in av):
                valueName = av['name']
            else:
                continue

            if (value):
                if (value.__class__.__name__ in ('str', 'unicode')):
                    if (exactMatch):
                        value = '^%s$' % (value)
                    if (re.match(value, valueName, re.IGNORECASE)):
                        if ('id' in av):
                            matchValue = {'id' : av['id']}
                        else:
                            matchValue = valueName
                        if (firstMatch):
                            break

            if (not defaultValue):
                continue

            if (defaultValue.__class__.__name__ in ('str', 'unicode')):
                if (re.match(defaultValue, valueName, re.IGNORECASE)):
                    bDefault = True
                    if ('id' in av):
                        defaultValue = {'id' : av['id']}
                    else:
                        defaultValue = valueName
                    if (not value):
                        break

        if (not matchValue):
            if (bDefault):
                log.debug('[research] default option : %s' % (defaultValue))
                matchValue = defaultValue

        return matchValue

    def getCustomFieldID(self, issue, fieldName):
        log.debug('[research] get issue customfield ID')

        fieldID = ''

        for field in self.jira.fields():
            if (field['name'].lower() == fieldName.lower()):
                log.debug('[research] customfield matched %s' % (fieldName))
                fieldID = field['id']
                break
        
        return fieldID

    def getEventID(self, data):
        log.debug('[research] get eventID')

        eventID = '${evt/evid}'
        try:
            eventID = self.processEventFields(data, eventID, 'eventID')
        except Exception:
            eventID = ''

        return eventID 

    def getEventClearDate(self, data):
        log.debug('[research] get event clear date')

        eventCLR = '${evt/stateChange}'
        try:
            eventCLR = self.processEventFields(data, eventCLR, 'clear date')
        except Exception:
            eventCLR = ''

        if (eventCLR):
            try:
                eventCLR = datatime.strptime(
                    eventCLR, '%Y-%m-%d %H:%M:%S'
                ).isoformat()[:19] + '.000+0000'
            except:
                try:
                    eventCLR = datatime.strptime(
                        eventCLR, '%Y-%m-%d %H:%M:%S.%f'
                    ).isoformat()[:19] + '.000+0000'
                except:
                    eventCLR = ''

        if (not eventCLR):
            eventCLR = datetime.now().isoformat()[:19] + '.000+0000'

        return eventCLR

    def getDeviceID(self, data):
        log.debug('[research] get deviceID')

        deviceID = '${evt/device}'
        try:
            deviceID = self.processEventFields(data, deviceID, 'deviceID')
        except Exception:
            deviceID = ''

        return deviceID 

    def getBaseURL(self, data):
        log.debug('[research] get baseURL')

        baseURL = '${urls/baseUrl}'
        try:
            baseURL = self.processEventFields(data, baseURL, 'baseURL')
        except Exception:
            baseURL = ''

        if (baseURL):
            baseURL = self.getSiteURI(baseURL)

        return baseURL

    def getBaseHost(self, data):
        log.debug('[research] get baseHost')

        baseHost = ''

        baseHost = self.getBaseURL(data)

        return urlparse(baseHost).hostname

    def getEnvironment(self, data):
        log.debug('[research] get environment')

        eventENV = '${dev/getProductionStateString}'
        try:
            eventENV = self.processEventFields(
                data, eventENV, 'Event ENV (dev)'
            )
        except Exception:
            eventENV = '${evt/prodState}'
            try:
                eventENV = self.processEventFields(
                    data, eventENV, 'Event ENV (evt)'
                )
            except Exception:
                eventENV = ''

        return eventENV

    def getServiceGroup(self, data, valuePattern):
        log.debug('[research] get service group')

        srvcGRP = '${evt/DeviceGroups}'
        try:
            srvcGRP = self.processEventFields(data, srvcGRP, 'Service')
            srvcGRP = srvcGRP.split('|')
        except Exception:
            srvcGRP = []

        extendGRP = []
        defaultGRP = None

        valuePattern = valuePattern.split(';')
        if (len(valuePattern) > 1):
            defaultGRP = valuePattern[1].strip()
        valuePattern = valuePattern[0].strip()

        if (valuePattern):
            for ix in range(len(srvcGRP)):
                svcm = re.match(valuePattern, srvcGRP[ix], re.IGNORECASE)
                if (svcm):
                    valGRP = svcm.group(2)
                    if (valGRP):
                        valGRP = valGRP.split('/')
                        for ex in range(len(valGRP)):
                            extendGRP.append(
                                '\(' + '/'.join(valGRP[:ex + 1]) + '\)'
                            )

        log.debug('[research] service group patterns : %s' % (extendGRP))

        if (extendGRP):
            srvcGRP = '.*(' + '|'.join(extendGRP) + ').*'
        else:
            srvcGRP = ''

        if (defaultGRP):
            srvcGRP += ';' + defaultGRP

        log.debug('[research] service pattern : %s' % (srvcGRP))

        return srvcGRP

    def getLocation(self, data):
        log.debug('[research] get location')

        loc = '${evt/Location}'
        try:
            loc = self.processEventFields(data, loc, 'Location')
        except Exception:
            loc = ''

        for locx in loc.split('/'):
            if (locx):
                return locx

        return loc

    def getSiteURI(self, source):
        outURI = re.findall("((http|https)://[a-zA-Z0-9-\.:]*)", source)
        if (outURI.__class__.__name__ in ['list']):
            if (len(outURI) > 0):
                if (len(outURI[0]) > 0):
                    outURI = outURI[0][0]
        log.debug('[research] zenoss URL : %s' % (outURI)) 
        outURI = urlparse(source)
        return "%s://%s" % (outURI.scheme, outURI.netloc)

    def processEventFields(self, data, content, name):
        log.debug('[research] process TAL expressions')

        try:
            content = processTalSource(content, **data)
            log.debug('[research] %s : %s' % (name, content))
        except Exception:
            log.debug('[research] unable to process : %s' % (name))
            raise ActionExecutionException(
                '[research] failed to process TAL in %s' % (name))

        if (content == 'None'):
            content = ''

        return content

    def removeEmptyListElements(self, listObj):
        log.debug('[research] remove empty list elements')

        bDirty = True
        for lx in range(len(listObj)): 
            try:
                ix = listObj.index('')
                listObj[ix:ix + 1] = []
            except Exception:
                bDirty = False

            try:
                ix = listObj.index()
                listObj[ix:ix + 1] = []
                if (not bDirty):
                    bDirty = True
            except Exception:
                if (not bDirty):
                    bDirty = False

            if (not bDirty):
                break

        return listObj

    def processServiceGroupUsingRoot(self, serviceGroups, rootPattern):
        log.debug('[research] filter service group values')

        return serviceGroups
示例#31
0
class CommandAction(IActionBase, TargetableAction):
    implements(IAction)

    id = 'command'
    name = 'Command'
    actionContentInfo = ICommandActionContentInfo

    shouldExecuteInBatch = False

    def configure(self, options):
        super(CommandAction, self).configure(options)
        self.processQueue = ProcessQueue(options.get('maxCommands', 10))
        self.processQueue.start()

    def setupAction(self, dmd):
        self.guidManager = GUIDManager(dmd)
        self.dmd = dmd
        self.zenossHostname = dmd.zenossHostname

    def execute(self, notification, signal):
        # check to see if we have any targets
        if notification.recipients:
            return super(CommandAction, self).execute(notification, signal)
        else:
            self._execute(notification, signal)

    def executeOnTarget(self, notification, signal, target):
        log.debug('Executing command action: %s on %s', self.name, target)
        environ = {}
        environ['user'] = getattr(self.dmd.ZenUsers, target, None)
        self._execute(notification, signal, environ)

    def _execute(self, notification, signal, extra_env= {}):
        self.setupAction(notification.dmd)
        log.debug('Executing command action: %s', self.name)

        if signal.clear:
            command = notification.content['clear_body_format']
        else:
            command = notification.content['body_format']

        log.debug('Executing this command: %s', command)

        actor = signal.event.occurrence[0].actor
        device = None
        if actor.element_uuid:
            device = self.guidManager.getObject(actor.element_uuid)

        component = None
        if actor.element_sub_uuid:
            component = self.guidManager.getObject(actor.element_sub_uuid)

        user_env_format = notification.content.get('user_env_format', '') or ''
        env = os.environ.copy()
        user_env = dict( envvar.split('=', 1) for envvar in user_env_format.split(';') if '=' in envvar)
        env.update(user_env)
        environ = {'dev': device, 'component': component, 'dmd': notification.dmd, 'env': env}
        data = self._signalToContextDict(signal, notification)
        environ.update(data)

        if environ.get('evt', None):
            environ['evt'] = self._escapeEvent(environ['evt'])
        if environ.get('clearEvt', None):
            environ['clearEvt'] = self._escapeEvent(environ['clearEvt'])
        environ.update(extra_env)

        # Get the proper command
        command = processTalSource(command, **environ)

        log.debug('Executing this compiled command: "%s"' % command)
        _protocol = EventCommandProtocol(command, notification)

        log.debug('Queueing up command action process.')
        self.processQueue.queueProcess(
            '/bin/sh',
                ('/bin/sh', '-c', command),
            env=environ['env'],
            processProtocol=_protocol,
            timeout=int(notification.content['action_timeout']),
            timeout_callback=_protocol.timedOut
        )

    def getActionableTargets(self, target):
        ids = [target.id]
        if isinstance(target, GroupSettings):
            ids = [x.id for x in target.getMemberUserSettings()]
        return ids

    def _escapeEvent(self, evt):
        """
        Escapes the relavent fields of an event context for event commands.
        """
        if evt.message:
            evt.message = self._wrapInQuotes(evt.message)
        if evt.summary:
            evt.summary = self._wrapInQuotes(evt.summary)
        return evt

    def _wrapInQuotes(self, msg):
        """
        Wraps the message in quotes, escaping any existing quote.

        Before:  How do you pronounce "Zenoss"?
        After:  "How do you pronounce \"Zenoss\"?"
        """
        QUOTE = '"'
        BACKSLASH = '\\'
        return ''.join((QUOTE, msg.replace(QUOTE, BACKSLASH + QUOTE), QUOTE))
示例#32
0
 def setupAction(self, dmd):
     log.debug('[research] setup : %s' % (self.name))
     self.guidManager = GUIDManager(dmd)
     self.dmd = dmd
示例#33
0
 def setupAction(self, dmd):
     self.guidManager = GUIDManager(dmd)
     self.zenossHostname = dmd.zenossHostname
示例#34
0
class PagerDutyEventsAPIAction(IActionBase):
    """
    Derived class to contact PagerDuty's events API when a notification is
    triggered.
    """
    implements(IAction)

    id = 'pagerduty'
    name = 'PagerDuty'
    actionContentInfo = IPagerDutyEventsAPIActionContentInfo

    shouldExecuteInBatch = False

    def __init__(self):
        super(PagerDutyEventsAPIAction, self).__init__()

    def setupAction(self, dmd):
        self.guidManager = GUIDManager(dmd)
        self.dmd = dmd

    def execute(self, notification, signal):
        """
        Sets up the execution environment and POSTs to PagerDuty's Event API.
        """
        log.debug('Executing Pagerduty Events API action: %s', self.name)

        self.setupAction(notification.dmd)

        if signal.clear:
            eventType = EventType.RESOLVE
        elif signal.event.status == STATUS_ACKNOWLEDGED:
            eventType = EventType.ACKNOWLEDGE
        else:
            eventType = EventType.TRIGGER

        # Set up the TALES environment
        environ = {'dmd': notification.dmd, 'env': None}

        actor = signal.event.occurrence[0].actor

        device = None
        if actor.element_uuid:
            device = self.guidManager.getObject(actor.element_uuid)
        environ.update({'dev': device})

        component = None
        if actor.element_sub_uuid:
            component = self.guidManager.getObject(actor.element_sub_uuid)
        environ.update({'component': component})

        data = _signalToContextDict(signal, self.options.get('zopeurl'),
                                    notification, self.guidManager)
        environ.update(data)

        try:
            detailsList = json.loads(notification.content['details'])
        except ValueError:
            raise ActionExecutionException('Invalid JSON string in details')

        details = dict()
        for kv in detailsList:
            details[kv['key']] = kv['value']

        details['zenoss'] = {
            'version': ZENOSS_VERSION,
            'zenpack_version': zenpack_version(),
        }

        payload = {
            'severity': '${evt/severity}',
            'class': '${evt/eventClass}',
            'custom_details': details,
        }
        body = {
            'event_action': eventType,
            'dedup_key': data['evt'].evid,
            'payload': payload
        }

        for prop in REQUIRED_PROPERTIES:
            if prop in notification.content:
                payload.update({prop: notification.content[prop]})
            else:
                raise ActionExecutionException(
                    "Required property '%s' not found" % prop)

        if NotificationProperties.SERVICE_KEY in notification.content:
            body.update({'routing_key': notification.content['serviceKey']})
        else:
            raise ActionExecutionException(
                "API Key for PagerDuty service was not found. "
                "Did you configure a notification correctly?")

        self._performRequest(body, environ)

    def _performRequest(self, body, environ):
        """
        Actually performs the request to PagerDuty's Event API.

        Raises:
            ActionExecutionException: Some error occurred while contacting
            PagerDuty's Event API (e.g., API down, invalid service key).
        """

        bodyWithProcessedTalesExpressions = self._processTalExpressions(
            body, environ)
        bodyWithProcessedTalesExpressions['payload'][
            'severity'] = EVENT_MAPPING[
                bodyWithProcessedTalesExpressions['payload']['severity']]
        requestBody = json.dumps(bodyWithProcessedTalesExpressions)

        headers = {'Content-Type': 'application/json'}
        req = urllib2.Request(EVENT_API_URI, requestBody, headers)
        try:
            # bypass default handler SVC-1819
            opener = urllib2.build_opener()
            f = opener.open(req, None, API_TIMEOUT_SECONDS)
        except urllib2.URLError as e:
            if hasattr(e, 'reason'):
                msg = 'Failed to contact the PagerDuty server: %s' % (e.reason)
                raise ActionExecutionException(msg)
            elif hasattr(e, 'code'):
                msg = 'The PagerDuty server couldn\'t fulfill the request: HTTP %d (%s)' % (
                    e.code, e.msg)
                raise ActionExecutionException(msg)
            else:
                raise ActionExecutionException('Unknown URLError occurred')

        response = f.read()
        log.debug('PagerDuty response: %s', response)
        f.close()

    def _processTalExpression(self, value, environ):
        if type(value) is str or type(value) is unicode:
            if '${' not in value:
                return value
            try:
                return processTalSource(value, **environ)
            except Exception:
                raise ActionExecutionException(
                    'Unable to perform TALES evaluation on "%s" -- is there an unescaped $?'
                    % value)
        else:
            return value

    def _processTalExpressions(self, data, environ):
        for payloadKey in data['payload']:
            if not payloadKey.startswith('custom_details'):
                data['payload'][payloadKey] = self._processTalExpression(
                    data['payload'][payloadKey], environ)
        for detailKey in data['payload']['custom_details']:
            data['payload']['custom_details'][
                detailKey] = self._processTalExpression(
                    data['payload']['custom_details'][detailKey], environ)
        return data

    def updateContent(self, content=None, data=None):
        updates = dict()

        for k in NotificationProperties.ALL:
            updates[k] = data.get(k)

        content.update(updates)