def _validate_allocation_pools(self):
        """Validate IP allocation pools.

        Verify start and end address for each allocation pool are valid,
        ie: constituted by valid and appropriately ordered IP addresses.
        Also, verify pools do not overlap among themselves.
        Finally, verify that each range fall within the subnet's CIDR.
        """
        ip_pools = self._alloc_pools
        subnet_cidr = self._subnet_cidr

        LOG.debug(_("Performing IP validity checks on allocation pools"))
        ip_sets = []
        for ip_pool in ip_pools:
            try:
                start_ip = netaddr.IPAddress(ip_pool['start'])
                end_ip = netaddr.IPAddress(ip_pool['end'])
            except netaddr.AddrFormatError:
                LOG.info(_("Found invalid IP address in pool: "
                           "%(start)s - %(end)s:"),
                         {'start': ip_pool['start'],
                          'end': ip_pool['end']})
                raise n_exc_ext.InvalidAllocationPool(pool=ip_pool)
            if (start_ip.version != self._subnet_cidr.version or
                    end_ip.version != self._subnet_cidr.version):
                LOG.info(_("Specified IP addresses do not match "
                           "the subnet IP version"))
                raise n_exc_ext.InvalidAllocationPool(pool=ip_pool)
            if end_ip < start_ip:
                LOG.info(_("Start IP (%(start)s) is greater than end IP "
                           "(%(end)s)"),
                         {'start': ip_pool['start'], 'end': ip_pool['end']})
                raise n_exc_ext.InvalidAllocationPool(pool=ip_pool)
            if (start_ip < self._subnet_first_ip or
                    end_ip > self._subnet_last_ip):
                LOG.info(_("Found pool larger than subnet "
                           "CIDR:%(start)s - %(end)s"),
                         {'start': ip_pool['start'],
                          'end': ip_pool['end']})
                raise n_exc_ext.OutOfBoundsAllocationPool(
                    pool=ip_pool,
                    subnet_cidr=subnet_cidr)
            # Valid allocation pool
            # Create an IPSet for it for easily verifying overlaps
            ip_sets.append(netaddr.IPSet(netaddr.IPRange(
                ip_pool['start'],
                ip_pool['end']).cidrs()))

        LOG.debug(_("Checking for overlaps among allocation pools "
                    "and gateway ip"))
        ip_ranges = ip_pools[:]

        # Use integer cursors as an efficient way for implementing
        # comparison and avoiding comparing the same pair twice
        for l_cursor in xrange(len(ip_sets)):
            for r_cursor in xrange(l_cursor + 1, len(ip_sets)):
                if ip_sets[l_cursor] & ip_sets[r_cursor]:
                    l_range = ip_ranges[l_cursor]
                    r_range = ip_ranges[r_cursor]
                    LOG.info(_("Found overlapping ranges: %(l_range)s and "
                               "%(r_range)s"),
                             {'l_range': l_range, 'r_range': r_range})
                    raise n_exc_ext.OverlappingAllocationPools(
                        pool_1=l_range,
                        pool_2=r_range,
                        subnet_cidr=subnet_cidr)


def add_job_to_context(context, job_id):
    """Adds job to neutron context for use later."""
    db_job = db_api.async_transaction_find(
        context, id=job_id, scope=db_api.ONE)
    if not db_job:
        return
    context.async_job = {"job": v._make_job_dict(db_job)}

def add_job_to_context ( context , job_id ) : db_job = db_api . async_transaction_find ( context , id = job_id , scope = db_api . ONE ) if not db_job : return context . async_job = { "job" : v . _make_job_dict ( db_job ) }

Adds job to neutron context for use later .


def create_job(context, body):
    """Creates a job with support for subjobs.

    If parent_id is not in the body:
    * the job is considered a parent job
    * it will have a NULL transaction id
    * its transaction id == its id
    * all subjobs will use its transaction id as theirs

    Else:
    * the job is a sub job
    * the parent id is the id passed in
    * the transaction id is the root of the job tree
    """
    LOG.info("create_job for tenant %s" % context.tenant_id)

    if not context.is_admin:
        raise n_exc.NotAuthorized()
    job = body.get('job')
    if 'parent_id' in job:
        parent_id = job['parent_id']
        if not parent_id:
            raise q_exc.JobNotFound(job_id=parent_id)
        parent_job = db_api.async_transaction_find(
            context, id=parent_id, scope=db_api.ONE)
        if not parent_job:
            raise q_exc.JobNotFound(job_id=parent_id)
        tid = parent_id
        if parent_job.get('transaction_id'):
            tid = parent_job.get('transaction_id')
        job['transaction_id'] = tid

    if not job:
        raise n_exc.BadRequest(resource="job", msg="Invalid request body.")
    with context.session.begin(subtransactions=True):
        new_job = db_api.async_transaction_create(context, **job)
    return v._make_job_dict(new_job)

def
create_job
(
context
,
body
)
:
LOG
.
info
(
"create_job for tenant %s"
%
context
.
tenant_id
)
if
not
context
.
is_admin
:
raise
n_exc
.
NotAuthorized
(
)
job
=
body
.
get
(
'job'
)
if
'parent_id'
in
job
:
parent_id
=
job
[
'parent_id'
]
if
not
parent_id
:
raise
q_exc
.
JobNotFound
(
job_id
=
parent_id
)
parent_job
=
db_api
.
async_transaction_find
(
context
,
id
=
parent_id
,
scope
=
db_api
.
ONE
)
if
not
parent_job
:
raise
q_exc
.
JobNotFound
(
job_id
=
parent_id
)
tid
=
parent_id
if
parent_job
.
get
(
'transaction_id'
)
:
tid
=
parent_job
.
get
(
'transaction_id'
)
job
[
'transaction_id'
]
=
tid
if
not
job
:
raise
n_exc
.
BadRequest
(
resource
=
"job"
,
msg
=
"Invalid request body."
)
with
context
.
session
.
begin
(
subtransactions
=
True
)
:
new_job
=
db_api
.
async_transaction_create
(
context
,
*
*
job
)
return
v
.
_make_job_dict
(
new_job
)

Creates a job with support for subjobs .


def _lswitch_select_open(self, context, switches=None, **kwargs):
        """Selects an open lswitch for a network.

        Note that it does not select the most full switch, but merely one with
        ports available.
        """

        if switches is not None:
            for res in switches["results"]:
                count = res["_relations"]["LogicalSwitchStatus"]["lport_count"]
                if (self.limits['max_ports_per_switch'] == 0 or
                        count < self.limits['max_ports_per_switch']):
                    return res["uuid"]
        return None

def _lswitch_select_open ( self , context , switches = None , * * kwargs ) : if switches is not None : for res in switches [ "results" ] : count = res [ "_relations" ] [ "LogicalSwitchStatus" ] [ "lport_count" ] if ( self . limits [ 'max_ports_per_switch' ] == 0 or count < self . limits [ 'max_ports_per_switch' ] ) : return res [ "uuid" ] return None


def _add_default_tz_bindings(self, context, switch, network_id):
        """Configure any additional default transport zone bindings."""
        default_tz = CONF.NVP.default_tz

        # If there is no default tz specified it's pointless to try
        # and add any additional default tz bindings.
        if not default_tz:
            LOG.warn("additional_default_tz_types specified, "
                     "but no default_tz. Skipping "
                     "_add_default_tz_bindings().")
            return

        # This should never be called without a neutron network uuid,
        # we require it to bind some segment allocations.
        if not network_id:
            LOG.warn("neutron network_id not specified, skipping "
                     "_add_default_tz_bindings()")
            return

        for net_type in CONF.NVP.additional_default_tz_types:
            if net_type in TZ_BINDINGS:
                binding = TZ_BINDINGS[net_type]
                binding.add(context, switch, default_tz, network_id)
            else:
                LOG.warn("Unknown default tz type %s" % (net_type))

Configure any additional default transport zone bindings .


def _remove_default_tz_bindings(self, context, network_id):
        """Deconfigure any additional default transport zone bindings."""
        default_tz = CONF.NVP.default_tz

        if not default_tz:
            LOG.warn("additional_default_tz_types specified, "
                     "but no default_tz. Skipping "
                     "_remove_default_tz_bindings().")
            return

        if not network_id:
            LOG.warn("neutron network_id not specified, skipping "
                     "_remove_default_tz_bindings()")
            return

        for net_type in CONF.NVP.additional_default_tz_types:
            if net_type in TZ_BINDINGS:
                binding = TZ_BINDINGS[net_type]
                binding.remove(context, default_tz, network_id)
            else:
                LOG.warn("Unknown default tz type %s" % (net_type))

Deconfigure any additional default transport zone bindings .


def get_lswitch_ids_for_network(self, context, network_id):
        """Public interface for fetching lswitch ids for a given network.

        NOTE(morgabra) This is here because calling private methods
        from outside the class feels wrong, and we need to be able to
        fetch lswitch ids for use in other drivers.
        """
        lswitches = self._lswitches_for_network(context, network_id).results()
        return [s['uuid'] for s in lswitches["results"]]

def get_lswitch_ids_for_network ( self , context , network_id ) : lswitches = self . _lswitches_for_network ( context , network_id ) . results ( ) return [ s [ 'uuid' ] for s in lswitches [ "results" ] ]

Public interface for fetching lswitch ids for a given network .


def _load_worker_plugin_with_module(self, module, version):
        """Instantiates worker plugins that have requsite properties.

        The required properties are:
        * must have PLUGIN_EP entrypoint registered (or it wouldn't be in the
          list)
        * must have class attribute versions (list) of supported RPC versions
        * must subclass QuarkAsyncPluginBase
        """
        classes = inspect.getmembers(module, inspect.isclass)
        loaded = 0
        for cls_name, cls in classes:
            if hasattr(cls, 'versions'):
                if version not in cls.versions:
                    continue
            else:
                continue
            if issubclass(cls, base_worker.QuarkAsyncPluginBase):
                LOG.debug("Loading plugin %s" % cls_name)
                plugin = cls()
                self.plugins.append(plugin)
                loaded += 1
        LOG.debug("Found %d possible plugins and loaded %d" %
                  (len(classes), loaded))

Instantiates worker plugins that have requsite properties .


def _discover_via_entrypoints(self):
        """Looks for modules with amtching entry points."""
        emgr = extension.ExtensionManager(PLUGIN_EP, invoke_on_load=False)
        return ((ext.name, ext.plugin) for ext in emgr)

def _discover_via_entrypoints ( self ) : emgr = extension . ExtensionManager ( PLUGIN_EP , invoke_on_load = False ) return ( ( ext . name , ext . plugin ) for ext in emgr )

Looks for modules with amtching entry points .


def start_api_and_rpc_workers(self):
        """Initializes eventlet and starts wait for workers to exit.

        Spawns the workers returned from serve_rpc
        """
        pool = eventlet.GreenPool()

        quark_rpc = self.serve_rpc()
        pool.spawn(quark_rpc.wait)

        pool.waitall()

def start_api_and_rpc_workers ( self ) : pool = eventlet . GreenPool ( ) quark_rpc = self . serve_rpc ( ) pool . spawn ( quark_rpc . wait ) pool . waitall ( )

Initializes eventlet and starts wait for workers to exit .


