Add a prefix and return its ID.

            * `auth` [BaseAuth]
                AAA options.
            * `attr` [prefix_attr]
                Prefix attributes.
            * `args` [add_prefix_args]
                Arguments explaining how the prefix should be allocated.

            Returns a dict describing the prefix which was added.

            Prefixes can be added in three ways; manually, from a pool or
            from a prefix.

            Manually
                All prefix data, including the prefix itself is specified in the
                `attr` argument. The `args` argument shall be omitted.

            From a pool
                Most prefixes are expected to be automatically assigned from a pool.
                In this case, the :attr:`prefix` key is omitted from the `attr` argument.
                Also the :attr:`type` key can be omitted and the prefix type will then be
                set to the pools default prefix type. The :func:`find_free_prefix`
                function is used to find available prefixes for this allocation
                method, see its documentation for a description of how the
                `args` argument should be formatted.

            From a prefix
                A prefix can also be selected from another prefix. Also in this case
                the :attr:`prefix` key is omitted from the `attr` argument. See the
                documentation for the :func:`find_free_prefix` for a description of how
                the `args` argument is to be formatted.

            This is the documentation of the internal backend function. It's
            exposed over XML-RPC, please also see the XML-RPC documentation for
            :py:func:`nipap.xmlrpc.NipapXMLRPC.add_prefix` for full
            understanding.

