Beispiel #1
0
    def handle(self):
        input_command = self.request.input.command or ''

        if not input_command:
            msg = 'No command sent'
            raise ZatoException(self.cid, msg)

        try:
            parse_result = redis_grammar.parseString(input_command)

            options = {}
            command = parse_result.command
            parameters = parse_result.parameters if parse_result.parameters else []

            if command == 'CONFIG':
                options['parse'] = parameters[0]
            elif command == 'OBJECT':
                options['infotype'] = parameters[0]

            response = self.server.kvdb.conn.execute_command(
                command, *parameters, **options) or ''

            if response and command in ('KEYS', 'HKEYS', 'HVALS'):
                response = unicode(response).encode('utf-8')
            elif command in ('HLEN', 'LLEN', 'LRANGE', 'SMEMBERS', 'HGETALL'):
                response = str(response)

            self.response.payload.result = response or '(None)'

        except Exception, e:
            msg = 'Command parsing error, command:[{}], e:[{}]'.format(
                input_command, format_exc(e))
            self.logger.error(msg)
            raise ZatoException(self.cid, msg)
Beispiel #2
0
    def invoke_by_impl_name(self, impl_name, payload='', channel=CHANNEL.INVOKE, data_format=DATA_FORMAT.DICT,
        transport=None, serialize=False, as_bunch=False, timeout=None, raise_timeout=True, **kwargs):
        """ Invokes a service synchronously by its implementation name (full dotted Python name).
        """
        if self.component_enabled_target_matcher:

            orig_impl_name = impl_name
            impl_name, target = self.extract_target(impl_name)

            # It's possible we are being invoked through self.invoke or self.invoke_by_id
            target = target or kwargs.get('target', '')

            if not self._worker_store.target_matcher.is_allowed(target):
                raise ZatoException(self.cid, 'Invocation target `{}` not allowed ({})'.format(target, orig_impl_name))

        if self.component_enabled_invoke_matcher:
            if not self._worker_store.invoke_matcher.is_allowed(impl_name):
                raise ZatoException(self.cid, 'Service `{}` (impl_name) cannot be invoked'.format(impl_name))

        if self.impl_name == impl_name:
            msg = 'A service cannot invoke itself, name:[{}]'.format(self.name)
            self.logger.error(msg)
            raise ZatoException(self.cid, msg)

        service, is_active = self.server.service_store.new_instance(impl_name)
        if not is_active:
            raise Inactive(service.get_name())

        set_response_func = kwargs.pop('set_response_func', service.set_response_data)

        invoke_args = (set_response_func, service, payload, channel, data_format, transport, self.server,
            self.broker_client, self._worker_store, kwargs.pop('cid', self.cid), self.request.simple_io_config)

        kwargs.update({'serialize':serialize, 'as_bunch':as_bunch})

        try:
            if timeout:
                try:
                    g = spawn(self.update_handle, *invoke_args, **kwargs)
                    return g.get(block=True, timeout=timeout)
                except Timeout:
                    g.kill()
                    logger.warn('Service `%s` timed out (%s)', service.name, self.cid)
                    if raise_timeout:
                        raise
            else:
                out = self.update_handle(*invoke_args, **kwargs)
                if kwargs.get('skip_response_elem') and hasattr(out, 'keys'):
                    keys = list(iterkeys(out))
                    response_elem = keys[0]
                    return out[response_elem]
                else:
                    return out
        except Exception:
            logger.warn('Could not invoke `%s`, e:`%s`', service.name, format_exc())
            raise
Beispiel #3
0
    def get(self, name):
        params = {
            'hostname': None,
            'database': None,
            'port': None,
            'login': None,
            'password': None
        }
        missing = []
        for param in params:
            key_prefix = OE_PARAMS[param]
            key = ':'.join((key_prefix, name))
            value = self.kvdb.conn.get(key)

            if not value:
                missing.append(key)
            else:
                value = int(value) if param == 'port' else value
                params[param] = value

        if missing:
            msg = 'One or more config key is missing or has no value: {}'.format(
                missing)
            self.logger.error(msg)
            raise ZatoException(self.cid, msg)

        client = Client(**params)
        client.connect()

        return client
Beispiel #4
0
    def get(self, name):
        params = {
            'smtp_port': 25,
            'smtp_debug': False,
            'smtp_encryption': None,
        }
        missing = []
        for param in SMTP_PARAMS:
            key_prefix = SMTP_PARAMS[param]
            key = ':'.join((key_prefix, name))
            value = self.kvdb.conn.get(key)

            if not value and not params.get(param, False):
                missing.append(key)
            else:
                if param == 'smtp_port':
                    value = int(value)
                elif param == 'smtp_debug':
                    if value == 'True':
                        value = True
                    else:
                        value = False
                params[param] = value

        if missing:
            msg = 'One or more config key is missing or has no value: {}'.format(
                missing)
            self.logger.error(msg)
            raise ZatoException(self.cid, msg)

        client = SMTPClient(**params)
        client.connect()

        return client