def _chunks(self, iterable, chunk_size):
        """Chunks data into chunk with size<=chunk_size."""
        iterator = iter(iterable)
        chunk = list(itertools.islice(iterator, 0, chunk_size))
        while chunk:
            yield chunk
            chunk = list(itertools.islice(iterator, 0, chunk_size))

def _chunks ( self , iterable , chunk_size ) : iterator = iter ( iterable ) chunk = list ( itertools . islice ( iterator , 0 , chunk_size ) ) while chunk : yield chunk chunk = list ( itertools . islice ( iterator , 0 , chunk_size ) )

Chunks data into chunk with size< = chunk_size .


def _check_collisions(self, new_range, existing_ranges):
        """Check for overlapping ranges."""
        def _contains(num, r1):
            return (num >= r1[0] and
                    num <= r1[1])

        def _is_overlap(r1, r2):
            return (_contains(r1[0], r2) or
                    _contains(r1[1], r2) or
                    _contains(r2[0], r1) or
                    _contains(r2[1], r1))

        for existing_range in existing_ranges:
            if _is_overlap(new_range, existing_range):
                return True
        return False

def _check_collisions ( self , new_range , existing_ranges ) : def _contains ( num , r1 ) : return ( num >= r1 [ 0 ] and num <= r1 [ 1 ] ) def _is_overlap ( r1 , r2 ) : return ( _contains ( r1 [ 0 ] , r2 ) or _contains ( r1 [ 1 ] , r2 ) or _contains ( r2 [ 0 ] , r1 ) or _contains ( r2 [ 1 ] , r1 ) ) for existing_range in existing_ranges : if _is_overlap ( new_range , existing_range ) : return True return False


def _try_allocate(self, context, segment_id, network_id):
        """Find a deallocated network segment id and reallocate it.

        NOTE(morgabra) This locks the segment table, but only the rows
        in use by the segment, which is pretty handy if we ever have
        more than 1 segment or segment type.
        """
        LOG.info("Attempting to allocate segment for network %s "
                 "segment_id %s segment_type %s"
                 % (network_id, segment_id, self.segment_type))

        filter_dict = {
            "segment_id": segment_id,
            "segment_type": self.segment_type,
            "do_not_use": False
        }
        available_ranges = db_api.segment_allocation_range_find(
            context, scope=db_api.ALL, **filter_dict)
        available_range_ids = [r["id"] for r in available_ranges]

        try:
            with context.session.begin(subtransactions=True):
                # Search for any deallocated segment ids for the
                # given segment.
                filter_dict = {
                    "deallocated": True,
                    "segment_id": segment_id,
                    "segment_type": self.segment_type,
                    "segment_allocation_range_ids": available_range_ids
                }

                # NOTE(morgabra) We select 100 deallocated segment ids from
                # the table here, and then choose 1 randomly. This is to help
                # alleviate the case where an uncaught exception might leave
                # an allocation active on a remote service but we do not have
                # a record of it locally. If we *do* end up choosing a
                # conflicted id, the caller should simply allocate another one
                # and mark them all as reserved. If a single object has
                # multiple reservations on the same segment, they will not be
                # deallocated, and the operator must resolve the conficts
                # manually.
                allocations = db_api.segment_allocation_find(
                    context, lock_mode=True, **filter_dict).limit(100).all()

                if allocations:
                    allocation = random.choice(allocations)

                    # Allocate the chosen segment.
                    update_dict = {
                        "deallocated": False,
                        "deallocated_at": None,
                        "network_id": network_id
                    }
                    allocation = db_api.segment_allocation_update(
                        context, allocation, **update_dict)
                    LOG.info("Allocated segment %s for network %s "
                             "segment_id %s segment_type %s"
                             % (allocation["id"], network_id, segment_id,
                                self.segment_type))
                    return allocation
        except Exception:
            LOG.exception("Error in segment reallocation.")

        LOG.info("Cannot find reallocatable segment for network %s "
                 "segment_id %s segment_type %s"
                 % (network_id, segment_id, self.segment_type))

Find a deallocated network segment id and reallocate it .


def delete_locks(context, network_ids, addresses):
    """Deletes locks for each IP address that is no longer null-routed."""
    addresses_no_longer_null_routed = _find_addresses_to_be_unlocked(
        context, network_ids, addresses)
    LOG.info("Deleting %s lock holders on IPAddress with ids: %s",
             len(addresses_no_longer_null_routed),
             [addr.id for addr in addresses_no_longer_null_routed])

    for address in addresses_no_longer_null_routed:
        lock_holder = None
        try:
            lock_holder = db_api.lock_holder_find(
                context, lock_id=address.lock_id, name=LOCK_NAME,
                scope=db_api.ONE)
            if lock_holder:
                db_api.lock_holder_delete(context, address, lock_holder)
        except Exception:
            LOG.exception("Failed to delete lock holder %s", lock_holder)
            continue
    context.session.flush()

Deletes locks for each IP address that is no longer null - routed .


def create_locks(context, network_ids, addresses):
    """Creates locks for each IP address that is null-routed.

    The function creates the IP address if it is not present in the database.

    """

    for address in addresses:
        address_model = None
        try:
            address_model = _find_or_create_address(
                context, network_ids, address)
            lock_holder = None
            if address_model.lock_id:
                lock_holder = db_api.lock_holder_find(
                    context,
                    lock_id=address_model.lock_id, name=LOCK_NAME,
                    scope=db_api.ONE)

            if not lock_holder:
                LOG.info("Creating lock holder on IPAddress %s with id %s",
                         address_model.address_readable,
                         address_model.id)
                db_api.lock_holder_create(
                    context, address_model, name=LOCK_NAME, type="ip_address")
        except Exception:
            LOG.exception("Failed to create lock holder on IPAddress %s",
                          address_model)
            continue
    context.session.flush()

Creates locks for each IP address that is null - routed .


def select_ipam_strategy(self, network_id, network_strategy, **kwargs):
        """Return relevant IPAM strategy name.

        :param network_id: neutron network id.
        :param network_strategy: default strategy for the network.

        NOTE(morgabra) This feels like a hack but I can't think of a better
        idea. The root problem is we can now attach ports to networks with
        a different backend driver/ipam strategy than the network speficies.

        We handle the the backend driver part with allowing network_plugin to
        be specified for port objects. This works pretty well because nova or
        whatever knows when we are hooking up an Ironic node so it can pass
        along that key during port_create().

        IPAM is a little trickier, especially in Ironic's case, because we
        *must* use a specific IPAM for provider networks. There isn't really
        much of an option other than involve the backend driver when selecting
        the IPAM strategy.
        """
        LOG.info("Selecting IPAM strategy for network_id:%s "
                 "network_strategy:%s" % (network_id, network_strategy))

        net_type = "tenant"
        if STRATEGY.is_provider_network(network_id):
            net_type = "provider"

        strategy = self._ipam_strategies.get(net_type, {})
        default = strategy.get("default")
        overrides = strategy.get("overrides", {})

        # If we override a particular strategy explicitly, we use it.
        if network_strategy in overrides:
            LOG.info("Selected overridden IPAM strategy: %s"
                     % (overrides[network_strategy]))
            return overrides[network_strategy]

        # Otherwise, we are free to use an explicit default.
        if default:
            LOG.info("Selected default IPAM strategy for tenant "
                     "network: %s" % (default))
            return default

        # Fallback to the network-specified IPAM strategy
        LOG.info("Selected network strategy for tenant "
                 "network: %s" % (network_strategy))
        return network_strategy


def _get_base_network_info(self, context, network_id, base_net_driver):
        """Return a dict of extra network information.

        :param context: neutron request context.
        :param network_id: neturon network id.
        :param net_driver: network driver associated with network_id.
        :raises IronicException: Any unexpected data fetching failures will
            be logged and IronicException raised.

        This driver can attach to networks managed by other drivers. We may
        need some information from these drivers, or otherwise inform
        downstream about the type of network we are attaching to. We can
        make these decisions here.
        """
        driver_name = base_net_driver.get_name()
        net_info = {"network_type": driver_name}
        LOG.debug('_get_base_network_info: %s %s'
                  % (driver_name, network_id))

        # If the driver is NVP, we need to look up the lswitch id we should
        # be attaching to.
        if driver_name == 'NVP':
            LOG.debug('looking up lswitch ids for network %s'
                      % (network_id))
            lswitch_ids = base_net_driver.get_lswitch_ids_for_network(
                context, network_id)

            if not lswitch_ids or len(lswitch_ids) > 1:
                msg = ('lswitch id lookup failed, %s ids found.'
                       % (len(lswitch_ids)))
                LOG.error(msg)
                raise IronicException(msg)

            lswitch_id = lswitch_ids.pop()
            LOG.info('found lswitch for network %s: %s'
                     % (network_id, lswitch_id))
            net_info['lswitch_id'] = lswitch_id

        LOG.debug('_get_base_network_info finished: %s %s %s'
                  % (driver_name, network_id, net_info))
        return net_info

Return a dict of extra network information .


def create_port(self, context, network_id, port_id, **kwargs):
        """Create a port.

        :param context: neutron api request context.
        :param network_id: neutron network id.
        :param port_id: neutron port id.
        :param kwargs:
            required keys - device_id: neutron port device_id (instance_id)
                            instance_node_id: nova hypervisor host id
                            mac_address: neutron port mac address
                            base_net_driver: the base network driver
            optional keys - addresses: list of allocated IPAddress models
                            security_groups: list of associated security groups
        :raises IronicException: If the client is unable to create the
            downstream port for any reason, the exception will be logged
            and IronicException raised.
        """
        LOG.info("create_port %s %s %s" % (context.tenant_id, network_id,
                                           port_id))

        # sanity check
        if not kwargs.get('base_net_driver'):
            raise IronicException(msg='base_net_driver required.')
        base_net_driver = kwargs['base_net_driver']

        if not kwargs.get('device_id'):
            raise IronicException(msg='device_id required.')
        device_id = kwargs['device_id']

        if not kwargs.get('instance_node_id'):
            raise IronicException(msg='instance_node_id required.')
        instance_node_id = kwargs['instance_node_id']

        if not kwargs.get('mac_address'):
            raise IronicException(msg='mac_address is required.')
        mac_address = str(netaddr.EUI(kwargs["mac_address"]["address"]))
        mac_address = mac_address.replace('-', ':')

        # TODO(morgabra): Change this when we enable security groups.
        if kwargs.get('security_groups'):
            msg = 'ironic driver does not support security group operations.'
            raise IronicException(msg=msg)

        # unroll the given address models into a fixed_ips list we can
        # pass downstream
        fixed_ips = []
        addresses = kwargs.get('addresses')
        if not isinstance(addresses, list):
            addresses = [addresses]
        for address in addresses:
            fixed_ips.append(self._make_fixed_ip_dict(context, address))

        body = {
            "id": port_id,
            "network_id": network_id,
            "device_id": device_id,
            "device_owner": kwargs.get('device_owner', ''),
            "tenant_id": context.tenant_id or "quark",
            "roles": context.roles,
            "mac_address": mac_address,
            "fixed_ips": fixed_ips,
            "switch:hardware_id": instance_node_id,
            "dynamic_network": not STRATEGY.is_provider_network(network_id)
        }

        net_info = self._get_base_network_info(
            context, network_id, base_net_driver)
        body.update(net_info)

        try:
            LOG.info("creating downstream port: %s" % (body))
            port = self._create_port(context, body)
            LOG.info("created downstream port: %s" % (port))
            return {"uuid": port['port']['id'],
                    "vlan_id": port['port']['vlan_id']}
        except Exception as e:
            msg = "failed to create downstream port. Exception: %s" % (e)
            raise IronicException(msg=msg)


