예제 #1
0
def create_provider_groups(ctx):
    ctx.status("creating provider groups")
    obj_tbl = resource_models.get_table('object_names')
    pg_tbl = resource_models.get_table('provider_groups')
    object_type_id = get_object_type_map()['runm.provider_group']

    sess = resource_models.get_session()

    try:
        for pg in ctx.deployment_config.provider_groups.values():
            # Create the object lookup record
            obj_rec = dict(
                object_type=object_type_id,
                uuid=pg.uuid,
                name=pg.name,
            )
            ins = obj_tbl.insert().values(**obj_rec)
            sess.execute(ins)

            # Create the base provider group record
            pg_rec = dict(
                uuid=pg.uuid,
            )
            ins = pg_tbl.insert().values(**pg_rec)
            sess.execute(ins)

        sess.commit()
        ctx.status_ok()
    except Exception as err:
        sess.rollback()
        ctx.status_fail(err)
예제 #2
0
def _find_providers_with_any_caps(ctx, caps, limit=50):
    """Returns providers that have any of the supplied capabilities.

    The SQL that is generated looks like this:

    SELECT p.id, p.uuid
    FROM providers AS p
    JOIN provider_capabilities AS pc
      ON p.id = pc.provider_id
    WHERE pc.capability_id IN ($CAPABILITIES)
    """
    p_tbl = resource_models.get_table('providers')
    p_caps_tbl = resource_models.get_table('provider_capabilities')

    cap_ids = [_cap_id_from_code(ctx, cap) for cap in caps]

    p_to_p_caps = sa.join(
        p_tbl,
        p_caps_tbl,
        p_tbl.c.id == p_caps_tbl.c.provider_id,
    )
    cols = [
        p_tbl.c.id,
        p_tbl.c.uuid,
    ]
    sel = sa.select(cols).select_from(p_to_p_caps).where(
        p_caps_tbl.c.capability_id.in_(cap_ids)).limit(limit)
    sess = resource_models.get_session()
    return {r[0]: r[1] for r in sess.execute(sel)}
예제 #3
0
def _cap_id_from_code(ctx, cap):
    cap_tbl = resource_models.get_table('capabilities')
    sel = sa.select([cap_tbl.c.id]).where(cap_tbl.c.code == cap)

    sess = resource_models.get_session()
    res = sess.execute(sel).fetchone()
    if not res:
        raise ValueError("Could not find ID for capability %s" % cap)
    return res[0]
예제 #4
0
def _rc_id_from_code(ctx, resource_class):
    rc_tbl = resource_models.get_table('resource_classes')
    sel = sa.select([rc_tbl.c.id]).where(rc_tbl.c.code == resource_class)

    sess = resource_models.get_session()
    res = sess.execute(sel).fetchone()
    if not res:
        raise ValueError("Could not find ID for resource class %s" %
                         resource_class)
    return res[0]
예제 #5
0
def get_object_type_map():
    """Returns a dict, keyed by object type string code, of internal object
    type ID.
    """
    global _OBJECT_TYPE_MAP
    if _OBJECT_TYPE_MAP is not None:
        return _OBJECT_TYPE_MAP
    tbl = resource_models.get_table('object_types')
    sel = sa.select([tbl.c.id, tbl.c.code])
    sess = resource_models.get_session()
    _OBJECT_TYPE_MAP = {r[1]: r[0] for r in sess.execute(sel)}
    return _OBJECT_TYPE_MAP
예제 #6
0
def get_provider_type_map():
    """Returns a dict, keyed by provider type string code, of internal provider
    type ID.
    """
    global _PROVIDER_TYPE_MAP
    if _PROVIDER_TYPE_MAP is not None:
        return _PROVIDER_TYPE_MAP
    tbl = resource_models.get_table('provider_types')
    sel = sa.select([tbl.c.id, tbl.c.code])
    sess = resource_models.get_session()
    _PROVIDER_TYPE_MAP = {r[1]: r[0] for r in sess.execute(sel)}
    return _PROVIDER_TYPE_MAP