Beispiel #5
0
    def handle(self):
        with closing(self.odb.session()) as session:
            try:
                server = session.query(Server).\
                    filter(Server.id==self.request.input.id).\
                    one()

                # Sanity check
                if server.id == self.server.id:
                    msg = 'A server cannot delete itself, id:[{}], name:[{}]'.format(
                        server.id, server.name)
                    self.logger.error(msg)
                    raise ZatoException(self.cid, msg)

                # This will cascade and delete every related object
                session.delete(server)
                session.commit()

            except Exception, e:
                session.rollback()
                msg = 'Could not delete the server, e:[{e}]'.format(
                    e=format_exc(e))
                self.logger.error(msg)

                raise
Beispiel #6
0
    def _getvalue(self, name, item, is_sa_namedtuple, is_required,
                  leave_as_is):
        """ Returns an element's value if any has been provided while taking
        into account the differences between dictionaries and other formats
        as well as the type conversions.
        """
        lookup_name = name.name if isinstance(name, ForceType) else name

        if is_sa_namedtuple or self._is_sqlalchemy(item):
            elem_value = getattr(item, lookup_name, '')
        else:
            elem_value = item.get(lookup_name, '')

        if isinstance(elem_value, basestring) and not elem_value:
            msg = self._missing_value_log_msg(name, item, is_sa_namedtuple,
                                              is_required)
            if is_required:
                self.zato_logger.debug(msg)
                raise ZatoException(self.zato_cid, msg)
            else:
                if self.zato_logger.isEnabledFor(TRACE1):
                    self.zato_logger.log(TRACE1, msg)

        if leave_as_is:
            return elem_value
        else:
            return self.convert(name, lookup_name, elem_value, True,
                                self.zato_is_xml, self.bool_parameter_prefixes,
                                self.int_parameters,
                                self.int_parameter_suffixes, None,
                                self.zato_data_format, True)
Beispiel #7
0
    def invoke_by_impl_name(self,
                            impl_name,
                            payload='',
                            channel=CHANNEL.INVOKE,
                            data_format=None,
                            transport=None,
                            serialize=False,
                            as_bunch=False):
        """ Invokes a service synchronously by its implementation name (full dotted Python name).
        """

        if self.impl_name == impl_name:
            msg = 'A service cannot invoke itself, name:[{}]'.format(self.name)
            self.logger.error(msg)
            raise ZatoException(self.cid, msg)

        service = self.server.service_store.new_instance(impl_name)
        return self.update_handle(self.set_response_data,
                                  service,
                                  payload,
                                  channel,
                                  data_format,
                                  transport,
                                  self.server,
                                  self.broker_client,
                                  self.worker_store,
                                  self.cid,
                                  self.request.simple_io_config,
                                  serialize=serialize,
                                  as_bunch=as_bunch)
Beispiel #8
0
    def handle(self):
        with closing(self.odb.session()) as session:
            try:
                service = session.query(Service).\
                    filter(Service.id==self.request.input.id).\
                    one()

                internal_del = is_boolean(self.server.fs_server_config.misc.
                                          internal_services_may_be_deleted)

                if service.is_internal and not internal_del:
                    msg = "Can't delete service:[{}], it's an internal one and internal_services_may_be_deleted is not True".format(
                        service.name)
                    raise ZatoException(self.cid, msg)

                # This will also cascade to delete the related DeployedService objects
                session.delete(service)
                session.commit()

                msg = {
                    'action': SERVICE.DELETE.value,
                    'id': self.request.input.id,
                    'impl_name': service.impl_name,
                    'is_internal': service.is_internal
                }
                self.broker_client.publish(msg)

            except Exception:
                session.rollback()
                msg = 'Service could not be deleted, e:`{}`'.format(
                    format_exc())
                self.logger.error(msg)

                raise
