def create_call_event(owner_imsi, from_imsi, from_number, to_imsi, to_number,
                      old_balance, cost, kind, reason, tariff, call_duration,
                      billsec):
    """Creates a call event with data attached to the owner_imsi.

    This event can be created for outgoing and incoming calls.  For the former,
    the event data will be attached to the from_imsi.  For incoming calls,
    the owner_imsi will be the to_imsi.
    """
    new_balance = old_balance - int(cost)
    # Clamp the new_balance to a min of zero.
    if new_balance < 0:
        message = 'Nearly created a call event with a negative new_balance'
        message += ' for %s.  Old balance: %s, cost: %s, reason: %s' % (
            owner_imsi, old_balance, cost, reason)
        logger.warning(message)
        new_balance = 0
    _create_event(owner_imsi,
                  old_balance,
                  new_balance,
                  reason,
                  kind=kind,
                  call_duration=call_duration,
                  billsec=billsec,
                  from_imsi=from_imsi,
                  from_number=from_number,
                  to_imsi=to_imsi,
                  to_number=to_number,
                  tariff=tariff)
Exemple #2
0
def get_registration_conf():
    """Attempts to get registration config information from the cloud.

    Returns:
      the config data

    Raises:
      RegistrationError if the request does not return 200
    """
    bts_uuid = _get_snowflake()
    params = {'bts_uuid': bts_uuid}
    try:
        return _send_cloud_req(requests.get,
                               '/bts/sslconf',
                               'get cert config',
                               params=params)
    except RegistrationServerError as ex:
        if ex.status_code == 403:
            msg = 'BTS already registered - manually generate new snowflake'
            # unrecoverable error - exit
            logger.critical(msg)
            raise SystemExit(msg)
        if ex.status_code == 404:
            logger.warning('*** ensure BTS UUID (%s) is registered' %
                           (bts_uuid, ))
        raise
Exemple #3
0
 def _get_credit_delta(amount):
     """ Convert to int, should always be positive. """
     delta = int(amount)
     # check for negative values and warn
     if delta < 0:
         logger.warning("negative credit delta")
         delta = -delta
     return delta
Exemple #4
0
def ensure_fs_external_bound_to_vpn():
    # Make sure that we're bound to the VPN IP on the external sofia profile,
    # assuming the VPN is up.
    vpn_ip = system_utilities.get_vpn_ip()
    if not vpn_ip:
        return
    external_profile_ip = system_utilities.get_fs_profile_ip('external')
    if external_profile_ip != vpn_ip:  # TODO: treat these as netaddr, not string
        logger.warning('external profile should be bound to VPN IP and isn\'t,'
                       ' restarting FS.')
        Service.SystemService('freeswitch').restart()
    def process_update(self, net_subs):
        """
        Processes the subscriber list. Format is:

        {
            IMSI1: {'number': [<numbers>,...], 'balance': {<PN counter>}},
            IMSI2: ...
        }

        This updates the BTS with all subscribers instructed by the cloud; any
        subscribers that are not reported by the cloud will be removed from
        this BTS.
        """
        # dict where keys are imsis and values are sub info
        bts_imsis = self.get_subscriber_imsis()
        net_imsis = set(net_subs.keys())

        subs_to_add = net_imsis.difference(bts_imsis)
        subs_to_delete = bts_imsis.difference(net_imsis)
        subs_to_update = bts_imsis.intersection(net_imsis)

        for imsi in subs_to_delete:
            self.delete_subscriber(imsi)

        # TODO(shasan) does not add new numbers
        for imsi in subs_to_update:
            sub = net_subs[imsi]
            try:
                bal = crdt.PNCounter.from_state(sub['balance'])
                self.update_balance(imsi, bal)
            except SubscriberNotFound as e:
                logger.warning(
                    "Balance sync fail! IMSI: %s is not found Error: %s" %
                    (imsi, e))
            except ValueError as e:
                logger.error("Balance sync fail! IMSI: %s, %s Error: %s" %
                             (imsi, sub['balance'], e))
                subs_to_add.add(imsi)  # try to add it (again)

        for imsi in subs_to_add:
            sub = net_subs[imsi]
            numbers = sub['numbers']
            if not numbers:
                logger.notice("IMSI with no numbers? %s" % imsi)
                continue
            self.create_subscriber(imsi, numbers[0])
            for n in numbers[1:]:
                self.add_number(imsi, n)
            try:
                bal = crdt.PNCounter.from_state(sub['balance'])
                self.update_balance(imsi, bal)
            except (SubscriberNotFound, ValueError) as e:
                logger.error("Balance sync fail! IMSI: %s, %s Error: %s" %
                             (imsi, sub['balance'], e))
Exemple #6
0
def get_vpn_conf(eapi, csr):
    data = {'bts_uuid': _get_snowflake(), 'csr': csr}
    try:
        return _send_cloud_req(requests.post,
                               '/bts/register',
                               'get VPN config',
                               data=data,
                               headers=eapi.auth_header)
    except RegistrationServerError as ex:
        if ex.status_code == 400 and ex.text == '"status: 500"':
            logger.warning('*** internal certifier error, reset snowflake?')
        raise
Exemple #7
0
def verify_cert(_, cert_path, ca_path):
    """ Validate that cert has been signed by the specified CA. """
    try:
        # ugh, gotta explicitly send stderr to /dev/null with subprocess
        with open("/dev/null", "wb") as dev_null:
            subprocess.check_output("openssl verify -CAfile %s %s" %
                                    (ca_path, cert_path),
                                    shell=True,
                                    stderr=dev_null)
        return True
    except subprocess.CalledProcessError as ex:
        logger.warning("Unable to verify %s against %s: %s" %
                       (cert_path, ca_path, ex.output))
    return False