def add_prefix(self, auth, attr, args=None):
        """ Add a prefix and return its ID.

            * `auth` [BaseAuth]
                AAA options.
            * `attr` [prefix_attr]
                Prefix attributes.
            * `args` [add_prefix_args]
                Arguments explaining how the prefix should be allocated.

            Returns a dict describing the prefix which was added.

            Prefixes can be added in three ways; manually, from a pool or
            from a prefix.

            Manually
                All prefix data, including the prefix itself is specified in the
                `attr` argument. The `args` argument shall be omitted.

            From a pool
                Most prefixes are expected to be automatically assigned from a pool.
                In this case, the :attr:`prefix` key is omitted from the `attr` argument.
                Also the :attr:`type` key can be omitted and the prefix type will then be
                set to the pools default prefix type. The :func:`find_free_prefix`
                function is used to find available prefixes for this allocation
                method, see its documentation for a description of how the
                `args` argument should be formatted.

            From a prefix
                A prefix can also be selected from another prefix. Also in this case
                the :attr:`prefix` key is omitted from the `attr` argument. See the
                documentation for the :func:`find_free_prefix` for a description of how
                the `args` argument is to be formatted.

            This is the documentation of the internal backend function. It's
            exposed over XML-RPC, please also see the XML-RPC documentation for
            :py:func:`nipap.xmlrpc.NipapXMLRPC.add_prefix` for full
            understanding.
        """

        if args is None:
            args = {}

        self._logger.debug("add_prefix called; attr: %s; args: %s" % (unicode(attr), unicode(args)))

        # args defined?
        if args is None:
            args = {}

        # attr must be a dict!
        if type(attr) != dict:
            raise NipapInputError("'attr' must be a dict")

        # handle pool attributes - find correct one and remove bad pool keys
        # note how this is not related to from-pool but is merely the pool
        # attributes if this prefix is to be part of a pool
        if 'pool_id' in attr or 'pool_name' in attr:
            if 'pool_id' in attr:
                if attr['pool_id'] is None:
                    pool = {
                        'id': None,
                        'name': None
                    }
                else:
                    pool = self._get_pool(auth, { 'id': attr['pool_id'] })

            else:
                if attr['pool_name'] is None:
                    pool = {
                        'id': None,
                        'name': None
                    }
                else:
                    # resolve pool name to pool id
                    pool = self._get_pool(auth, { 'name': attr['pool_name'] })

                # and delete the pool_name attr
                del(attr['pool_name'])

            attr['pool_id'] = pool['id']

        else:
            pool = {
                'id': None,
                'name': None
            }

        attr['authoritative_source'] = auth.authoritative_source

        # sanity checks for manual prefix vs from-pool vs from-prefix
        if 'prefix' in attr:
            if 'from-pool' in args or 'from-prefix' in args:
                raise NipapExtraneousInputError("specify 'prefix' or 'from-prefix' or 'from-pool'")

        else:
            if ('from-pool' not in args and 'from-prefix' not in args) or ('from-pool' in args and 'from-prefix' in args):
                raise NipapExtraneousInputError("specify 'prefix' or 'from-prefix' or 'from-pool'")

        # VRF handling for manually specified prefix
        if 'prefix' in attr:
            # handle VRF - find the correct one and remove bad VRF keys
            vrf = self._get_vrf(auth, attr)
            if 'vrf_rt' in attr:
                del(attr['vrf_rt'])
            if 'vrf_name' in attr:
                del(attr['vrf_name'])
            attr['vrf_id'] = vrf['id']

        # VRF handling for allocation from pool or parent prefix
        if 'from-pool' in args or 'from-prefix' in args:
            # did we get a VRF from the client?
            if 'vrf_id' in attr or 'vrf_rt' in attr or 'vrf_name' in attr:
                # handle VRF - find the correct one and remove bad VRF keys
                vrf = self._get_vrf(auth, attr)
                if 'vrf_rt' in attr:
                    del(attr['vrf_rt'])
                if 'vrf_name' in attr:
                    del(attr['vrf_name'])
                attr['vrf_id'] = vrf['id']

            if 'from-pool' in args:
                from_pool = self._get_pool(auth, args['from-pool'])

                # make sure there are prefixes in pool, if any prefix is present
                # than the implied-vrf is set, otherwise the pool is empty
                if from_pool['vrf_id'] is None:
                    raise NipapInputError('No prefixes in pool')

                # set default type from pool if missing
                if 'type' not in attr:
                    attr['type'] = from_pool['default_type']

                # set implied VRF of pool if missing
                if 'vrf_id' not in attr:
                    attr['vrf_id'] = from_pool['vrf_id']

                # make sure VRF aligns with pool implied VRF
                if attr['vrf_id'] != from_pool['vrf_id']:
                    raise NipapInputError("VRF must be the same as the pools implied VRF")

                vrf = self._get_vrf(auth, attr)

            if 'from-prefix' in args:
                # handle VRF - find the correct one and remove bad VRF keys
                vrf = self._get_vrf(auth, attr)
                if 'vrf_rt' in attr:
                    del(attr['vrf_rt'])
                if 'vrf_name' in attr:
                    del(attr['vrf_name'])
                attr['vrf_id'] = vrf['id']

            # VRF fiddling
            ffp_vrf = self._get_vrf(auth, attr)

            # get a new prefix
            res = self.find_free_prefix(auth, ffp_vrf, args)
            if res != []:
                attr['prefix'] = res[0]
            else:
                # TODO: Raise other exception?
                raise NipapNonExistentError("no free prefix found")

        # do we have all attributes?
        req_attr = [ 'prefix', 'authoritative_source' ]
        self._check_attr(attr, req_attr, _prefix_attrs)
        if ('description' not in attr) and ('node' not in attr):
            raise NipapMissingInputError('Either description or node must be specified.')

        if 'expires' in attr:
            attr['expires'] = _parse_expires(attr['expires'])

        insert, params = self._sql_expand_insert(attr)
        sql = "INSERT INTO ip_net_plan " + insert

        self._execute(sql, params)
        prefix_id = self._lastrowid()
        prefix = self.list_prefix(auth, { 'id': prefix_id })[0]

        # write to audit table
        audit_params = {
            'vrf_id': prefix['vrf_id'],
            'vrf_rt': prefix['vrf_rt'],
            'vrf_name': prefix['vrf_name'],
            'prefix_id': prefix['id'],
            'prefix_prefix': prefix['prefix'],
            'username': auth.username,
            'authenticated_as': auth.authenticated_as,
            'full_name': auth.full_name,
            'authoritative_source': auth.authoritative_source,
            'description': 'Added prefix %s with attr: %s' % (prefix['prefix'], unicode(attr))
        }
        sql, params = self._sql_expand_insert(audit_params)
        self._execute('INSERT INTO ip_net_log %s' % sql, params)

        if pool['id'] is not None:
            audit_params['pool_id'] = pool['id']
            audit_params['pool_name'] = pool['name']
            audit_params['description'] = 'Pool %s expanded with prefix %s in VRF %s' % (pool['name'], prefix['prefix'], unicode(prefix['vrf_rt']))

            sql, params = self._sql_expand_insert(audit_params)
            self._execute('INSERT INTO ip_net_log %s' % sql, params)

        return prefix