Beispiel #9
0
def payload_from_request(cid, request, data_format, transport):
    """ Converts a raw request to a payload suitable for usage with SimpleIO.
    """
    if request is not None:
        if data_format == DATA_FORMAT.XML:
            if transport == 'soap':
                if isinstance(request, objectify.ObjectifiedElement):
                    soap = request
                else:
                    soap = objectify.fromstring(request)
                body = soap_body_xpath(soap)
                if not body:
                    raise ZatoException(
                        cid, 'Client did not send the [{}] element'.format(
                            soap_body_path))
                payload = get_body_payload(body)
            else:
                if isinstance(request, objectify.ObjectifiedElement):
                    payload = request
                else:
                    payload = objectify.fromstring(request)
        elif data_format in (DATA_FORMAT.DICT, DATA_FORMAT.JSON):
            if not request:
                return ''
            if isinstance(request,
                          basestring) and data_format == DATA_FORMAT.JSON:
                payload = loads(request)
            else:
                payload = request
        else:
            payload = request
    else:
        payload = request

    return payload
Beispiel #10
0
 def _validate_tls(self, input, sec_info):
     if sec_info['sec_type'] == SEC_DEF_TYPE.TLS_KEY_CERT:
         if not input.get('sec_tls_ca_cert_id'):
             raise ZatoException(
                 self.cid,
                 'TLS CA certs is a required field if TLS keys/certs are used'
             )
Beispiel #11
0
    def _get_slice_period_type(self, start, stop, orig_start, orig_stop):
        """ Returns information regarding whether a given period should be sliced
        by minutes, hours, days, months and/or years.
        """
        start = Date(start)
        stop = Date(stop)

        if start > stop:
            msg = 'start:[{}] must not be greater than stop:[{}]'.format(orig_start, orig_stop)
            raise ZatoException(self.cid, msg)

        delta = stop - start

        by_mins = False
        by_hours_mins = False
        by_days_hours_mins = False
        by_months_days_hours_mins = False

        by_mins = delta.total_minutes <= 60
        by_hours_mins = delta.total_minutes > 60
        by_days_hours_mins = delta.total_days > 1
        by_months_days_hours_mins = delta.total_months > 1

        if any((by_days_hours_mins, by_months_days_hours_mins)):
            by_hours_mins = False

        if by_months_days_hours_mins:
            by_days_hours_mins = False

        return delta, {
            'by_mins': by_mins,
            'by_hours_mins': by_hours_mins,
            'by_days_hours_mins': by_days_hours_mins,
            'by_months_days_hours_mins': by_months_days_hours_mins,
        }
Beispiel #12
0
    def init(self, is_sio, cid, sio, data_format, transport, wsgi_environ):
        """ Initializes the object with an invocation-specific data.
        """

        if is_sio:
            self.is_xml = data_format == SIMPLE_IO.FORMAT.XML
            self.data_format = data_format
            self.transport = transport
            self._wsgi_environ = wsgi_environ

            path_prefix = getattr(sio, 'request_elem', 'request')
            required_list = getattr(sio, 'input_required', [])
            optional_list = getattr(sio, 'input_optional', [])
            default_value = getattr(sio, 'default_value', NO_DEFAULT_VALUE)
            use_text = getattr(sio, 'use_text', True)
            use_channel_params_only = getattr(sio, 'use_channel_params_only',
                                              False)

            if self.simple_io_config:
                self.has_simple_io_config = True
                self.bool_parameter_prefixes = self.simple_io_config.get(
                    'bool_parameter_prefixes', [])
                self.int_parameters = self.simple_io_config.get(
                    'int_parameters', [])
                self.int_parameter_suffixes = self.simple_io_config.get(
                    'int_parameter_suffixes', [])
            else:
                self.payload = self.raw_request

            required_params = {}

            if required_list:

                # Needs to check for this exact default value to prevent a FutureWarning in 'if not self.payload'
                if self.payload == '' and not self.channel_params:
                    raise ZatoException(cid, 'Missing input')

                required_params.update(
                    self.get_params(required_list, use_channel_params_only,
                                    path_prefix, default_value, use_text))

            if optional_list:
                optional_params = self.get_params(optional_list,
                                                  use_channel_params_only,
                                                  path_prefix, default_value,
                                                  use_text, False)
            else:
                optional_params = {}

            self.input.update(required_params)
            self.input.update(optional_params)

            for param, value in self.channel_params.iteritems():
                if param not in self.input:
                    self.input[param] = value

        # We merge channel params in if requested even if it's not SIO
        else:
            if self.merge_channel_params:
                self.input.update(self.channel_params)