Exemple #8
0
    def get(self, request):

        logger.warning("Use of deprecated API call %s" % (request.GET, ))

        if "uuid" not in request.GET:
            return Response("No uuid specified.",
                            status=status.HTTP_400_BAD_REQUEST)
        query_num = request.GET["uuid"]
        try:
            network = get_network_from_user(request.user)
            bts = BTS.objects.get(uuid=query_num)
            # Strip the protocol field and just return the rest, removing any
            # trailing slash.
            bts_info = urlparse.urlparse(bts.inbound_url)
            result = {
                'netloc': bts_info.netloc,
                'hostname': bts_info.hostname,
                'owner': bts.network.id
            }
            return Response(result, status=status.HTTP_200_OK)
        except Number.DoesNotExist:
            return Response("No such UUID", status=status.HTTP_404_NOT_FOUND)
def create_sms_event(owner_imsi,
                     old_balance,
                     cost,
                     reason,
                     to_number,
                     from_imsi=None,
                     from_number=None):
    """Creates an SMS-related event with data attached to the owner_imsi."""
    new_balance = old_balance - int(cost)
    # Clamp the new_balance to a min of zero.
    if new_balance < 0:
        message = 'Nearly created an sms event with a negative new_balance'
        message += ' for %s.  Old balance: %s, cost: %s, reason: %s' % (
            owner_imsi, old_balance, cost, reason)
        logger.warning(message)
        new_balance = 0
    _create_event(owner_imsi,
                  old_balance,
                  new_balance,
                  reason,
                  from_imsi=from_imsi,
                  from_number=from_number,
                  to_number=to_number,
                  tariff=cost)
Exemple #10
0
    def post(self, request, format=None):
        """Handle POST requests."""
        # First make sure the dom parses.
        try:
            dom = xml.dom.minidom.parseString(request.POST['cdr'])
        except xml.parsers.expat.ExpatError:
            logger.warning("invalid XML CDR: '%s'" % (request.POST['cdr'], ))
            return Response("Bad XML", status=status.HTTP_400_BAD_REQUEST)
        except KeyError:
            logger.warning("invalid POST request: '%s'" % (request.POST, ))
            return Response("Missing CDR", status=status.HTTP_400_BAD_REQUEST)
        # Then make sure all of the necessary pieces are there.  Fail if any
        # required tags are missing
        data = {}
        for tag_name in [
                "billsec", "username", "caller_id_name", "network_addr",
                "destination_number"
        ]:
            data[tag_name] = dom.getElementsByTagName(tag_name)
        for tag_name in data:
            if not data[tag_name]:
                return Response("Missing XML",
                                status=status.HTTP_400_BAD_REQUEST)
            else:
                data[tag_name] = self.getText(data[tag_name][0].childNodes)
        # Convert certain tags to ints.
        for tag_name in ["billsec"]:
            data[tag_name] = int(data[tag_name])
        # Try to get Number instances for both the caller and recipient.
        # If we find both Numbers in the system then a user on one Endaga
        # network is calling a subscriber on a different Endaga-managed
        # network, cool. In our cloud freeswitch instance we actually
        # "short circuit" this call so it never goes to Nexmo, but to the
        # operators the call is a regular incoming / outgoing event, so we
        # will bill it as such.
        caller_number, dest_number = None, None
        try:
            caller_number = Number.objects.get(number=data["caller_id_name"])
            try:
                caller_cost = caller_number.network.calculate_operator_cost(
                    'off_network_send',
                    'call',
                    destination_number=data['destination_number'])
            except ValueError as ex:
                # this is raised iff the destination has an invalid prefix
                logger.error("invalid number prefix: %s" % (ex, ))
                caller_cost = 0
            caller_number.network.bill_for_call(caller_cost, data['billsec'],
                                                'outside_call')
            logger.info("billed network '%s' for outgoing call: %d" %
                        (caller_number.network.name, caller_cost))
        except Number.DoesNotExist:
            pass
        try:
            dest_number = Number.objects.get(number=data["destination_number"])
            # cost to receive doesn't take source/caller into account
            dest_cost = dest_number.network.calculate_operator_cost(
                'off_network_receive', 'call')
            dest_number.network.bill_for_call(dest_cost, data['billsec'],
                                              'incoming_call')
            logger.info("billed network '%s' for incoming call: %d" %
                        (dest_number.network.name, dest_cost))
        except Number.DoesNotExist:
            pass
        if not (caller_number or dest_number):
            # We didn't find either Number, that's a failure.
            return Response("Invalid caller and destination",
                            status=status.HTTP_404_NOT_FOUND)

        # Local, incoming and outgoing calls respond with 200 OK.
        return Response("", status=status.HTTP_200_OK)
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree. An additional grant
of patent rights can be found in the PATENTS file in the same directory.
"""

import abc
from enum import Enum
import delegator
import xmlrpc.client

from ccm.common import logger

try:
    from supervisor.xmlrpc import SupervisorTransport
except ImportError as e:
    logger.warning("Proceeding without supervisor support")


class ServiceState(Enum):
    Running = 1
    Error = 2


class BaseServiceControl(object, metaclass=abc.ABCMeta):
    """
    Service Control is the interface to managing services and their state. This
    allows us to start system services after system provisiong, and probe
    service state to monitor service health and issue restarts on failures.
    """
    __instance = None