def strftime(fmt, t):
	"""A class to replace the strftime in datetime package or time module.
	Identical to strftime behavior in those modules except supports any
	year.
	Also supports datetime.datetime times.
	Also supports milliseconds using %s
	Also supports microseconds using %u"""
	if isinstance(t, (time.struct_time, tuple)):
		t = datetime.datetime(*t[:6])
	assert isinstance(t, (datetime.datetime, datetime.time, datetime.date))
	try:
		year = t.year
		if year < 1900:
			t = t.replace(year=1900)
	except AttributeError:
		year = 1900
	subs = (
		('%Y', '%04d' % year),
		('%y', '%02d' % (year % 100)),
		('%s', '%03d' % (t.microsecond // 1000)),
		('%u', '%03d' % (t.microsecond % 1000))
	)

	def doSub(s, sub):
		return s.replace(*sub)

	def doSubs(s):
		return functools.reduce(doSub, subs, s)

	fmt = '%%'.join(map(doSubs, fmt.split('%%')))
	return t.strftime(fmt)

def strftime ( fmt , t ) : if isinstance ( t , ( time . struct_time , tuple ) ) : t = datetime . datetime ( * t [ : 6 ] ) assert isinstance ( t , ( datetime . datetime , datetime . time , datetime . date ) ) try : year = t . year if year < 1900 : t = t . replace ( year = 1900 ) except AttributeError : year = 1900 subs = ( ( '%Y' , '%04d' % year ) , ( '%y' , '%02d' % ( year % 100 ) ) , ( '%s' , '%03d' % ( t . microsecond // 1000 ) ) , ( '%u' , '%03d' % ( t . microsecond % 1000 ) ) ) def doSub ( s , sub ) : return s . replace ( * sub ) def doSubs ( s ) : return functools . reduce ( doSub , subs , s ) fmt = '%%' . join ( map ( doSubs , fmt . split ( '%%' ) ) ) return t . strftime ( fmt )

A class to replace the strftime in datetime package or time module . Identical to strftime behavior in those modules except supports any year . Also supports datetime . datetime times . Also supports milliseconds using %s Also supports microseconds using %u


def strptime(s, fmt, tzinfo=None):
	"""
	A function to replace strptime in the time module.  Should behave
	identically to the strptime function except it returns a datetime.datetime
	object instead of a time.struct_time object.
	Also takes an optional tzinfo parameter which is a time zone info object.
	"""
	res = time.strptime(s, fmt)
	return datetime.datetime(tzinfo=tzinfo, *res[:6])

def strptime ( s , fmt , tzinfo = None ) : res = time . strptime ( s , fmt ) return datetime . datetime ( tzinfo = tzinfo , * res [ : 6 ] )

A function to replace strptime in the time module . Should behave identically to the strptime function except it returns a datetime . datetime object instead of a time . struct_time object . Also takes an optional tzinfo parameter which is a time zone info object .


def get_nearest_year_for_day(day):
	"""
	Returns the nearest year to now inferred from a Julian date.
	"""
	now = time.gmtime()
	result = now.tm_year
	# if the day is far greater than today, it must be from last year
	if day - now.tm_yday > 365 // 2:
		result -= 1
	# if the day is far less than today, it must be for next year.
	if now.tm_yday - day > 365 // 2:
		result += 1
	return result

def get_nearest_year_for_day ( day ) : now = time . gmtime ( ) result = now . tm_year if day - now . tm_yday > 365 // 2 : result -= 1 if now . tm_yday - day > 365 // 2 : result += 1 return result

Returns the nearest year to now inferred from a Julian date .


def get_period_seconds(period):
	"""
	return the number of seconds in the specified period

	>>> get_period_seconds('day')
	86400
	>>> get_period_seconds(86400)
	86400
	>>> get_period_seconds(datetime.timedelta(hours=24))
	86400
	>>> get_period_seconds('day + os.system("rm -Rf *")')
	Traceback (most recent call last):
	...
	ValueError: period not in (second, minute, hour, day, month, year)
	"""
	if isinstance(period, six.string_types):
		try:
			name = 'seconds_per_' + period.lower()
			result = globals()[name]
		except KeyError:
			msg = "period not in (second, minute, hour, day, month, year)"
			raise ValueError(msg)
	elif isinstance(period, numbers.Number):
		result = period
	elif isinstance(period, datetime.timedelta):
		result = period.days * get_period_seconds('day') + period.seconds
	else:
		raise TypeError('period must be a string or integer')
	return result


def divide_timedelta_float(td, divisor):
	"""
	Divide a timedelta by a float value

	>>> one_day = datetime.timedelta(days=1)
	>>> half_day = datetime.timedelta(days=.5)
	>>> divide_timedelta_float(one_day, 2.0) == half_day
	True
	>>> divide_timedelta_float(one_day, 2) == half_day
	True
	"""
	# td is comprised of days, seconds, microseconds
	dsm = [getattr(td, attr) for attr in ('days', 'seconds', 'microseconds')]
	dsm = map(lambda elem: elem / divisor, dsm)
	return datetime.timedelta(*dsm)

def divide_timedelta_float ( td , divisor ) : dsm = [ getattr ( td , attr ) for attr in ( 'days' , 'seconds' , 'microseconds' ) ] dsm = map ( lambda elem : elem / divisor , dsm ) return datetime . timedelta ( * dsm )


def parse_timedelta(str):
	"""
	Take a string representing a span of time and parse it to a time delta.
	Accepts any string of comma-separated numbers each with a unit indicator.

	>>> parse_timedelta('1 day')
	datetime.timedelta(days=1)

	>>> parse_timedelta('1 day, 30 seconds')
	datetime.timedelta(days=1, seconds=30)

	>>> parse_timedelta('47.32 days, 20 minutes, 15.4 milliseconds')
	datetime.timedelta(days=47, seconds=28848, microseconds=15400)

	Supports weeks, months, years

	>>> parse_timedelta('1 week')
	datetime.timedelta(days=7)

	>>> parse_timedelta('1 year, 1 month')
	datetime.timedelta(days=395, seconds=58685)

	Note that months and years strict intervals, not aligned
	to a calendar:

	>>> now = datetime.datetime.now()
	>>> later = now + parse_timedelta('1 year')
	>>> diff = later.replace(year=now.year) - now
	>>> diff.seconds
	20940
	"""
	deltas = (_parse_timedelta_part(part.strip()) for part in str.split(','))
	return sum(deltas, datetime.timedelta())

def parse_timedelta ( str ) : deltas = ( _parse_timedelta_part ( part . strip ( ) ) for part in str . split ( ',' ) ) return sum ( deltas , datetime . timedelta ( ) )

Take a string representing a span of time and parse it to a time delta . Accepts any string of comma - separated numbers each with a unit indicator .


def divide_timedelta(td1, td2):
	"""
	Get the ratio of two timedeltas

	>>> one_day = datetime.timedelta(days=1)
	>>> one_hour = datetime.timedelta(hours=1)
	>>> divide_timedelta(one_hour, one_day) == 1 / 24
	True
	"""
	try:
		return td1 / td2
	except TypeError:
		# Python 3.2 gets division
		# http://bugs.python.org/issue2706
		return td1.total_seconds() / td2.total_seconds()

def divide_timedelta ( td1 , td2 ) : try : return td1 / td2 except TypeError : return td1 . total_seconds ( ) / td2 . total_seconds ( )


def date_range(start=None, stop=None, step=None):
	"""
	Much like the built-in function range, but works with dates

	>>> range_items = date_range(
	...     datetime.datetime(2005,12,21),
	...     datetime.datetime(2005,12,25),
	... )
	>>> my_range = tuple(range_items)
	>>> datetime.datetime(2005,12,21) in my_range
	True
	>>> datetime.datetime(2005,12,22) in my_range
	True
	>>> datetime.datetime(2005,12,25) in my_range
	False
	"""
	if step is None:
		step = datetime.timedelta(days=1)
	if start is None:
		start = datetime.datetime.now()
	while start < stop:
		yield start
		start += step

def date_range ( start = None , stop = None , step = None ) : if step is None : step = datetime . timedelta ( days = 1 ) if start is None : start = datetime . datetime . now ( ) while start < stop : yield start start += step

Much like the built - in function range but works with dates


def construct_datetime(cls, *args, **kwargs):
		"""Construct a datetime.datetime from a number of different time
		types found in python and pythonwin"""
		if len(args) == 1:
			arg = args[0]
			method = cls.__get_dt_constructor(
				type(arg).__module__,
				type(arg).__name__,
			)
			result = method(arg)
			try:
				result = result.replace(tzinfo=kwargs.pop('tzinfo'))
			except KeyError:
				pass
			if kwargs:
				first_key = kwargs.keys()[0]
				tmpl = (
					"{first_key} is an invalid keyword "
					"argument for this function."
				)
				raise TypeError(tmpl.format(**locals()))
		else:
			result = datetime.datetime(*args, **kwargs)
		return result

def
construct_datetime
(
cls
,
*
args
,
**
kwargs
)
:
if
len
(
args
)
==
1
:
arg
=
args
[
0
]
method
=
cls
.
__get_dt_constructor
(
type
(
arg
)
.
__module__
,
type
(
arg
)
.
__name__
,
)
result
=
method
(
arg
)
try
:
result
=
result
.
replace
(
tzinfo
=
kwargs
.
pop
(
'tzinfo'
)
)
except
KeyError
:
pass
if
kwargs
:
first_key
=
kwargs
.
keys
(
)
[
0
]
tmpl
=
(
"{first_key} is an invalid keyword "
"argument for this function."
)
raise
TypeError
(
tmpl
.
format
(
**
locals
(
)
)
)
else
:
result
=
datetime
.
datetime
(
*
args
,
**
kwargs
)
return
result

Construct a datetime . datetime from a number of different time types found in python and pythonwin


def get(self, query, responseformat="geojson", verbosity="body", build=True):
        """Pass in an Overpass query in Overpass QL."""
        # Construct full Overpass query
        if build:
            full_query = self._construct_ql_query(
                query, responseformat=responseformat, verbosity=verbosity
            )
        else:
            full_query = query

        if self.debug:
            logging.getLogger().info(query)

        # Get the response from Overpass
        r = self._get_from_overpass(full_query)
        content_type = r.headers.get("content-type")

        if self.debug:
            print(content_type)
        if content_type == "text/csv":
            result = []
            reader = csv.reader(StringIO(r.text), delimiter="\t")
            for row in reader:
                result.append(row)
            return result
        elif content_type in ("text/xml", "application/xml", "application/osm3s+xml"):
            return r.text
        elif content_type == "application/json":
            response = json.loads(r.text)

        if not build:
            return response

        # Check for valid answer from Overpass.
        # A valid answer contains an 'elements' key at the root level.
        if "elements" not in response:
            raise UnknownOverpassError("Received an invalid answer from Overpass.")

        # If there is a 'remark' key, it spells trouble.
        overpass_remark = response.get("remark", None)
        if overpass_remark and overpass_remark.startswith("runtime error"):
            raise ServerRuntimeError(overpass_remark)

        if responseformat is not "geojson":
            return response

        # construct geojson
        return self._as_geojson(response["elements"])

Pass in an Overpass query in Overpass QL .


def get_ports_count(context, filters=None):
    """Return the number of ports.

    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 port as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictionary
        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_ports_count for tenant %s filters %s" %
             (context.tenant_id, filters))
    return db_api.port_count_all(context, join_security_groups=True, **filters)


def _allocate_from_v6_subnet(self, context, net_id, subnet,
                                 port_id, reuse_after, ip_address=None,
                                 **kwargs):
        """This attempts to allocate v6 addresses as per RFC2462 and RFC3041.

        To accomodate this, we effectively treat all v6 assignment as a
        first time allocation utilizing the MAC address of the VIF. Because
        we recycle MACs, we will eventually attempt to recreate a previously
        generated v6 address. Instead of failing, we've opted to handle
        reallocating that address in this method.

        This should provide a performance boost over attempting to check
        each and every subnet in the existing reallocate logic, as we'd
        have to iterate over each and every subnet returned
        """

        LOG.info("Attempting to allocate a v6 address - [{0}]".format(
            utils.pretty_kwargs(network_id=net_id, subnet=subnet,
                                port_id=port_id, ip_address=ip_address)))

        if ip_address:
            LOG.info("IP %s explicitly requested, deferring to standard "
                     "allocation" % ip_address)
            return self._allocate_from_subnet(context, net_id=net_id,
                                              subnet=subnet, port_id=port_id,
                                              reuse_after=reuse_after,
                                              ip_address=ip_address, **kwargs)
        else:
            mac = kwargs.get("mac_address")
            if mac:
                mac = kwargs["mac_address"].get("address")

            if subnet and subnet["ip_policy"]:
                ip_policy_cidrs = subnet["ip_policy"].get_cidrs_ip_set()
            else:
                ip_policy_cidrs = netaddr.IPSet([])

            for tries, ip_address in enumerate(
                    generate_v6(mac, port_id, subnet["cidr"])):

                LOG.info("Attempt {0} of {1}".format(
                    tries + 1, CONF.QUARK.v6_allocation_attempts))

                if tries > CONF.QUARK.v6_allocation_attempts - 1:
                    LOG.info("Exceeded v6 allocation attempts, bailing")
                    raise ip_address_failure(net_id)

                ip_address = netaddr.IPAddress(ip_address).ipv6()
                LOG.info("Generated a new v6 address {0}".format(
                    str(ip_address)))

                if (ip_policy_cidrs is not None and
                        ip_address in ip_policy_cidrs):
                    LOG.info("Address {0} excluded by policy".format(
                        str(ip_address)))
                    continue

                try:
                    with context.session.begin():
                        address = db_api.ip_address_create(
                            context, address=ip_address,
                            subnet_id=subnet["id"],
                            version=subnet["ip_version"], network_id=net_id,
                            address_type=kwargs.get('address_type',
                                                    ip_types.FIXED))
                        return address
                except db_exception.DBDuplicateEntry:
                    # This shouldn't ever happen, since we hold a unique MAC
                    # address from the previous IPAM step.
                    LOG.info("{0} exists but was already "
                             "allocated".format(str(ip_address)))
                    LOG.debug("Duplicate entry found when inserting subnet_id"
                              " %s ip_address %s", subnet["id"], ip_address)

This attempts to allocate v6 addresses as per RFC2462 and RFC3041 .


def _create_flip(context, flip, port_fixed_ips):
    """Associates the flip with ports and creates it with the flip driver

    :param context: neutron api request context.
    :param flip: quark.db.models.IPAddress object representing a floating IP
    :param port_fixed_ips: dictionary of the structure:
    {"<id of port>": {"port": <quark.db.models.Port>,
     "fixed_ip": "<fixed ip address>"}}
    :return: None
    """
    if port_fixed_ips:
        context.session.begin()
        try:
            ports = [val['port'] for val in port_fixed_ips.values()]
            flip = db_api.port_associate_ip(context, ports, flip,
                                            port_fixed_ips.keys())

            for port_id in port_fixed_ips:
                fixed_ip = port_fixed_ips[port_id]['fixed_ip']
                flip = db_api.floating_ip_associate_fixed_ip(context, flip,
                                                             fixed_ip)

            flip_driver = registry.DRIVER_REGISTRY.get_driver()

            flip_driver.register_floating_ip(flip, port_fixed_ips)
            context.session.commit()
        except Exception:
            context.session.rollback()
            raise

    # alexm: Notify from this method for consistency with _delete_flip
    billing.notify(context, billing.IP_ASSOC, flip)

def _create_flip ( context , flip , port_fixed_ips ) : if port_fixed_ips : context . session . begin ( ) try : ports = [ val [ 'port' ] for val in port_fixed_ips . values ( ) ] flip = db_api . port_associate_ip ( context , ports , flip , port_fixed_ips . keys ( ) ) for port_id in port_fixed_ips : fixed_ip = port_fixed_ips [ port_id ] [ 'fixed_ip' ] flip = db_api . floating_ip_associate_fixed_ip ( context , flip , fixed_ip ) flip_driver = registry . DRIVER_REGISTRY . get_driver ( ) flip_driver . register_floating_ip ( flip , port_fixed_ips ) context . session . commit ( ) except Exception : context . session . rollback ( ) raise billing . notify ( context , billing . IP_ASSOC , flip )


def create_floatingip(context, content):
    """Allocate or reallocate a floating IP.

    :param context: neutron api request context.
    :param content: dictionary describing the floating ip, with keys
        as listed in the RESOURCE_ATTRIBUTE_MAP object in
        neutron/api/v2/attributes.py.  All keys will be populated.

    :returns: Dictionary containing details for the new floating IP.  If values
        are declared in the fields parameter, then only those keys will be
        present.
    """
    LOG.info('create_floatingip %s for tenant %s and body %s' %
             (id, context.tenant_id, content))
    network_id = content.get('floating_network_id')
    # TODO(blogan): Since the extension logic will reject any requests without
    # floating_network_id, is this still needed?
    if not network_id:
        raise n_exc.BadRequest(resource='floating_ip',
                               msg='floating_network_id is required.')
    fixed_ip_address = content.get('fixed_ip_address')
    ip_address = content.get('floating_ip_address')
    port_id = content.get('port_id')
    port = None
    port_fixed_ip = {}

    network = _get_network(context, network_id)
    if port_id:
        port = _get_port(context, port_id)
        fixed_ip = _get_fixed_ip(context, fixed_ip_address, port)
        port_fixed_ip = {port.id: {'port': port, 'fixed_ip': fixed_ip}}
    flip = _allocate_ip(context, network, port, ip_address, ip_types.FLOATING)
    _create_flip(context, flip, port_fixed_ip)
    return v._make_floating_ip_dict(flip, port_id)


def update_floatingip(context, id, content):
    """Update an existing floating IP.

    :param context: neutron api request context.
    :param id: id of the floating ip
    :param content: 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.

    :returns: Dictionary containing details for the new floating IP.  If values
        are declared in the fields parameter, then only those keys will be
        present.
    """

    LOG.info('update_floatingip %s for tenant %s and body %s' %
             (id, context.tenant_id, content))

    if 'port_id' not in content:
        raise n_exc.BadRequest(resource='floating_ip',
                               msg='port_id is required.')

    requested_ports = []
    if content.get('port_id'):
        requested_ports = [{'port_id': content.get('port_id')}]
    flip = _update_flip(context, id, ip_types.FLOATING, requested_ports)
    return v._make_floating_ip_dict(flip)


def delete_floatingip(context, id):
    """deallocate a floating IP.

    :param context: neutron api request context.
    :param id: id of the floating ip
    """

    LOG.info('delete_floatingip %s for tenant %s' % (id, context.tenant_id))

    _delete_flip(context, id, ip_types.FLOATING)

def
delete_floatingip
(
context
,
id
)
:
LOG
.
info
(
'delete_floatingip %s for tenant %s'
%
(
id
,
context
.
tenant_id
)
)
_delete_flip
(
context
,
id
,
ip_types
.
FLOATING
)


def get_floatingip(context, id, fields=None):
    """Retrieve a floating IP.

    :param context: neutron api request context.
    :param id: The UUID of the floating IP.
    :param fields: a list of strings that are valid keys in a
        floating IP dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.

    :returns: Dictionary containing details for the floating IP.  If values
        are declared in the fields parameter, then only those keys will be
        present.
    """
    LOG.info('get_floatingip %s for tenant %s' % (id, context.tenant_id))

    filters = {'address_type': ip_types.FLOATING, '_deallocated': False}

    floating_ip = db_api.floating_ip_find(context, id=id, scope=db_api.ONE,
                                          **filters)

    if not floating_ip:
        raise q_exc.FloatingIpNotFound(id=id)

    return v._make_floating_ip_dict(floating_ip)

def
get_floatingip
(
context
,
id
,
fields
=
)
:
LOG
.
info
(
'get_floatingip %s for tenant %s'
%
(
id
,
context
.
tenant_id
)
)
filters
=
{
'address_type'
:
ip_types
.
FLOATING
,
'_deallocated'
:
False
}
floating_ip
=
db_api
.
floating_ip_find
(
context
,
id
=
id
,
scope
=
db_api
.
ONE
,
**
filters
)
if
not
floating_ip
:
raise
q_exc
.
FloatingIpNotFound
(
id
=
id
)
return
v
.
_make_floating_ip_dict
(
floating_ip
)


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

    :param context: neutron api request context.
    :param filters: a dictionary with keys that are valid keys for
        a floating ip as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictionary
        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
        floating IP dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.

    :returns: List of floating IPs that are accessible to the tenant who
        submits the request (as indicated by the tenant id of the context)
        as well as any filters.
    """
    LOG.info('get_floatingips for tenant %s filters %s fields %s' %
             (context.tenant_id, filters, fields))

    floating_ips = _get_ips_by_type(context, ip_types.FLOATING,
                                    filters=filters, fields=fields)

    return [v._make_floating_ip_dict(flip) for flip in floating_ips]


def get_floatingips_count(context, filters=None):
    """Return the number of floating IPs.

    :param context: neutron api request context
    :param filters: a dictionary with keys that are valid keys for
        a floating IP as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictionary
        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.

    :returns: The number of floating IPs that are accessible to the tenant who
        submits the request (as indicated by the tenant id of the context)
        as well as any filters.

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

    if filters is None:
        filters = {}

    filters['_deallocated'] = False
    filters['address_type'] = ip_types.FLOATING
    count = db_api.ip_address_count_all(context, filters)

    LOG.info('Found %s floating ips for tenant %s' % (count,
                                                      context.tenant_id))
    return count


def create_scalingip(context, content):
    """Allocate or reallocate a scaling IP.

    :param context: neutron api request context.
    :param content: dictionary describing the scaling ip, with keys
        as listed in the RESOURCE_ATTRIBUTE_MAP object in
        neutron/api/v2/attributes.py.  All keys will be populated.

    :returns: Dictionary containing details for the new scaling IP.  If values
        are declared in the fields parameter, then only those keys will be
        present.
    """
    LOG.info('create_scalingip for tenant %s and body %s',
             context.tenant_id, content)
    network_id = content.get('scaling_network_id')
    ip_address = content.get('scaling_ip_address')
    requested_ports = content.get('ports', [])

    network = _get_network(context, network_id)
    port_fixed_ips = {}
    for req_port in requested_ports:
        port = _get_port(context, req_port['port_id'])
        fixed_ip = _get_fixed_ip(context, req_port.get('fixed_ip_address'),
                                 port)
        port_fixed_ips[port.id] = {"port": port, "fixed_ip": fixed_ip}
    scip = _allocate_ip(context, network, None, ip_address, ip_types.SCALING)
    _create_flip(context, scip, port_fixed_ips)
    return v._make_scaling_ip_dict(scip)


def update_scalingip(context, id, content):
    """Update an existing scaling IP.

    :param context: neutron api request context.
    :param id: id of the scaling ip
    :param content: 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.

    :returns: Dictionary containing details for the new scaling IP.  If values
        are declared in the fields parameter, then only those keys will be
        present.
    """
    LOG.info('update_scalingip %s for tenant %s and body %s' %
             (id, context.tenant_id, content))
    requested_ports = content.get('ports', [])
    flip = _update_flip(context, id, ip_types.SCALING, requested_ports)
    return v._make_scaling_ip_dict(flip)


def delete_scalingip(context, id):
    """Deallocate a scaling IP.

    :param context: neutron api request context.
    :param id: id of the scaling ip
    """
    LOG.info('delete_scalingip %s for tenant %s' % (id, context.tenant_id))
    _delete_flip(context, id, ip_types.SCALING)

def
delete_scalingip
(
context
,
id
)
:
LOG
.
info
(
'delete_scalingip %s for tenant %s'
%
(
id
,
context
.
tenant_id
)
)
_delete_flip
(
context
,
id
,
ip_types
.
SCALING
)


def get_scalingip(context, id, fields=None):
    """Retrieve a scaling IP.

    :param context: neutron api request context.
    :param id: The UUID of the scaling IP.
    :param fields: a list of strings that are valid keys in a
        scaling IP dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.

    :returns: Dictionary containing details for the scaling IP.  If values
        are declared in the fields parameter, then only those keys will be
        present.
    """
    LOG.info('get_scalingip %s for tenant %s' % (id, context.tenant_id))
    filters = {'address_type': ip_types.SCALING, '_deallocated': False}
    scaling_ip = db_api.floating_ip_find(context, id=id, scope=db_api.ONE,
                                         **filters)
    if not scaling_ip:
        raise q_exc.ScalingIpNotFound(id=id)
    return v._make_scaling_ip_dict(scaling_ip)

def
get_scalingip
(
context
,
id
,
fields
=
)
:
LOG
.
info
(
'get_scalingip %s for tenant %s'
%
(
id
,
context
.
tenant_id
)
)
filters
=
{
'address_type'
:
ip_types
.
SCALING
,
'_deallocated'
:
False
}
scaling_ip
=
db_api
.
floating_ip_find
(
context
,
id
=
id
,
scope
=
db_api
.
ONE
,
**
filters
)
if
not
scaling_ip
:
raise
q_exc
.
ScalingIpNotFound
(
id
=
id
)
return
v
.
_make_scaling_ip_dict
(
scaling_ip
)


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

    :param context: neutron api request context.
    :param filters: a dictionary with keys that are valid keys for
        a scaling ip as listed in the RESOURCE_ATTRIBUTE_MAP object
        in neutron/api/v2/attributes.py.  Values in this dictionary
        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
        scaling IP dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.

    :returns: List of scaling IPs that are accessible to the tenant who
        submits the request (as indicated by the tenant id of the context)
        as well as any filters.
    """
    LOG.info('get_scalingips for tenant %s filters %s fields %s' %
             (context.tenant_id, filters, fields))
    scaling_ips = _get_ips_by_type(context, ip_types.SCALING,
                                   filters=filters, fields=fields)
    return [v._make_scaling_ip_dict(scip) for scip in scaling_ips]


def is_isonet_vif(vif):
    """Determine if a vif is on isonet

    Returns True if a vif belongs to an isolated network by checking
    for a nicira interface id.
    """
    nicira_iface_id = vif.record.get('other_config').get('nicira-iface-id')

    if nicira_iface_id:
        return True

    return False

def is_isonet_vif ( vif ) : nicira_iface_id = vif . record . get ( 'other_config' ) . get ( 'nicira-iface-id' ) if nicira_iface_id : return True return False


def partition_vifs(xapi_client, interfaces, security_group_states):
    """Splits VIFs into three explicit categories and one implicit

    Added - Groups exist in Redis that have not been ack'd and the VIF
            is not tagged.
            Action: Tag the VIF and apply flows
    Updated - Groups exist in Redis that have not been ack'd and the VIF
              is already tagged
              Action: Do not tag the VIF, do apply flows
    Removed - Groups do NOT exist in Redis but the VIF is tagged
              Action: Untag the VIF, apply default flows
    Self-Heal - Groups are ack'd in Redis but the VIF is untagged. We treat
                this case as if it were an "added" group.
                Action: Tag the VIF and apply flows
    NOOP - The VIF is not tagged and there are no matching groups in Redis.
           This is our implicit category
           Action: Do nothing
    """
    added = []
    updated = []
    removed = []

    for vif in interfaces:
        # Quark should not action on isonet vifs in regions that use FLIP
        if ('floating_ip' in CONF.QUARK.environment_capabilities and
                is_isonet_vif(vif)):
            continue

        vif_has_groups = vif in security_group_states
        if vif.tagged and vif_has_groups and\
                security_group_states[vif][sg_cli.SECURITY_GROUP_ACK]:
            # Already ack'd these groups and VIF is tagged, reapply.
            # If it's not tagged, fall through and have it self-heal
            continue

        if vif.tagged:
            if vif_has_groups:
                updated.append(vif)
            else:
                removed.append(vif)
        else:
            if vif_has_groups:
                added.append(vif)
            # if not tagged and no groups, skip

    return added, updated, removed

def partition_vifs ( xapi_client , interfaces , security_group_states ) : added = [ ] updated = [ ] removed = [ ] for vif in interfaces : if ( 'floating_ip' in CONF . QUARK . environment_capabilities and is_isonet_vif ( vif ) ) : continue vif_has_groups = vif in security_group_states if vif . tagged and vif_has_groups and security_group_states [ vif ] [ sg_cli . SECURITY_GROUP_ACK ] : continue if vif . tagged : if vif_has_groups : updated . append ( vif ) else : removed . append ( vif ) else : if vif_has_groups : added . append ( vif ) return added , updated , removed


def get_groups_to_ack(groups_to_ack, init_sg_states, curr_sg_states):
    """Compares initial security group rules with current sg rules.

    Given the groups that were successfully returned from
        xapi_client.update_interfaces call, compare initial and current
        security group rules to determine if an update occurred during
        the window that the xapi_client.update_interfaces was executing.
        Return a list of vifs whose security group rules have not changed.
    """
    security_groups_changed = []
    # Compare current security group rules with initial rules.
    for vif in groups_to_ack:
        initial_state = init_sg_states[vif][sg_cli.SECURITY_GROUP_HASH_ATTR]
        current_state = curr_sg_states[vif][sg_cli.SECURITY_GROUP_HASH_ATTR]
        bad_match_msg = ('security group rules were changed for vif "%s" while'
                         ' executing xapi_client.update_interfaces.'
                         ' Will not ack rule.' % vif)
        # If lists are different lengths, they're automatically different.
        if len(initial_state) != len(current_state):
            security_groups_changed.append(vif)
            LOG.info(bad_match_msg)
        elif len(initial_state) > 0:
            # Compare rules in equal length lists.
            for rule in current_state:
                if rule not in initial_state:
                    security_groups_changed.append(vif)
                    LOG.info(bad_match_msg)
                    break

    # Only ack groups whose rules have not changed since update. If
    # rules do not match, do not add them to ret so the change
    # can be picked up on the next cycle.
    ret = [group for group in groups_to_ack
           if group not in security_groups_changed]
    return ret

Compares initial security group rules with current sg rules .


def run():
    """Fetches changes and applies them to VIFs periodically

    Process as of RM11449:
    * Get all groups from redis
    * Fetch ALL VIFs from Xen
    * Walk ALL VIFs and partition them into added, updated and removed
    * Walk the final "modified" VIFs list and apply flows to each
    """
    groups_client = sg_cli.SecurityGroupsClient()
    xapi_client = xapi.XapiClient()

    interfaces = set()
    while True:
        try:
            interfaces = xapi_client.get_interfaces()
        except Exception:
            LOG.exception("Unable to get instances/interfaces from xapi")
            _sleep()
            continue

        try:
            sg_states = groups_client.get_security_group_states(interfaces)
            new_sg, updated_sg, removed_sg = partition_vifs(xapi_client,
                                                            interfaces,
                                                            sg_states)
            xapi_client.update_interfaces(new_sg, updated_sg, removed_sg)
            groups_to_ack = [v for v in new_sg + updated_sg if v.success]
            # NOTE(quade): This solves a race condition where a security group
            # rule may have changed between the time the sg_states were called
            # and when they were officially ack'd. It functions as a compare
            # and set. This is a fix until we get onto a proper messaging
            # queue. NCP-2287
            sg_sts_curr = groups_client.get_security_group_states(interfaces)
            groups_to_ack = get_groups_to_ack(groups_to_ack, sg_states,
                                              sg_sts_curr)
            # This list will contain all the security group rules that do not
            # match
            ack_groups(groups_client, groups_to_ack)

        except Exception:
            LOG.exception("Unable to get security groups from registry and "
                          "apply them to xapi")
            _sleep()
            continue

        _sleep()


def delete_tenant_quota(context, tenant_id):
        """Delete the quota entries for a given tenant_id.

        Atfer deletion, this tenant will use default quota values in conf.
        """

        tenant_quotas = context.session.query(Quota)
        tenant_quotas = tenant_quotas.filter_by(tenant_id=tenant_id)
        tenant_quotas.delete()

def delete_tenant_quota ( context , tenant_id ) : tenant_quotas = context . session . query ( Quota ) tenant_quotas = tenant_quotas . filter_by ( tenant_id = tenant_id ) tenant_quotas . delete ( )

Delete the quota entries for a given tenant_id .


def _validate_subnet_cidr(context, network_id, new_subnet_cidr):
    """Validate the CIDR for a subnet.

    Verifies the specified CIDR does not overlap with the ones defined
    for the other subnets specified for this network, or with any other
    CIDR if overlapping IPs are disabled.

    """
    if neutron_cfg.cfg.CONF.allow_overlapping_ips:
        return

    try:
        new_subnet_ipset = netaddr.IPSet([new_subnet_cidr])
    except TypeError:
        LOG.exception("Invalid or missing cidr: %s" % new_subnet_cidr)
        raise n_exc.BadRequest(resource="subnet",
                               msg="Invalid or missing cidr")

    filters = {
        'network_id': network_id,
        'shared': [False]
    }
    # Using admin context here, in case we actually share networks later
    subnet_list = db_api.subnet_find(context=context.elevated(), **filters)

    for subnet in subnet_list:
        if (netaddr.IPSet([subnet.cidr]) & new_subnet_ipset):
            # don't give out details of the overlapping subnet
            err_msg = (_("Requested subnet with cidr: %(cidr)s for "
                         "network: %(network_id)s overlaps with another "
                         "subnet") %
                       {'cidr': new_subnet_cidr,
                        'network_id': network_id})
            LOG.error(_("Validation for CIDR: %(new_cidr)s failed - "
                        "overlaps with subnet %(subnet_id)s "
                        "(CIDR: %(cidr)s)"),
                      {'new_cidr': new_subnet_cidr,
                       'subnet_id': subnet.id,
                       'cidr': subnet.cidr})
            raise n_exc.InvalidInput(error_message=err_msg)


def get_subnet(context, id, fields=None):
    """Retrieve a subnet.

    : param context: neutron api request context
    : param id: UUID representing the subnet to fetch.
    : param fields: a list of strings that are valid keys in a
        subnet dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_subnet %s for tenant %s with fields %s" %
             (id, context.tenant_id, fields))
    subnet = db_api.subnet_find(context=context, limit=None,
                                page_reverse=False, sorts=['id'],
                                marker_obj=None, fields=None, id=id,
                                join_dns=True, join_routes=True,
                                scope=db_api.ONE)
    if not subnet:
        raise n_exc.SubnetNotFound(subnet_id=id)

    cache = subnet.get("_allocation_pool_cache")
    if not cache:
        new_cache = subnet.allocation_pools
        db_api.subnet_update_set_alloc_pool_cache(context, subnet, new_cache)
    return v._make_subnet_dict(subnet)


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

    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 subnet 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
        subnet dictionary as listed in the RESOURCE_ATTRIBUTE_MAP
        object in neutron/api/v2/attributes.py. Only these fields
        will be returned.
    """
    LOG.info("get_subnets for tenant %s with filters %s fields %s" %
             (context.tenant_id, filters, fields))
    filters = filters or {}
    subnets = db_api.subnet_find(context, limit=limit,
                                 page_reverse=page_reverse, sorts=sorts,
                                 marker_obj=marker, join_dns=True,
                                 join_routes=True, join_pool=True, **filters)
    for subnet in subnets:
        cache = subnet.get("_allocation_pool_cache")
        if not cache:
            db_api.subnet_update_set_alloc_pool_cache(
                context, subnet, subnet.allocation_pools)
    return v._make_subnets_list(subnets, fields=fields)


def get_subnets_count(context, filters=None):
    """Return the number of subnets.

    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_subnets_count for tenant %s with filters %s" %
             (context.tenant_id, filters))
    return db_api.subnet_count_all(context, **filters)


def delete_subnet(context, id):
    """Delete a subnet.

    : param context: neutron api request context
    : param id: UUID representing the subnet to delete.
    """
    LOG.info("delete_subnet %s for tenant %s" % (id, context.tenant_id))
    with context.session.begin():
        subnet = db_api.subnet_find(context, id=id, scope=db_api.ONE)
        if not subnet:
            raise n_exc.SubnetNotFound(subnet_id=id)

        if not context.is_admin:
            if STRATEGY.is_provider_network(subnet.network_id):
                if subnet.tenant_id == context.tenant_id:
                    # A tenant can't delete subnets on provider network
                    raise n_exc.NotAuthorized(subnet_id=id)
                else:
                    # Raise a NotFound here because the foreign tenant
                    # does not have to know about other tenant's subnet
                    # existence.
                    raise n_exc.SubnetNotFound(subnet_id=id)

        _delete_subnet(context, subnet)

def
delete_subnet
(
context
,
id
)
:
LOG
.
info
(
"delete_subnet %s for tenant %s"
%
(
id
,
context
.
tenant_id
)
)
with
context
.
session
.
begin
(
)
:
subnet
=
db_api
.
subnet_find
(
context
,
id
=
id
,
scope
=
db_api
.
ONE
)
if
not
subnet
:
raise
n_exc
.
SubnetNotFound
(
subnet_id
=
id
)
if
not
context
.
is_admin
:
if
STRATEGY
.
is_provider_network
(
subnet
.
network_id
)
:
if
subnet
.
tenant_id
==
context
.
tenant_id
:
raise
n_exc
.
NotAuthorized
(
subnet_id
=
id
)
else
:
raise
n_exc
.
SubnetNotFound
(
subnet_id
=
id
)
_delete_subnet
(
context
,
subnet
)


def _perform_async_update_rule(context, id, db_sg_group, rule_id, action):
    """Updates a SG rule async and return the job information.

    Only happens if the security group has associated ports. If the async
    connection fails the update continues (legacy mode).
    """
    rpc_reply = None
    sg_rpc = sg_rpc_api.QuarkSGAsyncProcessClient()
    ports = db_api.sg_gather_associated_ports(context, db_sg_group)
    if len(ports) > 0:
        rpc_reply = sg_rpc.start_update(context, id, rule_id, action)
        if rpc_reply:
            job_id = rpc_reply['job_id']
            job_api.add_job_to_context(context, job_id)
        else:
            LOG.error("Async update failed. Is the worker running?")

Updates a SG rule async and return the job information .


def update_security_group_rule(context, id, security_group_rule):
    '''Updates a rule and updates the ports'''
    LOG.info("update_security_group_rule for tenant %s" %
             (context.tenant_id))
    new_rule = security_group_rule["security_group_rule"]
    # Only allow updatable fields
    new_rule = _filter_update_security_group_rule(new_rule)

    with context.session.begin():
        rule = db_api.security_group_rule_find(context, id=id,
                                               scope=db_api.ONE)
        if not rule:
            raise sg_ext.SecurityGroupRuleNotFound(id=id)

        db_rule = db_api.security_group_rule_update(context, rule, **new_rule)

        group_id = db_rule.group_id
        group = db_api.security_group_find(context, id=group_id,
                                           scope=db_api.ONE)
        if not group:
            raise sg_ext.SecurityGroupNotFound(id=group_id)

    if group:
        _perform_async_update_rule(context, group_id, group, rule.id,
                                   RULE_UPDATE)

    return v._make_security_group_rule_dict(db_rule)


def get_public_net_id(self):
        """Returns the public net id"""
        for id, net_params in self.strategy.iteritems():
            if id == CONF.QUARK.public_net_id:
                return id
        return None

def get_public_net_id ( self ) : for id , net_params in self . strategy . iteritems ( ) : if id == CONF . QUARK . public_net_id : return id return None


def opt_args_decorator(func):
    """A decorator to be used on another decorator

    This is done to allow separate handling on the basis of argument values
    """
    @wraps(func)
    def wrapped_dec(*args, **kwargs):
        if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
            # actual decorated function
            return func(args[0])
        else:
            # decorator arguments
            return lambda realf: func(realf, *args, **kwargs)

    return wrapped_dec

def opt_args_decorator ( func ) : @ wraps ( func ) def wrapped_dec ( * args , ** kwargs ) : if len ( args ) == 1 and len ( kwargs ) == 0 and callable ( args [ 0 ] ) : return func ( args [ 0 ] ) else : return lambda realf : func ( realf , * args , ** kwargs ) return wrapped_dec


def _fix_missing_tenant_id(self, context, body, key):
        """Will add the tenant_id to the context from body.

        It is assumed that the body must have a tenant_id because neutron
        core could never have gotten here otherwise.
        """
        if not body:
            raise n_exc.BadRequest(resource=key,
                                   msg="Body malformed")
        resource = body.get(key)
        if not resource:
            raise n_exc.BadRequest(resource=key,
                                   msg="Body malformed")
        if context.tenant_id is None:
            context.tenant_id = resource.get("tenant_id")
        if context.tenant_id is None:
            msg = _("Running without keystone AuthN requires "
                    "that tenant_id is specified")
            raise n_exc.BadRequest(resource=key, msg=msg)

Will add the tenant_id to the context from body .


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

def _pop ( self , model ) : tags = [ ] for tag in model . tags : if self . is_tag ( tag ) : tags . append ( tag ) 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

def
serialize_rules
(
self
,
rules
)
:
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
=
{
}
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

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 build_full_day_ips ( query , period_start , period_end ) : 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)

def calc_periods ( hour = 0 , minute = 0 ) : period_end = datetime . datetime . utcnow ( ) . replace ( hour = hour , minute = minute , second = 0 , microsecond = 0 ) period_start = period_end - datetime . timedelta ( days = 1 ) period_end -= datetime . timedelta ( seconds = 1 ) return ( period_start , period_end )

Returns a tuple of start_period and end_period .