Beispiel #13
0
    def _invoke(self, name=None, payload='', headers=None, channel='invoke', data_format='json',
                transport=None, is_async=False, expiration=BROKER.DEFAULT_EXPIRATION, id=None,
                to_json=True, output_repeated=ZATO_NOT_GIVEN, pid=None, all_pids=False, timeout=None):

        if not(name or id):
            raise ZatoException(msg='Either name or id must be provided')

        if name and output_repeated == ZATO_NOT_GIVEN:
            output_repeated = name.lower().endswith('list')

        if to_json:
            payload = dumps(payload, default=default_json_handler)

        id_, value = ('name', name) if name else ('id', id)

        request = {
            id_: value,
            'payload': b64encode(payload.encode('utf8') if PY3 else payload),
            'channel': channel,
            'data_format': data_format,
            'transport': transport,
            'is_async': is_async,
            'expiration':expiration,
            'pid':pid,
            'all_pids': all_pids,
            'timeout': timeout,
        }

        return super(AnyServiceInvoker, self).invoke(dumps(request, default=default_json_handler),
            ServiceInvokeResponse, is_async, headers, output_repeated)
Beispiel #14
0
    def connect(self):

        if self.smtp_encryption == 'ssl':
            if not 'SMTP_SSL' in smtplib.__all__:
                raise ZatoException(
                    self.cid,
                    "SMTP-over-SSL mode unavailable, Your Zato Server does not support SMTP-over-SSL. You could use STARTTLS instead. If SSL is needed, an upgrade to Python 2.6 on the server-side should do the trick."
                )
            self.conn = smtplib.SMTP_SSL(self.smtp_host, self.smtp_port)
        else:
            self.conn = smtplib.SMTP(self.smtp_host, self.smtp_port)
        self.conn.set_debuglevel(self.smtp_debug)
        if self.smtp_encryption == 'starttls':
            # starttls() will perform ehlo() if needed first
            # and will discard the previous list of services
            # after successfully performing STARTTLS command,
            # (as per RFC 3207) so for example any AUTH
            # capability that appears only on encrypted channels
            # will be correctly detected for next step
            self.conn.starttls()

        if self.smtp_user:
            # Attempt authentication - will raise if AUTH service not supported
            # The user/password must be converted to bytestrings in order to be usable for
            # certain hashing schemes, like HMAC.
            # See also bug #597143 and python issue #5285
            self.conn.login(self.smtp_user, self.smtp_pass)
        return self.conn
Beispiel #15
0
    def handle(self):
        payload = self.request.input.get('payload')
        if payload:
            payload = payload_from_request(self.cid, payload.decode('base64'),
                                           self.request.input.data_format,
                                           self.request.input.transport)

        id = self.request.input.get('id')
        name = self.request.input.get('name')
        pid = self.request.input.get('pid')

        channel = self.request.input.get('channel')
        data_format = self.request.input.get('data_format')
        transport = self.request.input.get('transport')
        expiration = self.request.input.get(
            'expiration') or BROKER.DEFAULT_EXPIRATION

        if name and id:
            raise ZatoException(
                'Cannot accept both id:`{}` and name:`{}`'.format(id, name))

        if self.request.input.get('async'):

            if id:
                impl_name = self.server.service_store.id_to_impl_name[id]
                name = self.server.service_store.service_data(
                    impl_name)['name']

            # If PID is given on input it means we must invoke this particular server process by it ID
            if pid:
                response = self.server.invoke_by_pid(name, payload, pid)
            else:
                response = self.invoke_async(name, payload, channel,
                                             data_format, transport,
                                             expiration)

        else:

            # Same as above in async branch
            if pid:
                response = self.server.invoke(name,
                                              payload,
                                              pid=pid,
                                              data_format=data_format)
            else:
                func, id_ = (self.invoke,
                             name) if name else (self.invoke_by_id, id)
                response = func(id_,
                                payload,
                                channel,
                                data_format,
                                transport,
                                serialize=True)

        if isinstance(response, basestring):
            if response:
                self.response.payload.response = response.encode(
                    'base64') if response else ''
Beispiel #16
0
    def _validate_entry(self, validate_item, id=None):
        for elem in('system', 'key'):
            name = self.request.input[elem]
            match = self.NAME_RE.match(name)
            if match and match.group() == name:
                continue
            else:
                msg = "System and key may contain only letters, digits and an underscore, failed to validate [{}] against the regular expression {}".format(
                    name, self.NAME_PATTERN)
                raise ZatoException(self.cid, msg)

        for item in self._get_dict_items():
            joined = KVDB.SEPARATOR.join((item['system'], item['key'], item['value']))
            if validate_item == joined and id != item['id']:
                msg = 'The triple of system:[{}], key:[{}], value:[{}] already exists'.format(item['system'], item['key'], item['value'])
                raise ZatoException(self.cid, msg)

        return True