예제 #7
0
def create_partitions(ctx):
    ctx.status("creating partitions")
    obj_tbl = resource_models.get_table('object_names')
    part_tbl = resource_models.get_table('partitions')
    object_type_id = get_object_type_map()['runm.partition']

    sess = resource_models.get_session()

    created = set()

    try:
        for p in ctx.deployment_config.providers.values():
            part_uuid = p.partition.uuid
            if part_uuid in created:
                continue
            # Create the object lookup record
            obj_rec = dict(
                object_type=object_type_id,
                uuid=part_uuid,
                name=p.partition.name,
            )
            ins = obj_tbl.insert().values(**obj_rec)
            sess.execute(ins)

            # Create the base provider group record
            part_rec = dict(
                uuid=part_uuid,
            )
            ins = part_tbl.insert().values(**part_rec)
            sess.execute(ins)
            created.add(part_uuid)

        sess.commit()
        ctx.status_ok()
    except Exception as err:
        sess.rollback()
        ctx.status_fail(err)
예제 #8
0
def _find_providers_with_resource(ctx,
                                  claim_time,
                                  release_time,
                                  resource_constraint,
                                  exclude=None,
                                  limit=50):
    """Queries for providers that have capacity for the requested amount of a
    resource class and optionally meet resource-specific capability
    constraints. The query is done in a claim start/end window.

    The SQL generated for a resource constraint without the optional capability
    constraint ends up looking like this:

    SELECT p.id, p.uuid
    FROM providers AS p
    JOIN inventories AS i
      ON p.id = i.provider_id
    LEFT JOIN (
      SELECT ai.provider_id, SUM(ai.used) AS total_used
      FROM allocation_items AS ai
      JOIN (
        SELECT id AS allocation_id
        FROM allocations
        WHERE claim_time >= $CLAIM_START
        AND release_time < $CLAIM_END
        GROUP BY id
      ) AS allocs_in_window
        ON ai.allocation_id = allocs_in_window
      WHERE ai.resource_class_id = $RESOURCE_CLASS
    ) AS usages
      ON i.provider_id = usages.provider_id
    WHERE i.resource_class_id = $RESOURCE_CLASS
    AND ((i.total - i.reserved) * i.allocation_ratio) >=
         $RESOURCE_REQUEST_AMOUNT + COALESCE(usages.used, 0))

    If the optional `exclude` argument is provided, we tack on a:

    WHERE p.id NOT IN ($EXCLUDE)

    clause. The `exclude` argument is populated when the capabilities
    constraints that may have been previously processed identified some
    providers that should be excluded (they met an "exclusion filter" -- i.e.
    they matched for a 'forbid' specification in a constraint).
    """
    p_tbl = resource_models.get_table('providers')
    inv_tbl = resource_models.get_table('inventories')
    alloc_tbl = resource_models.get_table('allocations')
    alloc_item_tbl = resource_models.get_table('allocation_items')

    rc_id = _rc_id_from_code(ctx, resource_constraint.resource_class)
    alloc_window_cols = [
        alloc_tbl.c.id.label('allocation_id'),
    ]
    allocs_in_window_subq = sa.select(alloc_window_cols).where(
        sa.and_(
            alloc_tbl.c.claim_time >= claim_time,
            alloc_tbl.c.release_time < release_time,
        )).group_by(alloc_tbl.c.id)
    allocs_in_window_subq = sa.alias(allocs_in_window_subq, "allocs_in_window")
    usage_cols = [
        alloc_item_tbl.c.provider_id,
        func.sum(alloc_item_tbl.c.used).label('total_used'),
    ]
    alloc_item_to_alloc_window = sa.outerjoin(
        alloc_item_tbl, allocs_in_window_subq, alloc_item_tbl.c.allocation_id
        == allocs_in_window_subq.c.allocation_id)
    usage_subq = sa.select(usage_cols).select_from(
        alloc_item_to_alloc_window).where(
            alloc_item_tbl.c.resource_class_id == rc_id).group_by(
                alloc_item_tbl.c.provider_id)
    usage_subq = sa.alias(usage_subq, "usages")

    join_to = p_tbl
    if resource_constraint.capability_constraint:
        cap_constraint = resource_constraint.capability_constraint
        join_to = _select_add_capability_constraint(ctx, p_tbl, cap_constraint)

    p_to_inv = sa.join(
        join_to, inv_tbl,
        sa.and_(
            p_tbl.c.id == inv_tbl.c.provider_id,
            inv_tbl.c.resource_class_id == rc_id,
        ))
    inv_to_usage = sa.outerjoin(
        p_to_inv, usage_subq,
        inv_tbl.c.provider_id == usage_subq.c.provider_id)
    cols = [
        p_tbl.c.id,
        p_tbl.c.uuid,
    ]
    sel = sa.select(cols).select_from(inv_to_usage).where(
        sa.and_(inv_tbl.c.resource_class_id == rc_id,
                ((inv_tbl.c.total - inv_tbl.c.reserved) *
                 inv_tbl.c.allocation_ratio) >=
                (resource_constraint.max_amount +
                 func.coalesce(usage_subq.c.total_used, 0))))
    if exclude:
        sel = sel.where(~p_tbl.c.id.in_(set(exclude)))
    sel = sel.limit(limit)
    sess = resource_models.get_session()
    return {r[0]: r[1] for r in sess.execute(sel)}