def update_port(self, context, port_id, **kwargs):
        """Update a port.

        :param context: neutron api request context.
        :param port_id: neutron port id.
        :param kwargs: optional kwargs.
        :raises IronicException: If the client is unable to update the
            downstream port for any reason, the exception will be logged
            and IronicException raised.

        TODO(morgabra) It does not really make sense in the context of Ironic
        to allow updating ports. fixed_ips and mac_address are burned in the
        configdrive on the host, and we otherwise cannot migrate a port between
        instances. Eventually we will need to support security groups, but for
        now it's a no-op on port data changes, and we need to rely on the
        API/Nova to not allow updating data on active ports.
        """
        LOG.info("update_port %s %s" % (context.tenant_id, port_id))

        # TODO(morgabra): Change this when we enable security groups.
        if kwargs.get("security_groups"):
            msg = 'ironic driver does not support security group operations.'
            raise IronicException(msg=msg)

        return {"uuid": port_id}


def diag_port(self, context, port_id, **kwargs):
        """Diagnose a port.

        :param context: neutron api request context.
        :param port_id: neutron port id.
        :param kwargs: optional kwargs.
        :raises IronicException: If the client is unable to fetch the
            downstream port for any reason, the exception will be
            logged and IronicException raised.
        """
        LOG.info("diag_port %s" % port_id)
        try:
            port = self._client.show_port(port_id)
        except Exception as e:
            msg = "failed fetching downstream port: %s" % (str(e))
            LOG.exception(msg)
            raise IronicException(msg=msg)
        return {"downstream_port": port}

def
diag_port
(
self
,
context
,
port_id
,
*
*
kwargs
)
:
LOG
.
info
(
"diag_port %s"
%
port_id
)
try
:
port
=
self
.
_client
.
show_port
(
port_id
)
except
Exception
as
e
:
msg
=
"failed fetching downstream port: %s"
%
(
str
(
e
)
)
LOG
.
exception
(
msg
)
raise
IronicException
(
msg
=
msg
)
return
{
"downstream_port"
:
port
}


def set(self, model, value):
        """Set tag on model object."""
        self.validate(value)
        self._pop(model)
        value = self.serialize(value)
        model.tags.append(value)

def set ( self , model , value ) : self . validate ( value ) self . _pop ( model ) value = self . serialize ( value ) model . tags . append ( value )


def get(self, model):
        """Get a matching valid tag off the model."""
        for tag in model.tags:
            if self.is_tag(tag):
                value = self.deserialize(tag)
                try:
                    self.validate(value)
                    return value
                except TagValidationError:
                    continue
        return None

def get ( self , model ) : for tag in model . tags : if self . is_tag ( tag ) : value = self . deserialize ( tag ) try : self . validate ( value ) return value except TagValidationError : continue return None

Get a matching valid tag off the model .


def _pop(self, model):
        """Pop all matching tags off the model and return them."""
        tags = []

        # collect any exsiting tags with matching prefix
        for tag in model.tags:
            if self.is_tag(tag):
                tags.append(tag)

        # remove collected tags from model
        if tags:
            for tag in tags:
                model.tags.remove(tag)

        return tags

Pop all matching tags off the model and return them .


def pop(self, model):
        """Pop all matching tags off the port, return a valid one."""
        tags = self._pop(model)
        if tags:
            for tag in tags:
                value = self.deserialize(tag)
                try:
                    self.validate(value)
                    return value
                except TagValidationError:
                    continue

def pop ( self , model ) : tags = self . _pop ( model ) if tags : for tag in tags : value = self . deserialize ( tag ) try : self . validate ( value ) return value except TagValidationError : continue

Pop all matching tags off the port return a valid one .


def has_tag(self, model):
        """Does the given port have this tag?"""
        for tag in model.tags:
            if self.is_tag(tag):
                return True
        return False

def has_tag ( self , model ) : for tag in model . tags : if self . is_tag ( tag ) : return True return False


def validate(self, value):
        """Validates a VLAN ID.

        :param value: The VLAN ID to validate against.
        :raises TagValidationError: Raised if the VLAN ID is invalid.
        """
        try:
            vlan_id_int = int(value)
            assert vlan_id_int >= self.MIN_VLAN_ID
            assert vlan_id_int <= self.MAX_VLAN_ID
        except Exception:
            msg = ("Invalid vlan_id. Got '%(vlan_id)s'. "
                   "vlan_id should be an integer between %(min)d and %(max)d "
                   "inclusive." % {'vlan_id': value,
                                   'min': self.MIN_VLAN_ID,
                                   'max': self.MAX_VLAN_ID})
            raise TagValidationError(value, msg)
        return True


def get_all(self, model):
        """Get all known tags from a model.

        Returns a dict of {<tag_name>:<tag_value>}.
        """
        tags = {}
        for name, tag in self.tags.items():
            for mtag in model.tags:
                if tag.is_tag(mtag):
                    tags[name] = tag.get(model)
        return tags

def get_all ( self , model ) : tags = { } for name , tag in self . tags . items ( ) : for mtag in model . tags : if tag . is_tag ( mtag ) : tags [ name ] = tag . get ( model ) return tags


def set_all(self, model, **tags):
        """Validate and set all known tags on a port."""
        for name, tag in self.tags.items():
            if name in tags:
                value = tags.pop(name)
                if value:
                    try:
                        tag.set(model, value)
                    except TagValidationError as e:
                        raise n_exc.BadRequest(
                            resource="tags",
                            msg="%s" % (e.message))

def set_all ( self , model , * * tags ) : for name , tag in self . tags . items ( ) : if name in tags : value = tags . pop ( name ) if value : try : tag . set ( model , value ) except TagValidationError as e : raise n_exc . BadRequest ( resource = "tags" , msg = "%s" % ( e . message ) )

Validate and set all known tags on a port .


def serialize_rules(self, rules):
        """Creates a payload for the redis server."""
        # TODO(mdietz): If/when we support other rule types, this comment
        #               will have to be revised.
        # Action and direction are static, for now. The implementation may
        # support 'deny' and 'egress' respectively in the future. We allow
        # the direction to be set to something else, technically, but current
        # plugin level call actually raises. It's supported here for unit
        # test purposes at this time
        serialized = []
        for rule in rules:
            direction = rule["direction"]
            source = ''
            destination = ''
            if rule.get("remote_ip_prefix"):
                prefix = rule["remote_ip_prefix"]
                if direction == "ingress":
                    source = self._convert_remote_network(prefix)
                else:
                    if (Capabilities.EGRESS not in
                            CONF.QUARK.environment_capabilities):
                        raise q_exc.EgressSecurityGroupRulesNotEnabled()
                    else:
                        destination = self._convert_remote_network(prefix)

            optional_fields = {}

            # NOTE(mdietz): this will expand as we add more protocols
            protocol_map = protocols.PROTOCOL_MAP[rule["ethertype"]]
            if rule["protocol"] == protocol_map["icmp"]:
                optional_fields["icmp type"] = rule["port_range_min"]
                optional_fields["icmp code"] = rule["port_range_max"]
            else:
                optional_fields["port start"] = rule["port_range_min"]
                optional_fields["port end"] = rule["port_range_max"]

            payload = {"ethertype": rule["ethertype"],
                       "protocol": rule["protocol"],
                       "source network": source,
                       "destination network": destination,
                       "action": "allow",
                       "direction": direction}
            payload.update(optional_fields)
            serialized.append(payload)
        return serialized

#               will have to be revised.

Creates a payload for the redis server .


def serialize_groups(self, groups):
        """Creates a payload for the redis server

        The rule schema is the following:

        REDIS KEY - port_device_id.port_mac_address/sg
        REDIS VALUE - A JSON dump of the following:

        port_mac_address must be lower-cased and stripped of non-alphanumeric
        characters

        {"id": "<arbitrary uuid>",
          "rules": [
            {"ethertype": <hexademical integer>,
             "protocol": <integer>,
             "port start": <integer>,  # optional
             "port end": <integer>,    # optional
             "icmp type": <integer>,   # optional
             "icmp code": <integer>,   # optional
             "source network": <string>,
             "destination network": <string>,
             "action": <string>,
             "direction": <string>},
          ],
          "security groups ack": <boolean>
        }

        Example:
        {"id": "004c6369-9f3d-4d33-b8f5-9416bf3567dd",
         "rules": [
           {"ethertype": 0x800,
            "protocol": "tcp",
            "port start": 1000,
            "port end": 1999,
            "source network": "10.10.10.0/24",
            "destination network": "",
            "action": "allow",
            "direction": "ingress"},
          ],
          "security groups ack": "true"
        }

        port start/end and icmp type/code are mutually exclusive pairs.
        """
        rules = []
        for group in groups:
            rules.extend(self.serialize_rules(group.rules))
        return rules

def serialize_groups ( self , groups ) : rules = [ ] for group in groups : rules . extend ( self . serialize_rules ( group . rules ) ) return rules


def apply_rules(self, device_id, mac_address, rules):
        """Writes a series of security group rules to a redis server."""
        LOG.info("Applying security group rules for device %s with MAC %s" %
                 (device_id, mac_address))

        rule_dict = {SECURITY_GROUP_RULE_KEY: rules}
        redis_key = self.vif_key(device_id, mac_address)
        # TODO(mdietz): Pipeline these. Requires some rewriting
        self.set_field(redis_key, SECURITY_GROUP_HASH_ATTR, rule_dict)
        self.set_field_raw(redis_key, SECURITY_GROUP_ACK, False)

Writes a series of security group rules to a redis server .


def get_security_group_states(self, interfaces):
        """Gets security groups for interfaces from Redis

        Returns a dictionary of xapi.VIFs with values of the current
        acknowledged status in Redis.

        States not explicitly handled:
        * ack key, no rules - This is the same as just tagging the VIF,
          the instance will be inaccessible
        * rules key, no ack - Nothing will happen, the VIF will
          not be tagged.
        """
        LOG.debug("Getting security groups from Redis for {0}".format(
            interfaces))
        interfaces = tuple(interfaces)
        vif_keys = [self.vif_key(vif.device_id, vif.mac_address)
                    for vif in interfaces]

        # Retrieve all fields associated with this key, which should be
        # 'security groups ack' and 'security group rules'.
        sec_grp_all = self.get_fields_all(vif_keys)

        ret = {}
        # Associate the vif with the fields in a dictionary
        for vif, group in zip(interfaces, sec_grp_all):
            if group:
                ret[vif] = {SECURITY_GROUP_ACK: None,
                            SECURITY_GROUP_HASH_ATTR: []}
                temp_ack = group[SECURITY_GROUP_ACK].lower()
                temp_rules = group[SECURITY_GROUP_HASH_ATTR]
                if temp_rules:
                    temp_rules = json.loads(temp_rules)
                    ret[vif][SECURITY_GROUP_HASH_ATTR] = temp_rules["rules"]
                if "true" in temp_ack:
                    ret[vif][SECURITY_GROUP_ACK] = True
                elif "false" in temp_ack:
                    ret[vif][SECURITY_GROUP_ACK] = False
                else:
                    ret.pop(vif, None)
                    LOG.debug("Skipping bad ack value %s" % temp_ack)

        return ret