Beispiel #17
0
    def init_flat_sio(self, cid, sio, data_format, transport, wsgi_environ,
                      required_list):
        """ Initializes flat SIO requests, i.e. not list ones.
        """
        self.is_xml = data_format == SIMPLE_IO.FORMAT.XML
        self.data_format = data_format
        self.transport = transport
        self._wsgi_environ = wsgi_environ

        optional_list = getattr(sio, 'input_optional', [])
        optional_list = [optional_list] if isinstance(
            optional_list, basestring) else optional_list

        path_prefix = getattr(sio, 'request_elem', 'request')
        default_value = getattr(sio, 'default_value', NO_DEFAULT_VALUE)
        use_text = getattr(sio, 'use_text', True)
        use_channel_params_only = getattr(sio, 'use_channel_params_only',
                                          False)
        self.encrypt_secrets = getattr(sio, 'encrypt_secrets', True)

        if self.simple_io_config:
            self.has_simple_io_config = True
            self.bool_parameter_prefixes = self.simple_io_config.get(
                'bool_parameter_prefixes', [])
            self.int_parameters = self.simple_io_config.get(
                'int_parameters', [])
            self.int_parameter_suffixes = self.simple_io_config.get(
                'int_parameter_suffixes', [])
        else:
            self.payload = self.raw_request

        required_params = {}

        if required_list:

            # Needs to check for this exact default value to prevent a FutureWarning in 'if not self.payload'
            if self.payload == '' and not self.channel_params:
                raise ZatoException(cid, 'Missing input')

            required_params.update(
                self.get_params(required_list, use_channel_params_only,
                                path_prefix, default_value, use_text))

        if optional_list:
            optional_params = self.get_params(optional_list,
                                              use_channel_params_only,
                                              path_prefix, default_value,
                                              use_text, False)
        else:
            optional_params = {}

        self.input.update(required_params)
        self.input.update(optional_params)

        for param, value in self.channel_params.iteritems():
            if param not in self.input:
                self.input[param] = value
Beispiel #18
0
 def _get_dict_item(self, id):
     """ Returns a dictionary entry by its ID.
     """
     for item in self._get_dict_items():
         if item['id'] == str(id):
             return item
     else:
         msg = 'Could not find the dictionary by its ID:[{}}]'.format(id)
         raise ZatoException(self.cid, msg)
Beispiel #19
0
def convert_sio(cid,
                param,
                param_name,
                value,
                has_simple_io_config,
                is_xml,
                bool_parameter_prefixes,
                int_parameters,
                int_parameter_suffixes,
                encrypt_func,
                date_time_format=None,
                data_format=ZATO_NONE,
                from_sio_to_external=False,
                special_values=(ZATO_NONE, ZATO_SEC_USE_RBAC),
                _is_bool=is_bool,
                _is_int=is_int,
                _is_secret=is_secret):
    try:
        if _is_bool(param, param_name, bool_parameter_prefixes):
            value = asbool(
                value or
                None)  # value can be an empty string and asbool chokes on that

        if value is not None:
            if isinstance(param, ForceType):
                value = param.convert(value, param_name, data_format,
                                      from_sio_to_external)
            else:
                # Empty string sent in lieu of integers are equivalent to None,
                # as though they were never sent - this is need for internal metaclasses
                if value == '' and _is_int(param_name, int_parameters,
                                           int_parameter_suffixes):
                    value = None

                if value and (value
                              not in special_values) and has_simple_io_config:
                    if _is_int(param_name, int_parameters,
                               int_parameter_suffixes):
                        value = int(value)
                    elif _is_secret(param_name):
                        # It will be None in SIO responses
                        if encrypt_func:
                            value = encrypt_func(value)

        return value

    except Exception, e:
        if isinstance(e, Reportable):
            e.cid = cid
            raise
        else:
            msg = 'Conversion error, param:`{}`, param_name:`{}`, repr:`{}`, type:`{}`, e:`{}`'.format(
                param, param_name, repr(value), type(value), format_exc(e))
            logger.error(msg)

            raise ZatoException(msg=msg)