예제 #9
0
def create_providers(ctx):
    obj_tbl = resource_models.get_table('object_names')
    rc_tbl = resource_models.get_table('resource_classes')
    cap_tbl = resource_models.get_table('capabilities')
    part_tbl = resource_models.get_table('partitions')
    pg_tbl = resource_models.get_table('provider_groups')
    pg_members_tbl = resource_models.get_table('provider_group_members')
    p_tbl = resource_models.get_table('providers')
    p_caps_tbl = resource_models.get_table('provider_capabilities')
    tree_tbl = resource_models.get_table('provider_trees')
    inv_tbl = resource_models.get_table('inventories')
    pd_tbl = resource_models.get_table('provider_distances')
    dt_tbl = resource_models.get_table('distance_types')
    d_tbl = resource_models.get_table('distances')

    # in-process cache of partition name -> internal ID
    part_ids = {}
    # in-process cache of provider group name -> internal ID
    pg_ids = {}
    # in-process cache of resource class code -> internal ID
    rc_ids = {}
    # in-process cache of capability code -> internal ID
    cap_ids = {}
    # Hashmap of distance type code to internal ID
    distance_type_ids = {}
    # Hashmap of (distance_type_code, distance_code) to internal ID
    distance_ids = {}

    sess = resource_models.get_session()
    ctx.status("caching provider group internal IDs")
    for pg in ctx.deployment_config.provider_groups.values():
        if pg.uuid in pg_ids:
            pg_id = pg_ids[pg.uuid]
        else:
            # Not yet in cache... look up the provider group by UUID
            sel = sa.select([pg_tbl.c.id]).where(pg_tbl.c.uuid == pg.uuid)
            res = sess.execute(sel).fetchone()
            pg_ids[pg.uuid] = res[0]
    ctx.status_ok()

    ctx.status("caching resource class and capability internal IDs")
    for prof in ctx.deployment_config.profiles.values():
        for rc_code in prof['inventory'].keys():
            if rc_code not in rc_ids:
                sel = sa.select([rc_tbl.c.id]).where(rc_tbl.c.code == rc_code)
                res = sess.execute(sel).fetchone()
                rc_id = res[0]
                rc_ids[rc_code] = rc_id
        for cap_code in prof['capabilities']:
            if cap_code not in cap_ids:
                sel = sa.select([cap_tbl.c.id]).where(
                    cap_tbl.c.code == cap_code)
                res = sess.execute(sel).fetchone()
                cap_id = res[0]
                cap_ids[cap_code] = cap_id
    ctx.status_ok()

    ctx.status("caching partition, distance type and distance internal IDs")
    for p in ctx.deployment_config.providers.values():
        # Populate our hashmap of partition name to internal ID
        part_uuid = p.partition.uuid
        if part_uuid not in part_ids:
            # Grab the partition internal ID matching the partition UUID
            sel = sa.select([part_tbl.c.id]).where(
                part_tbl.c.uuid == part_uuid)
            res = sess.execute(sel).fetchone()
            part_ids[part_uuid] = res[0]
        # Populate our hashmap of distance type and codes to internal IDs
        for pd in p.distances:
            d_key = (pd.distance_type, pd.distance_code)
            if d_key not in distance_ids:
                if pd.distance_type not in distance_type_ids:
                    # Grab the distance type internal ID matching the distance
                    # type code
                    sel = sa.select([dt_tbl.c.id]).where(
                        dt_tbl.c.code == pd.distance_type)
                    res = sess.execute(sel).fetchone()
                    distance_type_ids[pd.distance_type] = res[0]
                dt_id = distance_type_ids[pd.distance_type]
                # Not yet in cache... look up the provider group by name
                sel = sa.select([d_tbl.c.id]).where(
                    sa.and_(d_tbl.c.type_id == dt_id,
                            d_tbl.c.code == pd.distance_code))
                res = sess.execute(sel).fetchone()
                distance_ids[d_key] = res[0]
    ctx.status_ok()

    object_type_id = get_object_type_map()['runm.provider']
    compute_prov_type_id = get_provider_type_map()['runm.compute']
    ctx.status("creating providers")
    try:
        for p in ctx.deployment_config.providers.values():
            # Create the object lookup record
            obj_rec = dict(
                object_type=object_type_id,
                uuid=p.uuid,
                name=p.name,
            )
            ins = obj_tbl.insert().values(**obj_rec)
            sess.execute(ins)

            # Create the base provider record
            part_id = part_ids[p.partition.uuid]
            p_rec = dict(
                uuid=p.uuid,
                type_id=compute_prov_type_id,
                partition_id=part_id,
                generation=1,
            )
            ins = p_tbl.insert().values(**p_rec)
            res = sess.execute(ins)
            p_id = res.inserted_primary_key[0]

            # Now that we've added the base records, go ahead and flesh out
            # the provider group members and provider tree records, both of
            # which require some lookups into the provider_groups and
            # providers tables to get internal IDs.
            tree_rec = dict(
                root_provider_id=p_id,
                nested_left=1,
                nested_right=2,
                generation=1,
            )
            ins = tree_tbl.insert().values(**tree_rec)
            sess.execute(ins)

            # Add the provider's group memberships
            for pg in p.groups:
                pg_id = pg_ids[pg.uuid]
                pg_member_rec = dict(
                    provider_group_id=pg_id,
                    provider_id=p_id,
                )
                ins = pg_members_tbl.insert().values(**pg_member_rec)
                sess.execute(ins)

            # OK, now add the inventory records for the provider
            for rc_code, inv in p.profile.inventory.items():
                rc_id = rc_ids[rc_code]
                inv_rec = dict(
                    provider_id=p_id,
                    resource_class_id=rc_id,
                    total=inv['total'],
                    reserved=inv['reserved'],
                    min_unit=inv['min_unit'],
                    max_unit=inv['max_unit'],
                    step_size=inv['step_size'],
                    allocation_ratio=inv['allocation_ratio'],
                )
                ins = inv_tbl.insert().values(**inv_rec)
                sess.execute(ins)

            # Add the provider's capabilities
            for cap_code in p.profile.capabilities:
                cap_id = cap_ids[cap_code]
                p_cap_rec = dict(
                    provider_id=p_id,
                    capability_id=cap_id,
                )
                ins = p_caps_tbl.insert().values(**p_cap_rec)
                sess.execute(ins)

            # Add the distance relationships after all the provider groups have
            # been added
            for pd in p.distances:
                pg_id = pg_ids[pd.provider_group.uuid]
                d_key = (pd.distance_type, pd.distance_code)
                d_id = distance_ids[d_key]
                pd_rec = dict(
                    provider_id=p_id,
                    provider_group_id=pg_id,
                    distance_id=d_id,
                )
                ins = pd_tbl.insert().values(**pd_rec)
                sess.execute(ins)

        sess.commit()
        ctx.status_ok()
    except Exception as err:
        sess.rollback()
        ctx.status_fail(err)