def update_group_states_for_vifs(self, vifs, ack):
        """Updates security groups by setting the ack field"""
        vif_keys = [self.vif_key(vif.device_id, vif.mac_address)
                    for vif in vifs]
        self.set_fields(vif_keys, SECURITY_GROUP_ACK, ack)

def update_group_states_for_vifs ( self , vifs , ack ) : vif_keys = [ self . vif_key ( vif . device_id , vif . mac_address ) for vif in vifs ] self . set_fields ( vif_keys , SECURITY_GROUP_ACK , ack )


def run_migrations_offline():
    """Run migrations in 'offline' mode.

    This configures the context with just a URL
    and not an Engine, though an Engine is acceptable
    here as well.  By skipping the Engine creation
    we don't even need a DBAPI to be available.

    Calls to context.execute() here emit the given string to the
    script output.

    """
    context.configure(url=neutron_config.database.connection)

    with context.begin_transaction():
        context.run_migrations()

def run_migrations_offline ( ) : context . configure ( url = neutron_config . database . connection ) with context . begin_transaction ( ) : context . run_migrations ( )


def run_migrations_online():
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    engine = create_engine(
        neutron_config.database.connection,
        poolclass=pool.NullPool)

    connection = engine.connect()
    context.configure(
        connection=connection,
        target_metadata=target_metadata)

    try:
        with context.begin_transaction():
            context.run_migrations()
    finally:
        connection.close()

def run_migrations_online ( ) : engine = create_engine ( neutron_config . database . connection , poolclass = pool . NullPool ) connection = engine . connect ( ) context . configure ( connection = connection , target_metadata = target_metadata ) try : with context . begin_transaction ( ) : context . run_migrations ( ) finally : connection . close ( )


def do_notify(context, event_type, payload):
    """Generic Notifier.

    Parameters:
        - `context`: session context
        - `event_type`: the event type to report, i.e. ip.usage
        - `payload`: dict containing the payload to send
    """
    LOG.debug('IP_BILL: notifying {}'.format(payload))

    notifier = n_rpc.get_notifier('network')
    notifier.info(context, event_type, payload)

def
do_notify
(
context
,
event_type
,
payload
)
:
LOG
.
debug
(
'IP_BILL: notifying {}'
.
format
(
payload
)
)
notifier
=
n_rpc
.
get_notifier
(
'network'
)
notifier
.
info
(
context
,
event_type
,
payload
)


def notify(context, event_type, ipaddress, send_usage=False, *args, **kwargs):
    """Method to send notifications.

    We must send USAGE when a public IPv4 address is deallocated or a FLIP is
    associated.
    Parameters:
        - `context`: the context for notifier
        - `event_type`: the event type for IP allocate, deallocate, associate,
        disassociate
        - `ipaddress`: the ipaddress object to notify about
    Returns:
        nothing
    Notes: this may live in the billing module
    """
    if (event_type == IP_ADD and not CONF.QUARK.notify_ip_add) or \
       (event_type == IP_DEL and not CONF.QUARK.notify_ip_delete) or \
       (event_type == IP_ASSOC and not CONF.QUARK.notify_flip_associate) or \
       (event_type == IP_DISASSOC and not CONF.QUARK.notify_flip_disassociate)\
       or (event_type == IP_EXISTS and not CONF.QUARK.notify_ip_exists):
        LOG.debug('IP_BILL: notification {} is disabled by config'.
                  format(event_type))
        return

    # Do not send notifications when we are undoing due to an error
    if 'rollback' in kwargs and kwargs['rollback']:
        LOG.debug('IP_BILL: not sending notification because we are in undo')
        return

    # ip.add needs the allocated_at time.
    # All other events need the current time.
    ts = ipaddress.allocated_at if event_type == IP_ADD else _now()
    payload = build_payload(ipaddress, event_type, event_time=ts)

    # Send the notification with the payload
    do_notify(context, event_type, payload)

    # When we deallocate an IP or associate a FLIP we must send
    # a usage message to billing.
    # In other words when we supply end_time we must send USAGE to billing
    # immediately.
    # Our billing period is 24 hrs. If the address was allocated after midnight
    # send the start_time as as. If the address was allocated yesterday, then
    # send midnight as the start_time.
    # Note: if allocated_at is empty we assume today's midnight.
    if send_usage:
        if ipaddress.allocated_at is not None and \
           ipaddress.allocated_at >= _midnight_today():
            start_time = ipaddress.allocated_at
        else:
            start_time = _midnight_today()
        payload = build_payload(ipaddress,
                                IP_EXISTS,
                                start_time=start_time,
                                end_time=ts)
        do_notify(context, IP_EXISTS, payload)


def build_payload(ipaddress,
                  event_type,
                  event_time=None,
                  start_time=None,
                  end_time=None):
    """Method builds a payload out of the passed arguments.

    Parameters:
        `ipaddress`: the models.IPAddress object
        `event_type`: USAGE,CREATE,DELETE,SUSPEND,or UNSUSPEND
        `start_time`: startTime for cloudfeeds
        `end_time`: endTime for cloudfeeds
    Returns a dictionary suitable to notify billing.
    Message types mapping to cloud feeds for references:
        ip.exists       - USAGE
        ip.add          - CREATE
        ip.delete       - DELETE
        ip.associate    - UP
        ip.disassociate  - DOWN
    Refer to: http://rax.io/cf-api for more details.
    """
    # This is the common part of all message types
    payload = {
        'event_type': unicode(event_type),
        'tenant_id': unicode(ipaddress.used_by_tenant_id),
        'ip_address': unicode(ipaddress.address_readable),
        'ip_version': int(ipaddress.version),
        'ip_type': unicode(ipaddress.address_type),
        'id': unicode(ipaddress.id)
    }

    # Depending on the message type add the appropriate fields
    if event_type == IP_EXISTS:
        if start_time is None or end_time is None:
            raise ValueError('IP_BILL: {} start_time/end_time cannot be empty'
                             .format(event_type))
        payload.update({
            'startTime': unicode(convert_timestamp(start_time)),
            'endTime': unicode(convert_timestamp(end_time))
        })
    elif event_type in [IP_ADD, IP_DEL, IP_ASSOC, IP_DISASSOC]:
        if event_time is None:
            raise ValueError('IP_BILL: {}: event_time cannot be NULL'
                             .format(event_type))
        payload.update({
            'eventTime': unicode(convert_timestamp(event_time)),
            'subnet_id': unicode(ipaddress.subnet_id),
            'network_id': unicode(ipaddress.network_id),
            'public': True if ipaddress.network_id == PUBLIC_NETWORK_ID
            else False,
        })
    else:
        raise ValueError('IP_BILL: bad event_type: {}'.format(event_type))

    return payload

Method builds a payload out of the passed arguments .


def build_full_day_ips(query, period_start, period_end):
    """Method to build an IP list for the case 1

    when the IP was allocated before the period start
    and is still allocated after the period end.
    This method only looks at public IPv4 addresses.
    """
    # Filter out only IPv4 that have not been deallocated
    ip_list = query.\
        filter(models.IPAddress.version == 4L).\
        filter(models.IPAddress.network_id == PUBLIC_NETWORK_ID).\
        filter(models.IPAddress.used_by_tenant_id is not None).\
        filter(models.IPAddress.allocated_at != null()).\
        filter(models.IPAddress.allocated_at < period_start).\
        filter(or_(models.IPAddress._deallocated is False,
                   models.IPAddress.deallocated_at == null(),
                   models.IPAddress.deallocated_at >= period_end)).all()

    return ip_list


def calc_periods(hour=0, minute=0):
    """Returns a tuple of start_period and end_period.

    Assumes that the period is 24-hrs.
    Parameters:
        - `hour`: the hour from 0 to 23 when the period ends
        - `minute`: the minute from 0 to 59 when the period ends
    This method will calculate the end of the period as the closest hour/minute
    going backwards.
    It will also calculate the start of the period as the passed hour/minute
    but 24 hrs ago.
    Example, if we pass 0, 0 - we will get the events from 0:00 midnight of the
    day before yesterday until today's midnight.
    If we pass 2,0 - we will get the start time as 2am of the previous morning
    till 2am of today's morning.
    By default it's midnight.
    """
    # Calculate the time intervals in a usable form
    period_end = datetime.datetime.utcnow().replace(hour=hour,
                                                    minute=minute,
                                                    second=0,
                                                    microsecond=0)
    period_start = period_end - datetime.timedelta(days=1)

    # period end should be slightly before the midnight.
    # hence, we subtract a second
    # this will force period_end to store something like:
    # datetime.datetime(2016, 5, 19, 23, 59, 59, 999999)
    # instead of:
    # datetime.datetime(2016, 5, 20,  0,  0,  0,      0)
    period_end -= datetime.timedelta(seconds=1)

    return (period_start, period_end)

Returns a tuple of start_period and end_period .


def _make_job_dict(job):
    """Creates the view for a job while calculating progress.

    Since a root job does not have a transaction id (TID) it will return its
    id as the TID.
    """
    body = {"id": job.get('id'),
            "action": job.get('action'),
            "completed": job.get('completed'),
            "tenant_id": job.get('tenant_id'),
            "created_at": job.get('created_at'),
            "transaction_id": job.get('transaction_id'),
            "parent_id": job.get('parent_id', None)}
    if not body['transaction_id']:
        body['transaction_id'] = job.get('id')
    completed = 0
    for sub in job.subtransactions:
        if sub.get('completed'):
            completed += 1
    pct = 100 if job.get('completed') else 0
    if len(job.subtransactions) > 0:
        pct = float(completed) / len(job.subtransactions) * 100.0
    body['transaction_percent'] = int(pct)
    body['completed_subtransactions'] = completed
    body['subtransactions'] = len(job.subtransactions)
    return body

def _make_job_dict ( job ) : body = { "id" : job . get ( 'id' ) , "action" : job . get ( 'action' ) , "completed" : job . get ( 'completed' ) , "tenant_id" : job . get ( 'tenant_id' ) , "created_at" : job . get ( 'created_at' ) , "transaction_id" : job . get ( 'transaction_id' ) , "parent_id" : job . get ( 'parent_id' , None ) } if not body [ 'transaction_id' ] : body [ 'transaction_id' ] = job . get ( 'id' ) completed = 0 for sub in job . subtransactions : if sub . get ( 'completed' ) : completed += 1 pct = 100 if job . get ( 'completed' ) else 0 if len ( job . subtransactions ) > 0 : pct = float ( completed ) / len ( job . subtransactions ) * 100.0 body [ 'transaction_percent' ] = int ( pct ) body [ 'completed_subtransactions' ] = completed body [ 'subtransactions' ] = len ( job . subtransactions ) return body

Creates the view for a job while calculating progress .