Beispiel #20
0
    def __call__(self, req, initial_input_dict={}, initial_return_data={}, *args, **kwargs):
        """ Handles the request, taking care of common things and delegating 
        control to the subclass for fetching this view-specific data.
        """
        self.input_dict.clear()
        self.clear_user_message()

        try:
            super(CreateEdit, self).__call__(req, *args, **kwargs)
            self.set_input()

            input_dict = {'cluster_id': self.cluster_id}
            post_id = self.req.POST.get('id')

            if post_id:
                input_dict['id'] = post_id

            input_dict.update(initial_input_dict)

            for name in chain(self.SimpleIO.input_required, self.SimpleIO.input_optional):
                if name not in input_dict and name not in self.input_dict:
                    input_dict[name] = self.input.get(name)

            self.input_dict.update(input_dict)

            logger.debug(
                'Request input dict `%r` out of `%r`, `%r`, `%r`, `%r`, `%r`', self.input_dict,
                self.SimpleIO.input_required, self.SimpleIO.input_optional, self.input, self.req.GET, self.req.POST)

            response = self.req.zato.client.invoke(self.service_name, self.input_dict)
            if response.ok:
                return_data = {
                    'message': self.success_message(response.data)
                    }
                return_data.update(initial_return_data)

                for name in chain(self.SimpleIO.output_optional, self.SimpleIO.output_required):
                    if name not in initial_return_data:
                        value = getattr(response.data, name, None)
                        if value:
                            if isinstance(value, basestring):
                                value = value.encode('utf-8')
                            else:
                                value = str(value)
                        return_data[name] = value

                self.post_process_return_data(return_data)

                return HttpResponse(dumps(return_data), mimetype='application/javascript')
            else:
                msg = 'response:[{}], details.response.details:[{}]'.format(response, response.details)
                logger.error(msg)
                raise ZatoException(msg=msg)

        except Exception, e:
            return HttpResponseServerError(format_exc(e))
Beispiel #21
0
    def send_email(self, message):
        """Sends an email directly (no queuing).

        No retries are done, the caller should handle MailDeliveryException in order to ensure that
        the mail is never lost.

        :param message: the email.message.Message to send. The envelope sender will be extracted from the
                        ``Return-Path`` or ``From`` headers. The envelope recipients will be
                        extracted from the combined list of ``To``, ``CC`` and ``BCC`` headers.
        :return: the Message-ID of the message that was just sent, if successfully sent, otherwise raises
                 MailDeliveryException and logs root cause.
        """
        smtp_from = message['Return-Path'] or message['From']
        assert smtp_from, "The Return-Path or From header is required for any outbound email"

        # The email's "Envelope From" (Return-Path), and all recipient addresses must only contain ASCII characters.
        from_rfc2822 = extract_rfc2822_addresses(smtp_from)
        assert len(
            from_rfc2822
        ) == 1, "Malformed 'Return-Path' or 'From' address - it may only contain plain ASCII characters"
        smtp_from = from_rfc2822[0]
        email_to = message['To']
        email_cc = message['Cc']
        email_bcc = message['Bcc']
        smtp_to_list = filter(
            None,
            flatten(
                map(extract_rfc2822_addresses,
                    [email_to, email_cc, email_bcc])))
        assert smtp_to_list, "At least one valid recipient address should be specified for outgoing emails (To/Cc/Bcc)"

        try:
            message_id = message['Message-Id']

            # Add email in Maildir if smtp_host contains maildir.
            if self.smtp_host.startswith('maildir:/'):
                from mailbox import Maildir
                maildir_path = self.smtp_host[8:]
                mdir = Maildir(maildir_path, factory=None, create=True)
                mdir.add(message.as_string(True))
                return message_id

            try:
                self.conn.sendmail(smtp_from, smtp_to_list,
                                   message.as_string())
            finally:
                try:
                    # Close Connection of SMTP Server
                    self.conn.quit()
                except Exception:
                    # ignored, just a consequence of the previous exception
                    pass
        except Exception, e:
            msg = "Mail delivery failed via SMTP server '%s'.\n%s: %s" % (
                unicode(self.smtp_host), e.__class__.__name__, unicode(e))
            raise ZatoException(self.cid, msg)
Beispiel #22
0
    def invoke(self, target, *args, **kwargs):
        async_fallback, callback, callback_context, retry_repeats, retry_seconds, kwargs = self._get_retry_settings(
            target, **kwargs)

        # Let's invoke the service and find out if it works, maybe we don't need
        # to retry anything.

        kwargs['cid'] = kwargs.get('cid', new_cid())

        try:
            result = self.invoking_service.invoke(target, *args, **kwargs)
        except Exception as e:

            msg = 'Could not invoke:`{}`, cid:`{}`, e:`{}`'.format(
                target, self.invoking_service.cid, format_exc())
            logger.warn(msg)

            # How we handle the exception depends on whether the caller wants us
            # to block or prefers if we retry in background.
            if async_fallback:

                # .. invoke the background service and return CID to the caller.
                return self._invoke_async_retry(target, retry_repeats,
                                                retry_seconds,
                                                self.invoking_service.cid,
                                                kwargs['cid'], callback,
                                                callback_context, args, kwargs)

            # We are to block while repeating
            else:
                # Repeat the given number of times sleeping for as many seconds as we are told
                remaining = retry_repeats
                result = None

                while remaining > 1:
                    try:
                        result = self.invoking_service.invoke(
                            target, *args, **kwargs)
                    except Exception as e:
                        msg = retry_failed_msg(
                            (retry_repeats - remaining) + 1, retry_repeats,
                            target, retry_seconds, self.invoking_service.cid,
                            e)
                        logger.info(msg)
                        sleep(retry_seconds)
                        remaining -= 1

                # OK, give up now, there's nothing more we can do
                if not result:
                    msg = retry_limit_reached_msg(retry_repeats, target,
                                                  retry_seconds,
                                                  self.invoking_service.cid)
                    raise ZatoException(self.invoking_service.cid, msg)
        else:
            # All good, simply return the response
            return result