예제 #10
0
def create_distances(ctx):
    ctx.status("creating distance types")
    dt_tbl = resource_models.get_table('distance_types')
    d_tbl = resource_models.get_table('distances')

    recs = [
        dict(
            code="network",
            description="Relative network distances",
            generation=1,
        ),
        dict(
            code="storage",
            description="Relative storage distances",
            generation=1,
        ),
        dict(
            code="failure",
            description="Relative distance representing successively smaller "
                        "chance of failure affecting workloads running on "
                        "that provider",
            generation=1,
        ),
    ]
    try:
        _insert_records(dt_tbl, recs)
        ctx.status_ok()
    except Exception as err:
        ctx.status_fail(err)

    ctx.status("creating distances")

    sess = resource_models.get_session()
    sel = sa.select([dt_tbl.c.id]).where(dt_tbl.c.code == "network")
    net_dt_id = sess.execute(sel).fetchone()[0]

    sel = sa.select([dt_tbl.c.id]).where(dt_tbl.c.code == "storage")
    storage_dt_id = sess.execute(sel).fetchone()[0]

    sel = sa.select([dt_tbl.c.id]).where(dt_tbl.c.code == "failure")
    failure_dt_id = sess.execute(sel).fetchone()[0]

    recs = [
        dict(
            type_id=net_dt_id,
            code="local",
            position=0,
            description="Very little network latency - within rack L2 subnet",
        ),
        dict(
            type_id=net_dt_id,
            code="site",
            position=1,
            description="Slightly higher latency between leaf "
                        "switch-connected nodes in a DC",
        ),
        dict(
            type_id=net_dt_id,
            code="remote",
            position=2,
            description="WAN latency",
        ),
        dict(
            type_id=storage_dt_id,
            code="local",
            position=0,
            description="Block storage local to host running machine",
        ),
        dict(
            type_id=storage_dt_id,
            code="row",
            position=1,
            description="NAS server shared by row of compute",
        ),
        dict(
            type_id=storage_dt_id,
            code="remote",
            position=2,
            description="External cloud block storage with WAN latency",
        ),
        dict(
            type_id=failure_dt_id,
            code="local",
            position=0,
            description="Providers are within same rack failure domain",
        ),
        dict(
            type_id=failure_dt_id,
            code="rack",
            position=1,
            description="Providers are in different rack power/failure domain",
        ),
        dict(
            type_id=failure_dt_id,
            code="row",
            position=2,
            description="Providers are in different row power/failure domain",
        ),
        dict(
            type_id=failure_dt_id,
            code="site",
            position=3,
            description="Providers are in different sites",
        ),
    ]
    try:
        _insert_records(d_tbl, recs)
        ctx.status_ok()
    except Exception as err:
        ctx.status_fail(err)
예제 #11
0
def _insert_records(tbl, recs):
    sess = resource_models.get_session()
    for rec in recs:
        ins = tbl.insert().values(**rec)
        sess.execute(ins)
    sess.commit()