def get_mac_address_range(context, id, fields=None):
    """Retrieve a mac_address_range.

    : param context: neutron api request context
    : param id: UUID representing the network to fetch.
    : param fields: a list of strings that are valid keys in a
        network dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_mac_address_range %s for tenant %s fields %s" %
             (id, context.tenant_id, fields))

    if not context.is_admin:
        raise n_exc.NotAuthorized()

    mac_address_range = db_api.mac_address_range_find(
        context, id=id, scope=db_api.ONE)

    if not mac_address_range:
        raise q_exc.MacAddressRangeNotFound(
            mac_address_range_id=id)
    return v._make_mac_range_dict(mac_address_range)


def delete_mac_address_range(context, id):
    """Delete a mac_address_range.

    : param context: neutron api request context
    : param id: UUID representing the mac_address_range to delete.
    """
    LOG.info("delete_mac_address_range %s for tenant %s" %
             (id, context.tenant_id))
    if not context.is_admin:
        raise n_exc.NotAuthorized()

    with context.session.begin():
        mar = db_api.mac_address_range_find(context, id=id, scope=db_api.ONE)
        if not mar:
            raise q_exc.MacAddressRangeNotFound(
                mac_address_range_id=id)
        _delete_mac_address_range(context, mar)


def delete_segment_allocation_range(context, sa_id):
    """Delete a segment_allocation_range.

    : param context: neutron api request context
    : param id: UUID representing the segment_allocation_range to delete.
    """
    LOG.info("delete_segment_allocation_range %s for tenant %s" %
             (sa_id, context.tenant_id))
    if not context.is_admin:
        raise n_exc.NotAuthorized()

    with context.session.begin():
        sa_range = db_api.segment_allocation_range_find(
            context, id=sa_id, scope=db_api.ONE)
        if not sa_range:
            raise q_exc.SegmentAllocationRangeNotFound(
                segment_allocation_range_id=sa_id)
        _delete_segment_allocation_range(context, sa_range)


def filter_factory(global_conf, **local_conf):
    """Returns a WSGI filter app for use with paste.deploy."""
    conf = global_conf.copy()
    conf.update(local_conf)

    def wrapper(app):
        return ResponseAsyncIdAdder(app, conf)

    return wrapper

def filter_factory ( global_conf , * * local_conf ) : conf = global_conf . copy ( ) conf . update ( local_conf ) def wrapper ( app ) : return ResponseAsyncIdAdder ( app , conf ) return wrapper

Returns a WSGI filter app for use with paste . deploy .


def get_used_ips(session, **kwargs):
    """Returns dictionary with keys segment_id and value used IPs count.

    Used IP address count is determined by:
    - allocated IPs
    - deallocated IPs whose `deallocated_at` is within the `reuse_after`
    window compared to the present time, excluding IPs that are accounted for
    in the current IP policy (because IP policy is mutable and deallocated IPs
    are not checked nor deleted on IP policy creation, thus deallocated IPs
    that don't fit the current IP policy can exist in the neutron database).
    """
    LOG.debug("Getting used IPs...")
    with session.begin():
        query = session.query(
            models.Subnet.segment_id,
            func.count(models.IPAddress.address))
        query = query.group_by(models.Subnet.segment_id)
        query = _filter(query, **kwargs)

        reuse_window = timeutils.utcnow() - datetime.timedelta(
            seconds=cfg.CONF.QUARK.ipam_reuse_after)
        # NOTE(asadoughi): This is an outer join instead of a regular join
        # to include subnets with zero IP addresses in the database.
        query = query.outerjoin(
            models.IPAddress,
            and_(models.Subnet.id == models.IPAddress.subnet_id,
                 or_(not_(models.IPAddress.lock_id.is_(None)),
                     models.IPAddress._deallocated.is_(None),
                     models.IPAddress._deallocated == 0,
                     models.IPAddress.deallocated_at > reuse_window)))

        query = query.outerjoin(
            models.IPPolicyCIDR,
            and_(
                models.Subnet.ip_policy_id == models.IPPolicyCIDR.ip_policy_id,
                models.IPAddress.address >= models.IPPolicyCIDR.first_ip,
                models.IPAddress.address <= models.IPPolicyCIDR.last_ip))
        # NOTE(asadoughi): (address is allocated) OR
        # (address is deallocated and not inside subnet's IP policy)
        query = query.filter(or_(
            models.IPAddress._deallocated.is_(None),
            models.IPAddress._deallocated == 0,
            models.IPPolicyCIDR.id.is_(None)))

        ret = ((segment_id, address_count)
               for segment_id, address_count in query.all())
        return dict(ret)

Returns dictionary with keys segment_id and value used IPs count .


def get_unused_ips(session, used_ips_counts, **kwargs):
    """Returns dictionary with key segment_id, and value unused IPs count.

    Unused IP address count is determined by:
    - adding subnet's cidr's size
    - subtracting IP policy exclusions on subnet
    - subtracting used ips per segment
    """
    LOG.debug("Getting unused IPs...")
    with session.begin():
        query = session.query(
            models.Subnet.segment_id,
            models.Subnet)
        query = _filter(query, **kwargs)
        query = query.group_by(models.Subnet.segment_id, models.Subnet.id)

        ret = defaultdict(int)
        for segment_id, subnet in query.all():
            net_size = netaddr.IPNetwork(subnet._cidr).size
            ip_policy = subnet["ip_policy"] or {"size": 0}
            ret[segment_id] += net_size - ip_policy["size"]

        for segment_id in used_ips_counts:
            ret[segment_id] -= used_ips_counts[segment_id]

        return ret

def
get_unused_ips
(
session
,
used_ips_counts
,
*
*
kwargs
)
:
LOG
.
debug
(
"Getting unused IPs..."
)
with
session
.
begin
(
)
:
query
=
session
.
query
(
models
.
Subnet
.
segment_id
,
models
.
Subnet
)
query
=
_filter
(
query
,
*
*
kwargs
)
query
=
query
.
group_by
(
models
.
Subnet
.
segment_id
,
models
.
Subnet
.
id
)
ret
=
defaultdict
(
int
)
for
segment_id
,
subnet
in
query
.
all
(
)
:
net_size
=
netaddr
.
IPNetwork
(
subnet
.
_cidr
)
.
size
ip_policy
=
subnet
[
"ip_policy"
]
or
{
"size"
:
0
}
ret
[
segment_id
]
+=
net_size
ip_policy
[
"size"
]
for
segment_id
in
used_ips_counts
:
ret
[
segment_id
]
-=
used_ips_counts
[
segment_id
]
return
ret

Returns dictionary with key segment_id and value unused IPs count .


def get_interfaces(self):
        """Returns a set of VIFs from `get_instances` return value."""
        LOG.debug("Getting interfaces from Xapi")

        with self.sessioned() as session:
            instances = self.get_instances(session)
            recs = session.xenapi.VIF.get_all_records()

        interfaces = set()
        for vif_ref, rec in recs.iteritems():
            vm = instances.get(rec["VM"])
            if not vm:
                continue
            device_id = vm.uuid
            interfaces.add(VIF(device_id, rec, vif_ref))
        return interfaces

def
get_interfaces
(
self
)
:
LOG
.
debug
(
"Getting interfaces from Xapi"
)
with
self
.
sessioned
(
)
as
session
:
instances
=
self
.
get_instances
(
session
)
recs
=
session
.
xenapi
.
VIF
.
get_all_records
(
)
interfaces
=
set
(
)
for
vif_ref
,
rec
in
recs
.
iteritems
(
)
:
vm
=
instances
.
get
(
rec
[
"VM"
]
)
if
not
vm
:
continue
device_id
=
vm
.
uuid
interfaces
.
add
(
VIF
(
device_id
,
rec
,
vif_ref
)
)
return
interfaces

Returns a set of VIFs from get_instances return value .


def update_interfaces(self, added_sg, updated_sg, removed_sg):
        """Handles changes to interfaces' security groups

        Calls refresh_interfaces on argument VIFs. Set security groups on
        added_sg's VIFs. Unsets security groups on removed_sg's VIFs.
        """
        if not (added_sg or updated_sg or removed_sg):
            return

        with self.sessioned() as session:
            self._set_security_groups(session, added_sg)
            self._unset_security_groups(session, removed_sg)
            combined = added_sg + updated_sg + removed_sg
            self._refresh_interfaces(session, combined)

def update_interfaces ( self , added_sg , updated_sg , removed_sg ) : if not ( added_sg or updated_sg or removed_sg ) : return with self . sessioned ( ) as session : self . _set_security_groups ( session , added_sg ) self . _unset_security_groups ( session , removed_sg ) combined = added_sg + updated_sg + removed_sg self . _refresh_interfaces ( session , combined )

Handles changes to interfaces security groups


def update_network(context, id, network):
    """Update values of a network.

    : param context: neutron api request context
    : param id: UUID representing the network to update.
    : param network: dictionary with keys indicating fields to update.
        valid keys are those that have a value of True for 'allow_put'
        as listed in the RESOURCE_ATTRIBUTE_MAP object in
        neutron/api/v2/attributes.py.
    """
    LOG.info("update_network %s for tenant %s" %
             (id, context.tenant_id))
    with context.session.begin():
        net = db_api.network_find(context, id=id, scope=db_api.ONE)
        if not net:
            raise n_exc.NetworkNotFound(net_id=id)
        net_dict = network["network"]
        utils.pop_param(net_dict, "network_plugin")
        if not context.is_admin and "ipam_strategy" in net_dict:
            utils.pop_param(net_dict, "ipam_strategy")
        net = db_api.network_update(context, net, **net_dict)

    return v._make_network_dict(net)

def
update_network
(
context
,
id
,
network
)
:
LOG
.
info
(
"update_network %s for tenant %s"
%
(
id
,
context
.
tenant_id
)
)
with
context
.
session
.
begin
(
)
:
net
=
db_api
.
network_find
(
context
,
id
=
id
,
scope
=
db_api
.
ONE
)
if
not
net
:
raise
n_exc
.
NetworkNotFound
(
net_id
=
id
)
net_dict
=
network
[
"network"
]
utils
.
pop_param
(
net_dict
,
"network_plugin"
)
if
not
context
.
is_admin
and
"ipam_strategy"
in
net_dict
:
utils
.
pop_param
(
net_dict
,
"ipam_strategy"
)
net
=
db_api
.
network_update
(
context
,
net
,
*
*
net_dict
)
return
v
.
_make_network_dict
(
net
)


def get_network(context, id, fields=None):
    """Retrieve a network.

    : param context: neutron api request context
    : param id: UUID representing the network to fetch.
    : param fields: a list of strings that are valid keys in a
        network dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_network %s for tenant %s fields %s" %
             (id, context.tenant_id, fields))

    network = db_api.network_find(context=context, limit=None, sorts=['id'],
                                  marker=None, page_reverse=False,
                                  id=id, join_subnets=True, scope=db_api.ONE)
    if not network:
        raise n_exc.NetworkNotFound(net_id=id)
    return v._make_network_dict(network, fields=fields)