Beispiel #23
0
def validate_input_dict(cid, *validation_info):
    """ Checks that input belongs is one of allowed values.
    """
    for key_name, key, source in validation_info:
        if not source.has(key):
            msg = 'Invalid {}:[{}]'.format(key_name, key)
            log_msg = '{} (attrs: {})'.format(msg, source.attrs)

            logger.warn(log_msg)
            raise ZatoException(cid, msg)
Beispiel #24
0
    def invoke_async(self, name, payload='', channel=CHANNEL.INVOKE_ASYNC, data_format=DATA_FORMAT.DICT,
                     transport=None, expiration=BROKER.DEFAULT_EXPIRATION, to_json_string=False, cid=None, callback=None,
                     zato_ctx={}, environ={}):
        """ Invokes a service asynchronously by its name.
        """
        if self.component_enabled_target_matcher:
            name, target = self.extract_target(name)
            zato_ctx['zato.request_ctx.target'] = target
        else:
            target = None

        # Let's first find out if the service can be invoked at all
        impl_name = self.server.service_store.name_to_impl_name[name]

        if self.component_enabled_invoke_matcher:
            if not self._worker_store.invoke_matcher.is_allowed(impl_name):
                raise ZatoException(self.cid, 'Service `{}` (impl_name) cannot be invoked'.format(impl_name))

        if to_json_string:
            payload = dumps(payload)

        cid = cid or new_cid()

        # If there is any callback at all, we need to figure out its name because that's how it will be invoked by.
        if callback:

            # The same service
            if callback is self:
                callback = self.name

        else:
            sink = '{}-async-callback'.format(self.name)
            if sink in self.server.service_store.name_to_impl_name:
                callback = sink

            # Otherwise the callback must be a string pointing to the actual service to reply to so we don't need to do anything.

        msg = {}
        msg['action'] = SERVICE.PUBLISH.value
        msg['service'] = name
        msg['payload'] = payload
        msg['cid'] = cid
        msg['channel'] = channel
        msg['data_format'] = data_format
        msg['transport'] = transport
        msg['is_async'] = True
        msg['callback'] = callback
        msg['zato_ctx'] = zato_ctx
        msg['environ'] = environ

        # If we have a target we need to invoke all the servers
        # and these which are not able to handle the target will drop the message.
        (self.broker_client.publish if target else self.broker_client.invoke_async)(msg, expiration=expiration)

        return cid
Beispiel #25
0
    def get_slices(self, orig_start, orig_stop):
        """ Slices the time range into a series of per-minute/-hour/-day/-month or -year statistics.
        """
        slices = []
        start = parse(orig_start)
        stop = parse(orig_stop)

        delta, result = self._get_slice_period_type(start, stop, orig_start,
                                                    orig_stop)

        by_mins = result['by_mins']
        by_hours_mins = result['by_hours_mins']
        by_days_hours_mins = result['by_days_hours_mins']
        by_months_days_hours_mins = result['by_months_days_hours_mins']

        # Sanity check, find out whether more than one predicate is True.
        predicates = (by_mins, by_hours_mins, by_days_hours_mins,
                      by_months_days_hours_mins)
        sum_preds = sum(int(elem) for elem in predicates)
        if sum_preds > 1:
            msg = 'sum:[{}] of predicates:[{}] is > 1, delta:[{}, {} {} {} {}], start:[{}], stop:[{}]'.format(
                sum_preds, predicates, delta, delta.years, delta.months,
                delta.days, delta.hours, start, stop)
            raise ZatoException(self.cid, msg)

        # We require that start and stop be at least that many minutes apart and, obviosuly,
        # that start lives farther in the past.
        if by_mins and delta.total_minutes < self.MINIMUM_DIFFERENCE:
            raise ValueError(
                'stop and start must be at least [{}] minutes apart, start must be '\
                'farther in past; start:[{}], stop:[{}]'.format(
                   self.MINIMUM_DIFFERENCE, orig_start, orig_stop))

        if by_mins:
            # start=2012-10-23T20:13:00, stop=2012-10-23T21:07:00
            slices.append(self.by_minutes(start, stop))

        elif by_hours_mins:
            for slice in self._get_slices_by_hours(start, stop, delta):
                slices.append(slice)

        elif by_days_hours_mins or (by_months_days_hours_mins
                                    and delta.total_months == 1):
            for slice in self._get_slices_by_days(start, stop, delta):
                slices.append(slice)

        elif by_months_days_hours_mins:
            for slice in self._get_slices_by_months(start, stop, delta):
                slices.append(slice)

        else:
            for slice in self._get_slices_by_years(start, stop, delta):
                slices.append(slice)

        return slices