def get_networks(context, limit=None, sorts=['id'], marker=None,
                 page_reverse=False, filters=None, fields=None):
    """Retrieve a list of networks.

    The contents of the list depends on the identity of the user
    making the request (as indicated by the context) as well as any
    filters.
    : param context: neutron api request context
    : param filters: a dictionary with keys that are valid keys for
        a network as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictiontary
        are an iterable containing values that will be used for an exact
        match comparison for that value.  Each result returned by this
        function will have matched one of the values for each key in
        filters.
    : param fields: a list of strings that are valid keys in a
        network dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_networks for tenant %s with filters %s, fields %s" %
             (context.tenant_id, filters, fields))
    filters = filters or {}
    nets = db_api.network_find(context, limit, sorts, marker, page_reverse,
                               join_subnets=True, **filters) or []
    nets = [v._make_network_dict(net, fields=fields) for net in nets]
    return nets


def get_networks_count(context, filters=None):
    """Return the number of networks.

    The result depends on the identity of the user making the request
    (as indicated by the context) as well as any filters.
    : param context: neutron api request context
    : param filters: a dictionary with keys that are valid keys for
        a network as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictiontary
        are an iterable containing values that will be used for an exact
        match comparison for that value.  Each result returned by this
        function will have matched one of the values for each key in
        filters.

    NOTE: this method is optional, as it was not part of the originally
          defined plugin API.
    """
    LOG.info("get_networks_count for tenant %s filters %s" %
             (context.tenant_id, filters))
    return db_api.network_count_all(context)


def delete_network(context, id):
    """Delete a network.

    : param context: neutron api request context
    : param id: UUID representing the network to delete.
    """
    LOG.info("delete_network %s for tenant %s" % (id, context.tenant_id))
    with context.session.begin():
        net = db_api.network_find(context=context, limit=None, sorts=['id'],
                                  marker=None, page_reverse=False, id=id,
                                  scope=db_api.ONE)
        if not net:
            raise n_exc.NetworkNotFound(net_id=id)
        if not context.is_admin:
            if STRATEGY.is_provider_network(net.id):
                raise n_exc.NotAuthorized(net_id=id)
        if net.ports:
            raise n_exc.NetworkInUse(net_id=id)
        net_driver = registry.DRIVER_REGISTRY.get_driver(net["network_plugin"])
        net_driver.delete_network(context, id)
        for subnet in net["subnets"]:
            subnets._delete_subnet(context, subnet)
        db_api.network_delete(context, net)

def
delete_network
(
context
,
id
)
:
LOG
.
info
(
"delete_network %s for tenant %s"
%
(
id
,
context
.
tenant_id
)
)
with
context
.
session
.
begin
(
)
:
net
=
db_api
.
network_find
(
context
=
context
,
limit
=
,
sorts
=
[
'id'
]
,
marker
=
,
page_reverse
=
False
,
id
=
id
,
scope
=
db_api
.
ONE
)
if
not
net
:
raise
n_exc
.
NetworkNotFound
(
net_id
=
id
)
if
not
context
.
is_admin
:
if
STRATEGY
.
is_provider_network
(
net
.
id
)
:
raise
n_exc
.
NotAuthorized
(
net_id
=
id
)
if
net
.
ports
:
raise
n_exc
.
NetworkInUse
(
net_id
=
id
)
net_driver
=
registry
.
DRIVER_REGISTRY
.
get_driver
(
net
[
"network_plugin"
]
)
net_driver
.
delete_network
(
context
,
id
)
for
subnet
in
net
[
"subnets"
]
:
subnets
.
_delete_subnet
(
context
,
subnet
)
db_api
.
network_delete
(
context
,
net
)


def make_case2(context):
    """This is a helper method for testing.

    When run with the current context, it will create a case 2 entries
    in the database. See top of file for what case 2 is.
    """
    query = context.session.query(models.IPAddress)
    period_start, period_end = billing.calc_periods()
    ip_list = billing.build_full_day_ips(query, period_start, period_end)
    import random
    ind = random.randint(0, len(ip_list) - 1)
    address = ip_list[ind]
    address.allocated_at = datetime.datetime.utcnow() -\
        datetime.timedelta(days=1)
    context.session.add(address)
    context.session.flush()

def make_case2 ( context ) : query = context . session . query ( models . IPAddress ) period_start , period_end = billing . calc_periods ( ) ip_list = billing . build_full_day_ips ( query , period_start , period_end ) import random ind = random . randint ( 0 , len ( ip_list ) - 1 ) address = ip_list [ ind ] address . allocated_at = datetime . datetime . utcnow ( ) - datetime . timedelta ( days = 1 ) context . session . add ( address ) context . session . flush ( )


def main(notify, hour, minute):
    """Runs billing report. Optionally sends notifications to billing"""

    # Read the config file and get the admin context
    config_opts = ['--config-file', '/etc/neutron/neutron.conf']
    config.init(config_opts)
    # Have to load the billing module _after_ config is parsed so
    # that we get the right network strategy
    network_strategy.STRATEGY.load()
    billing.PUBLIC_NETWORK_ID = network_strategy.STRATEGY.get_public_net_id()
    config.setup_logging()
    context = neutron_context.get_admin_context()

    # A query to get all IPAddress objects from the db
    query = context.session.query(models.IPAddress)

    (period_start, period_end) = billing.calc_periods(hour, minute)

    full_day_ips = billing.build_full_day_ips(query,
                                              period_start,
                                              period_end)
    partial_day_ips = billing.build_partial_day_ips(query,
                                                    period_start,
                                                    period_end)

    if notify:
        # '==================== Full Day ============================='
        for ipaddress in full_day_ips:
            click.echo('start: {}, end: {}'.format(period_start, period_end))
            payload = billing.build_payload(ipaddress,
                                            billing.IP_EXISTS,
                                            start_time=period_start,
                                            end_time=period_end)
            billing.do_notify(context,
                              billing.IP_EXISTS,
                              payload)
        # '==================== Part Day ============================='
        for ipaddress in partial_day_ips:
            click.echo('start: {}, end: {}'.format(period_start, period_end))
            payload = billing.build_payload(ipaddress,
                                            billing.IP_EXISTS,
                                            start_time=ipaddress.allocated_at,
                                            end_time=period_end)
            billing.do_notify(context,
                              billing.IP_EXISTS,
                              payload)
    else:
        click.echo('Case 1 ({}):\n'.format(len(full_day_ips)))
        for ipaddress in full_day_ips:
            pp(billing.build_payload(ipaddress,
                                     billing.IP_EXISTS,
                                     start_time=period_start,
                                     end_time=period_end))

        click.echo('\n===============================================\n')

        click.echo('Case 2 ({}):\n'.format(len(partial_day_ips)))
        for ipaddress in partial_day_ips:
            pp(billing.build_payload(ipaddress,
                                     billing.IP_EXISTS,
                                     start_time=ipaddress.allocated_at,
                                     end_time=period_end))

Runs billing report . Optionally sends notifications to billing


def start_rpc_listeners(self):
        """Configure all listeners here"""
        self._setup_rpc()
        if not self.endpoints:
            return []
        self.conn = n_rpc.create_connection()
        self.conn.create_consumer(self.topic, self.endpoints,
                                  fanout=False)
        return self.conn.consume_in_threads()

def start_rpc_listeners ( self ) : self . _setup_rpc ( ) if not self . endpoints : return [ ] self . conn = n_rpc . create_connection ( ) self . conn . create_consumer ( self . topic , self . endpoints , fanout = False ) return self . conn . consume_in_threads ( )


def context(self):
        """Provides an admin context for workers."""
        if not self._context:
            self._context = context.get_admin_context()
        return self._context

def context ( self ) : if not self . _context : self . _context = context . get_admin_context ( ) return self . _context


def update_sg(self, context, sg, rule_id, action):
        """Begins the async update process."""
        db_sg = db_api.security_group_find(context, id=sg, scope=db_api.ONE)
        if not db_sg:
            return None
        with context.session.begin():
            job_body = dict(action="%s sg rule %s" % (action, rule_id),
                            resource_id=rule_id,
                            tenant_id=db_sg['tenant_id'])
            job_body = dict(job=job_body)
            job = job_api.create_job(context.elevated(), job_body)
            rpc_client = QuarkSGAsyncProducerClient()
            try:
                rpc_client.populate_subtasks(context, sg, job['id'])
            except om_exc.MessagingTimeout:
                LOG.error("Failed to create subtasks. Rabbit running?")
                return None
        return {"job_id": job['id']}


def populate_subtasks(self, context, sg, parent_job_id):
        """Produces a list of ports to be updated async."""
        db_sg = db_api.security_group_find(context, id=sg, scope=db_api.ONE)
        if not db_sg:
            return None
        ports = db_api.sg_gather_associated_ports(context, db_sg)
        if len(ports) == 0:
            return {"ports": 0}
        for port in ports:
            job_body = dict(action="update port %s" % port['id'],
                            tenant_id=db_sg['tenant_id'],
                            resource_id=port['id'],
                            parent_id=parent_job_id)
            job_body = dict(job=job_body)
            job = job_api.create_job(context.elevated(), job_body)
            rpc_consumer = QuarkSGAsyncConsumerClient()
            try:
                rpc_consumer.update_port(context, port['id'], job['id'])
            except om_exc.MessagingTimeout:
                # TODO(roaet): Not too sure what can be done here other than
                # updating the job as a failure?
                LOG.error("Failed to update port. Rabbit running?")
        return None

Produces a list of ports to be updated async .


def update_ports_for_sg(self, context, portid, jobid):
        """Updates the ports through redis."""
        port = db_api.port_find(context, id=portid, scope=db_api.ONE)
        if not port:
            LOG.warning("Port not found")
            return
        net_driver = port_api._get_net_driver(port.network, port=port)
        base_net_driver = port_api._get_net_driver(port.network)
        sg_list = [sg for sg in port.security_groups]

        success = False
        error = None
        retries = 3
        retry_delay = 2
        for retry in xrange(retries):
            try:
                net_driver.update_port(context, port_id=port["backend_key"],
                                       mac_address=port["mac_address"],
                                       device_id=port["device_id"],
                                       base_net_driver=base_net_driver,
                                       security_groups=sg_list)
                success = True
                error = None
                break
            except Exception as error:
                LOG.warning("Could not connect to redis, but retrying soon")
                time.sleep(retry_delay)
        status_str = ""
        if not success:
            status_str = "Port %s update failed after %d tries. Error: %s" % (
                portid, retries, error)
        update_body = dict(completed=True, status=status_str)
        update_body = dict(job=update_body)
        job_api.update_job(context.elevated(), jobid, update_body)


def sg_gather_associated_ports(context, group):
    """Gather all ports associated to security group.

    Returns:
    * list, or None
    """
    if not group:
        return None
    if not hasattr(group, "ports") or len(group.ports) <= 0:
        return []
    return group.ports

def sg_gather_associated_ports ( context , group ) : if not group : return None if not hasattr ( group , "ports" ) or len ( group . ports ) <= 0 : return [ ] return group . ports

Gather all ports associated to security group .


def security_group_rule_update(context, rule, **kwargs):
    '''Updates a security group rule.

    NOTE(alexm) this is non-standard functionality.
    '''
    rule.update(kwargs)
    context.session.add(rule)
    return rule

def security_group_rule_update ( context , rule , * * kwargs ) : rule . update ( kwargs ) context . session . add ( rule ) return rule


def segment_allocation_find(context, lock_mode=False, **filters):
    """Query for segment allocations."""
    range_ids = filters.pop("segment_allocation_range_ids", None)

    query = context.session.query(models.SegmentAllocation)
    if lock_mode:
        query = query.with_lockmode("update")

    query = query.filter_by(**filters)

    # Optionally filter by given list of range ids
    if range_ids:
        query.filter(
            models.SegmentAllocation.segment_allocation_range_id.in_(
                range_ids))
    return query


def send(self, s):
        """
        Sends the given command to Niko Home Control and returns the output of
        the system.

        Aliases: write, put, sendall, send_all
        """
        self._socket.send(s.encode())
        return self.read()

def send ( self , s ) : self . _socket . send ( s . encode ( ) ) return self . read ( )

Sends the given command to Niko Home Control and returns the output of the system .


def if_(*args):
    """Implements the 'if' operator with support for multiple elseif-s."""
    for i in range(0, len(args) - 1, 2):
        if args[i]:
            return args[i + 1]
    if len(args) % 2:
        return args[-1]
    else:
        return None

def if_ ( * args ) : for i in range ( 0 , len ( args ) - 1 , 2 ) : if args [ i ] : return args [ i + 1 ] if len ( args ) % 2 : return args [ - 1 ] else : return None

Implements the if operator with support for multiple elseif - s .


def soft_equals(a, b):
    """Implements the '==' operator, which does type JS-style coertion."""
    if isinstance(a, str) or isinstance(b, str):
        return str(a) == str(b)
    if isinstance(a, bool) or isinstance(b, bool):
        return bool(a) is bool(b)
    return a == b

def soft_equals ( a , b ) : if isinstance ( a , str ) or isinstance ( b , str ) : return str ( a ) == str ( b ) if isinstance ( a , bool ) or isinstance ( b , bool ) : return bool ( a ) is bool ( b ) return a == b

Implements the == operator which does type JS - style coertion .


def less(a, b, *args):
    """Implements the '<' operator with JS-style type coertion."""
    types = set([type(a), type(b)])
    if float in types or int in types:
        try:
            a, b = float(a), float(b)
        except TypeError:
            # NaN
            return False
    return a < b and (not args or less(b, *args))

def
less
(
a
,
b
,
*
args
)
:
types
=
set
(
[
type
(
a
)
,
type
(
b
)
]
)
if
float
in
types
or
int
in
types
:
try
:
a
,
b
=
float
(
a
)
,
float
(
b
)
except
TypeError
:
# NaN
return
False
return
a
<
b
and
(
not
args
or
less
(
b
,
*
args
)
)

Implements the < operator with JS - style type coertion .


def less_or_equal(a, b, *args):
    """Implements the '<=' operator with JS-style type coertion."""
    return (
        less(a, b) or soft_equals(a, b)
    ) and (not args or less_or_equal(b, *args))

def less_or_equal ( a , b , * args ) : return ( less ( a , b ) or soft_equals ( a , b ) ) and ( not args or less_or_equal ( b , * args ) )

Implements the < = operator with JS - style type coertion .


def minus(*args):
    """Also, converts either to ints or to floats."""
    if len(args) == 1:
        return -to_numeric(args[0])
    return to_numeric(args[0]) - to_numeric(args[1])

def minus ( * args ) : if len ( args ) == 1 : return - to_numeric ( args [ 0 ] ) return to_numeric ( args [ 0 ] ) - to_numeric ( args [ 1 ] )

Also converts either to ints or to floats .


def merge(*args):
    """Implements the 'merge' operator for merging lists."""
    ret = []
    for arg in args:
        if isinstance(arg, list) or isinstance(arg, tuple):
            ret += list(arg)
        else:
            ret.append(arg)
    return ret

def merge ( * args ) : ret = [ ] for arg in args : if isinstance ( arg , list ) or isinstance ( arg , tuple ) : ret += list ( arg ) else : ret . append ( arg ) return ret

Implements the merge operator for merging lists .


def get_var(data, var_name, not_found=None):
    """Gets variable value from data dictionary."""
    try:
        for key in str(var_name).split('.'):
            try:
                data = data[key]
            except TypeError:
                data = data[int(key)]
    except (KeyError, TypeError, ValueError):
        return not_found
    else:
        return data

def get_var ( data , var_name , not_found = None ) : try : for key in str ( var_name ) . split ( '.' ) : try : data = data [ key ] except TypeError : data = data [ int ( key ) ] except ( KeyError , TypeError , ValueError ) : return not_found else : return data

Gets variable value from data dictionary .


def missing(data, *args):
    """Implements the missing operator for finding missing variables."""
    not_found = object()
    if args and isinstance(args[0], list):
        args = args[0]
    ret = []
    for arg in args:
        if get_var(data, arg, not_found) is not_found:
            ret.append(arg)
    return ret

def missing ( data , * args ) : not_found = object ( ) if args and isinstance ( args [ 0 ] , list ) : args = args [ 0 ] ret = [ ] for arg in args : if get_var ( data , arg , not_found ) is not_found : ret . append ( arg ) return ret

Implements the missing operator for finding missing variables .


def missing_some(data, min_required, args):
    """Implements the missing_some operator for finding missing variables."""
    if min_required < 1:
        return []
    found = 0
    not_found = object()
    ret = []
    for arg in args:
        if get_var(data, arg, not_found) is not_found:
            ret.append(arg)
        else:
            found += 1
            if found >= min_required:
                return []
    return ret

def missing_some ( data , min_required , args ) : if min_required < 1 : return [ ] found = 0 not_found = object ( ) ret = [ ] for arg in args : if get_var ( data , arg , not_found ) is not_found : ret . append ( arg ) else : found += 1 if found >= min_required : return [ ] return ret

Implements the missing_some operator for finding missing variables .


def jsonLogic(tests, data=None):
    """Executes the json-logic with given data."""
    # You've recursed to a primitive, stop!
    if tests is None or not isinstance(tests, dict):
        return tests

    data = data or {}

    operator = list(tests.keys())[0]
    values = tests[operator]

    # Easy syntax for unary operators, like {"var": "x"} instead of strict
    # {"var": ["x"]}
    if not isinstance(values, list) and not isinstance(values, tuple):
        values = [values]

    # Recursion!
    values = [jsonLogic(val, data) for val in values]

    if operator == 'var':
        return get_var(data, *values)
    if operator == 'missing':
        return missing(data, *values)
    if operator == 'missing_some':
        return missing_some(data, *values)

    if operator not in operations:
        raise ValueError("Unrecognized operation %s" % operator)

    return operations[operator](*values)

Executes the json - logic with given data .


def indent(self):
        """
        Performs an indentation
        """
        if not self.tab_always_indent:
            super(PyIndenterMode, self).indent()
        else:
            cursor = self.editor.textCursor()
            assert isinstance(cursor, QtGui.QTextCursor)
            if cursor.hasSelection():
                self.indent_selection(cursor)
            else:
                # simply insert indentation at the cursor position
                tab_len = self.editor.tab_length
                cursor.beginEditBlock()
                if self.editor.use_spaces_instead_of_tabs:
                    cursor.insertText(tab_len * " ")
                else:
                    cursor.insertText('\t')
                cursor.endEditBlock()
                self.editor.setTextCursor(cursor)


def unindent(self):
        """
        Performs an un-indentation
        """
        if self.tab_always_indent:
            cursor = self.editor.textCursor()
            if not cursor.hasSelection():
                cursor.select(cursor.LineUnderCursor)
            self.unindent_selection(cursor)
        else:
            super(PyIndenterMode, self).unindent()

def unindent ( self ) : if self . tab_always_indent : cursor = self . editor . textCursor ( ) if not cursor . hasSelection ( ) : cursor . select ( cursor . LineUnderCursor ) self . unindent_selection ( cursor ) else : super ( PyIndenterMode , self ) . unindent ( )


def _handle_indent_between_paren(self, column, line, parent_impl, tc):
        """
        Handle indent between symbols such as parenthesis, braces,...
        """
        pre, post = parent_impl
        next_char = self._get_next_char(tc)
        prev_char = self._get_prev_char(tc)
        prev_open = prev_char in ['[', '(', '{']
        next_close = next_char in [']', ')', '}']
        (open_line, open_symbol_col), (close_line, close_col) = \
            self._get_paren_pos(tc, column)
        open_line_txt = self._helper.line_text(open_line)
        open_line_indent = len(open_line_txt) - len(open_line_txt.lstrip())
        if prev_open:
            post = (open_line_indent + self.editor.tab_length) * ' '
        elif next_close and prev_char != ',':
            post = open_line_indent * ' '
        elif tc.block().blockNumber() == open_line:
            post = open_symbol_col * ' '

        # adapt indent if cursor on closing line and next line have same
        # indent -> PEP8 compliance
        if close_line and close_col:
            txt = self._helper.line_text(close_line)
            bn = tc.block().blockNumber()
            flg = bn == close_line
            next_indent = self._helper.line_indent(bn + 1) * ' '
            if flg and txt.strip().endswith(':') and next_indent == post:
                # | look at how the previous line ( ``':'):`` ) was
                # over-indented, this is actually what we are trying to
                # achieve here
                post += self.editor.tab_length * ' '

        # breaking string
        if next_char in ['"', "'"]:
            tc.movePosition(tc.Left)
        is_string = self._helper.is_comment_or_string(tc, formats=['string'])
        if next_char in ['"', "'"]:
            tc.movePosition(tc.Right)
        if is_string:
            trav = QTextCursor(tc)
            while self._helper.is_comment_or_string(
                    trav, formats=['string']):
                trav.movePosition(trav.Left)
            trav.movePosition(trav.Right)
            symbol = '%s' % self._get_next_char(trav)
            pre += symbol
            post += symbol

        return pre, post

Handle indent between symbols such as parenthesis braces ...


def _at_block_start(tc, line):
        """
        Improve QTextCursor.atBlockStart to ignore spaces
        """
        if tc.atBlockStart():
            return True
        column = tc.columnNumber()
        indentation = len(line) - len(line.lstrip())
        return column <= indentation

def _at_block_start ( tc , line ) : if tc . atBlockStart ( ) : return True column = tc . columnNumber ( ) indentation = len ( line ) - len ( line . lstrip ( ) ) return column <= indentation

Improve QTextCursor . atBlockStart to ignore spaces


def update_terminal_colors(self):
        """
        Update terminal color scheme based on the pygments color scheme colors
        """
        self.color_scheme = self.create_color_scheme(
            background=self.syntax_highlighter.color_scheme.background,
            foreground=self.syntax_highlighter.color_scheme.formats['normal'].foreground().color())

def update_terminal_colors ( self ) : self . color_scheme = self . create_color_scheme ( background = self . syntax_highlighter . color_scheme . background , foreground = self . syntax_highlighter . color_scheme . formats [ 'normal' ] . foreground ( ) . color ( ) )


def mouseMoveEvent(self, e):
        """
        Extends mouseMoveEvent to display a pointing hand cursor when the
        mouse cursor is over a file location
        """
        super(PyInteractiveConsole, self).mouseMoveEvent(e)
        cursor = self.cursorForPosition(e.pos())
        assert isinstance(cursor, QtGui.QTextCursor)
        p = cursor.positionInBlock()
        usd = cursor.block().userData()
        if usd and usd.start_pos_in_block <= p <= usd.end_pos_in_block:
            if QtWidgets.QApplication.overrideCursor() is None:
                QtWidgets.QApplication.setOverrideCursor(
                    QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        else:
            if QtWidgets.QApplication.overrideCursor() is not None:
                QtWidgets.QApplication.restoreOverrideCursor()

def mouseMoveEvent ( self , e ) : super ( PyInteractiveConsole , self ) . mouseMoveEvent ( e ) cursor = self . cursorForPosition ( e . pos ( ) ) assert isinstance ( cursor , QtGui . QTextCursor ) p = cursor . positionInBlock ( ) usd = cursor . block ( ) . userData ( ) if usd and usd . start_pos_in_block <= p <= usd . end_pos_in_block : if QtWidgets . QApplication . overrideCursor ( ) is None : QtWidgets . QApplication . setOverrideCursor ( QtGui . QCursor ( QtCore . Qt . PointingHandCursor ) ) else : if QtWidgets . QApplication . overrideCursor ( ) is not None : QtWidgets . QApplication . restoreOverrideCursor ( )


def mousePressEvent(self, e):
        """
        Emits open_file_requested if the press event occured  over
        a file location string.
        """
        super(PyInteractiveConsole, self).mousePressEvent(e)
        cursor = self.cursorForPosition(e.pos())
        p = cursor.positionInBlock()
        usd = cursor.block().userData()
        if usd and usd.start_pos_in_block <= p <= usd.end_pos_in_block:
            if e.button() == QtCore.Qt.LeftButton:
                self.open_file_requested.emit(usd.filename, usd.line)

def mousePressEvent ( self , e ) : super ( PyInteractiveConsole , self ) . mousePressEvent ( e ) cursor = self . cursorForPosition ( e . pos ( ) ) p = cursor . positionInBlock ( ) usd = cursor . block ( ) . userData ( ) if usd and usd . start_pos_in_block <= p <= usd . end_pos_in_block : if e . button ( ) == QtCore . Qt . LeftButton : self . open_file_requested . emit ( usd . filename , usd . line )

Emits open_file_requested if the press event occured over a file location string .


def setup_actions(self):
        """ Connects slots to signals """
        self.actionOpen.triggered.connect(self.on_open)
        self.actionNew.triggered.connect(self.on_new)
        self.actionSave.triggered.connect(self.on_save)
        self.actionSave_as.triggered.connect(self.on_save_as)
        self.actionQuit.triggered.connect(
            QtWidgets.QApplication.instance().quit)
        self.tabWidget.current_changed.connect(self.on_current_tab_changed)
        self.tabWidget.last_tab_closed.connect(self.on_last_tab_closed)
        self.actionAbout.triggered.connect(self.on_about)
        self.actionRun.triggered.connect(self.on_run)
        self.interactiveConsole.process_finished.connect(
            self.on_process_finished)
        self.actionConfigure_run.triggered.connect(self.on_configure_run)

def setup_actions ( self ) : self . actionOpen . triggered . connect ( self . on_open ) self . actionNew . triggered . connect ( self . on_new ) self . actionSave . triggered . connect ( self . on_save ) self . actionSave_as . triggered . connect ( self . on_save_as ) self . actionQuit . triggered . connect ( QtWidgets . QApplication . instance ( ) . quit ) self . tabWidget . current_changed . connect ( self . on_current_tab_changed ) self . tabWidget . last_tab_closed . connect ( self . on_last_tab_closed ) self . actionAbout . triggered . connect ( self . on_about ) self . actionRun . triggered . connect ( self . on_run ) self . interactiveConsole . process_finished . connect ( self . on_process_finished ) self . actionConfigure_run . triggered . connect ( self . on_configure_run )


def setup_editor(self, editor):
        """
        Setup the python editor, run the server and connect a few signals.

        :param editor: editor to setup.
        """
        editor.cursorPositionChanged.connect(self.on_cursor_pos_changed)
        try:
            m = editor.modes.get(modes.GoToAssignmentsMode)
        except KeyError:
            pass
        else:
            assert isinstance(m, modes.GoToAssignmentsMode)
            m.out_of_doc.connect(self.on_goto_out_of_doc)

def setup_editor ( self , editor ) : editor . cursorPositionChanged . connect ( self . on_cursor_pos_changed ) try : m = editor . modes . get ( modes . GoToAssignmentsMode ) except KeyError : pass else : assert isinstance ( m , modes . GoToAssignmentsMode ) m . out_of_doc . connect ( self . on_goto_out_of_doc )

Setup the python editor run the server and connect a few signals .


def open_file(self, path, line=None):
        """
        Creates a new GenericCodeEdit, opens the requested file and adds it
        to the tab widget.

        :param path: Path of the file to open

        :return The opened editor if open succeeded.
        """
        editor = None
        if path:
            interpreter, pyserver, args = self._get_backend_parameters()
            editor = self.tabWidget.open_document(
                path, None, interpreter=interpreter, server_script=pyserver,
                args=args)
            if editor:
                self.setup_editor(editor)
            self.recent_files_manager.open_file(path)
            self.menu_recents.update_actions()
        if line is not None:
            TextHelper(self.tabWidget.current_widget()).goto_line(line)
        return editor

def open_file ( self , path , line = None ) : editor = None if path : interpreter , pyserver , args = self . _get_backend_parameters ( ) editor = self . tabWidget . open_document ( path , None , interpreter = interpreter , server_script = pyserver , args = args ) if editor : self . setup_editor ( editor ) self . recent_files_manager . open_file ( path ) self . menu_recents . update_actions ( ) if line is not None : TextHelper ( self . tabWidget . current_widget ( ) ) . goto_line ( line ) return editor

Creates a new GenericCodeEdit opens the requested file and adds it to the tab widget .


def on_new(self):
        """
        Add a new empty code editor to the tab widget
        """
        interpreter, pyserver, args = self._get_backend_parameters()
        self.setup_editor(self.tabWidget.create_new_document(
            extension='.py', interpreter=interpreter, server_script=pyserver,
            args=args))
        self.actionRun.setDisabled(True)
        self.actionConfigure_run.setDisabled(True)

def on_new ( self ) : interpreter , pyserver , args = self . _get_backend_parameters ( ) self . setup_editor ( self . tabWidget . create_new_document ( extension = '.py' , interpreter = interpreter , server_script = pyserver , args = args ) ) self . actionRun . setDisabled ( True ) self . actionConfigure_run . setDisabled ( True )


def on_open(self):
        """
        Shows an open file dialog and open the file if the dialog was
        accepted.

        """
        filename, filter = QtWidgets.QFileDialog.getOpenFileName(self, 'Open')
        if filename:
            self.open_file(filename)
        self.actionRun.setEnabled(True)
        self.actionConfigure_run.setEnabled(True)

def on_open ( self ) : filename , filter = QtWidgets . QFileDialog . getOpenFileName ( self , 'Open' ) if filename : self . open_file ( filename ) self . actionRun . setEnabled ( True ) self . actionConfigure_run . setEnabled ( True )

Shows an open file dialog and open the file if the dialog was accepted .


def on_save_as(self):
        """
        Save the current editor document as.
        """
        path = self.tabWidget.current_widget().file.path
        path = os.path.dirname(path) if path else ''
        filename, filter = QtWidgets.QFileDialog.getSaveFileName(
            self, 'Save', path)
        if filename:
            self.tabWidget.save_current(filename)
            self.recent_files_manager.open_file(filename)
            self.menu_recents.update_actions()
            self.actionRun.setEnabled(True)
            self.actionConfigure_run.setEnabled(True)
            self._update_status_bar(self.tabWidget.current_widget())

def on_save_as ( self ) : path = self . tabWidget . current_widget ( ) . file . path path = os . path . dirname ( path ) if path else '' filename , filter = QtWidgets . QFileDialog . getSaveFileName ( self , 'Save' , path ) if filename : self . tabWidget . save_current ( filename ) self . recent_files_manager . open_file ( filename ) self . menu_recents . update_actions ( ) self . actionRun . setEnabled ( True ) self . actionConfigure_run . setEnabled ( True ) self . _update_status_bar ( self . tabWidget . current_widget ( ) )


def setup_mnu_style(self, editor):
        """ setup the style menu for an editor tab """
        menu = QtWidgets.QMenu('Styles', self.menuEdit)
        group = QtWidgets.QActionGroup(self)
        self.styles_group = group
        current_style = editor.syntax_highlighter.color_scheme.name
        group.triggered.connect(self.on_style_changed)
        for s in sorted(PYGMENTS_STYLES):
            a = QtWidgets.QAction(menu)
            a.setText(s)
            a.setCheckable(True)
            if s == current_style:
                a.setChecked(True)
            group.addAction(a)
            menu.addAction(a)
        self.menuEdit.addMenu(menu)

def setup_mnu_style ( self , editor ) : menu = QtWidgets . QMenu ( 'Styles' , self . menuEdit ) group = QtWidgets . QActionGroup ( self ) self . styles_group = group current_style = editor . syntax_highlighter . color_scheme . name group . triggered . connect ( self . on_style_changed ) for s in sorted ( PYGMENTS_STYLES ) : a = QtWidgets . QAction ( menu ) a . setText ( s ) a . setCheckable ( True ) if s == current_style : a . setChecked ( True ) group . addAction ( a ) menu . addAction ( a ) self . menuEdit . addMenu ( menu )


def on_current_tab_changed(self):
        """
        Update action states when the current tab changed.
        """
        self.menuEdit.clear()
        self.menuModes.clear()
        self.menuPanels.clear()
        editor = self.tabWidget.current_widget()
        self.menuEdit.setEnabled(editor is not None)
        self.menuModes.setEnabled(editor is not None)
        self.menuPanels.setEnabled(editor is not None)
        self.actionSave.setEnabled(editor is not None)
        self.actionSave_as.setEnabled(editor is not None)
        self.actionConfigure_run.setEnabled(editor is not None)
        self.actionRun.setEnabled(editor is not None)
        if editor is not None:
            self.setup_mnu_edit(editor)
            self.setup_mnu_modes(editor)
            self.setup_mnu_panels(editor)
        self.widgetOutline.set_editor(editor)
        self._update_status_bar(editor)

def on_current_tab_changed ( self ) : self . menuEdit . clear ( ) self . menuModes . clear ( ) self . menuPanels . clear ( ) editor = self . tabWidget . current_widget ( ) self . menuEdit . setEnabled ( editor is not None ) self . menuModes . setEnabled ( editor is not None ) self . menuPanels . setEnabled ( editor is not None ) self . actionSave . setEnabled ( editor is not None ) self . actionSave_as . setEnabled ( editor is not None ) self . actionConfigure_run . setEnabled ( editor is not None ) self . actionRun . setEnabled ( editor is not None ) if editor is not None : self . setup_mnu_edit ( editor ) self . setup_mnu_modes ( editor ) self . setup_mnu_panels ( editor ) self . widgetOutline . set_editor ( editor ) self . _update_status_bar ( editor )

Update action states when the current tab changed .