Beispiel #26
0
def remote_command_execute(req):
    """ Executes a command against the key/value DB.
    """
    try:
        response = req.zato.client.invoke('zato.kvdb.remote-command.execute', {'command': req.POST['command']})
        if response.has_data:
            return HttpResponse(dumps({'message': dumps(response.data.result)}), content_type='application/javascript')
        else:
            raise ZatoException(msg=response.details)
    except Exception, e:
        return HttpResponseServerError(format_exc(e))
Beispiel #27
0
    def handle(self):
        conn_id = self.request.input.id
        patt_type = self.request.input.audit_repl_patt_type

        with closing(self.odb.session()) as session:
            conn = session.query(HTTPSOAP).\
                filter(HTTPSOAP.id==conn_id).\
                one()

            if not self.request.input.pattern_list:
                # OK, no patterns at all so we indiscriminately delete existing ones, if any, for the connection.
                self._clear_patterns(conn)
                session.commit()

            else:
                pattern_class = JSONPointer if patt_type == MSG_PATTERN_TYPE.JSON_POINTER.id else XPath
                conn_pattern_list_class = HTTSOAPAuditReplacePatternsJSONPointer if patt_type == MSG_PATTERN_TYPE.JSON_POINTER.id else \
                    HTTSOAPAuditReplacePatternsXPath

                all_patterns = session.query(pattern_class).\
                    filter(pattern_class.cluster_id==self.server.cluster_id).\
                    all()

                missing = set(self.request.input.pattern_list) - set(
                    [elem.name for elem in all_patterns])
                if missing:
                    msg = 'Could not find one or more pattern(s) {}'.format(
                        sorted(missing))
                    self.logger.warn(msg)
                    raise ZatoException(self.cid, msg)

                # Clears but doesn't commit yet
                self._clear_patterns(conn)

                for name in self.request.input.pattern_list:
                    for pattern in all_patterns:
                        if name == pattern.name:
                            item = conn_pattern_list_class()
                            item.conn_id = conn.id
                            item.pattern_id = pattern.id
                            item.cluster_id = self.server.cluster_id
                            session.add(item)

                session.commit()

                params = {
                    'action': CHANNEL.HTTP_SOAP_AUDIT_PATTERNS.value,
                    'id': conn_id,
                    'audit_repl_patt_type':
                    self.request.input.audit_repl_patt_type,
                    'pattern_list': self.request.input.pattern_list,
                }
                self.broker_client.publish(params)
Beispiel #28
0
def create(req):
    try:
        response = req.zato.client.invoke('zato.http-soap.create', _get_edit_create_message(req.POST))
        if response.has_data:
            return _edit_create_response(req, response.data.id, 'created',
                req.POST['transport'], req.POST['connection'], req.POST['name'])
        else:
            raise ZatoException(msg=response.details)
    except Exception:
        msg = 'Object could not be created, e:`{}`'.format(format_exc())
        logger.error(msg)
        return HttpResponseServerError(msg)
Beispiel #29
0
def edit(req):
    try:
        response = req.zato.client.invoke('zato.http-soap.edit', _get_edit_create_message(req.POST, 'edit-'))
        if response.has_data:
            return _edit_create_response(req, response.data.id, 'updated',
                req.POST['transport'], req.POST['connection'], req.POST['edit-name'])
        else:
            raise ZatoException(msg=response.details)
    except Exception:
        msg = 'Update error, e:`{}`'.format(format_exc())
        logger.error(msg)
        return HttpResponseServerError(msg)
Beispiel #30
0
    def extract_target(self, name):
        """ Splits a service's name into name and target, if the latter is provided on input at all.
        """
        # It can be either a name or a name followed by the target to invoke the service on,
        # i.e. 'myservice' or 'myservice@mytarget'.
        if '@' in name:
            name, target = name.split('@')
            if not target:
                raise ZatoException(self.cid, 'Target must not be empty in `{}`'.format(name))
        else:
            target = ''

        return name, target