The First LABS Project: Capacity API

Share this page:‚Äč

Share on facebook
Share on twitter
Share on linkedin
Share on email
Share on print

We are very excited to announce that the first LABS project has been officially committed to the Streaming Video Alliance repo: the Capacity API. As explained on its project page,

The Capacity API is intended to allow participants in the federated video delivery service to communicate availability and capacity information as needed. The design is meant to encompass multiple use cases, including one content provider to many CDNs/ISPs as well as more complex multiparty interactions, all while preserving the flexibility and business models of the underlying providers.

The current version of the python API code is provided below. You can access the Github project here.

#!/usr/bin/env python3

import json
from flask import Flask, request
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)

# Surrogate object
class Surrogate():
    def __init__(self, name, qty, units):
        self.name = name
        self.qty = qty
        self.units = units
    def toJson(self):
        host = hosts[self.name]
        return { "surrogate": host, "capacity": self.qty, "units": self.units }

class ScopeError(Exception):
    def __init__(self, message):
        self.message = message
        
# Footprint object types.
class Footprint():
    def __init__(self, value):
        self.value = value
    def constrain(self, attrib, value):
        # For now, just ignore them.
        return True
    def ispEnumerate(isp):
        result = []
        for surrogate in available:
            sl = available[surrogate]
            if isp in sl:
                result.append(Surrogate(surrogate, int(sl[isp]), 'gbps'))
        return result
    
class v4Footprint(Footprint):
    def valid(self):
        return self.value in ipv4cidr.keys()
    def available(self):
        # Translate to ISP, then iterate over surrogates and report
        # any that have capacity for that ISP
        try:
            isp = ipv4cidr[self.value]
            return Footprint.ispEnumerate(isp)
        except KeyError:
            raise ScopeError('invalid CIDR')

class asnFootprint(Footprint):
    def valid(self):
        return self.value in asn.keys()
    def available(self):
        # Translate to ISP, then iterate over surrogates and report
        # and that have capacity for that ISP
        try:
            isp = asn[self.value]
            return Footprint.ispEnumerate(isp)
        except KeyError:
            raise ScopeError('invalid ASN')

class ccFootprint(Footprint):
    def valid(self):
        return self.value in geo.keys()
    def available(self):
        try:
            surrogates = geo[self.value]
            sl = []
            for surrogate in surrogates:
                total = 0
                for isp in available[surrogate]:
                    total += available[surrogate][isp]
                sl.append(Surrogate(surrogate, total, 'gbps'))
            return sl
        except KeyError:
            raise ScopeError('invalid CC')

# Various types for field inputs
footprintTypes = { 'ipv4cidr' : v4Footprint,
                   'asn' : asnFootprint,
                   'countrycode' : ccFootprint,
                   'iso3166-2' : ccFootprint }

unitsTypes = [ 'gbps', 'krps' ]
classTypes = [ 'general', 'video-vod', 'video-live' ]

# Current capacity.  For this simple mock up we set the values, but in a
# real system these would likely need to be queried from an external
# service.  Capacity is broken down by surrogate identifier (airport code
# in this example), and then by peer identifier.  To keep this mockup as
# simple as possible but still meaningful, we're not modeling capacity for
# anything other than egress.
available = { 'jfk' : { 'att': 400, 'verizon': 200, 'google': 300 },
              'lga' : { 'att': 100, 'tata': 400 },
              'ord' : { 'verizon': 400, 'google': 200, 'foofoo': 50 },
              'lon' : { 'bt': 200, 'tata': 300, 'google': 100 } }

hosts = { 'jfk': 'jfk1.foo.cdn.example',
          'lga': 'lga3.foo.cdn.example',
          'ord': 'ord2.foo.cdn.example',
          'lon': 'lon1.foo.cdn.example' }

# We also need a map of pops to geo information
geo = { 'US': ['jfk', 'lga', 'ord'],
        'US-NY': ['jfk', 'lga'],
        'US-IL': ['ord'],
        'GB': [ 'lon' ] }

# One for peers to ASN
asn = { '1': 'att',
        '2': 'verizon',
        '3': 'google',
        '4': 'tata',
        '5': 'bt',
        '6': 'foofoo' }

# And one for CIDRs to ASN
ipv4cidr = { '34.2.0.0/16': 'att',
             '34.3.0.0/16': 'att',
             '34.5.0.0/16': 'google' }

class Capacity(Resource):
    def post(self, type=None, value=None):
        json = request.get_json()
        if not type: # Pull in from json
            try:
                foot = json['footprint']
                type = foot['type']
                value = foot['value']
            except:
                return { 'error': 'missing types' }, 400
        try:
            fp = footprintTypes[type](value)
        except KeyError:
            return { 'error': 'invalid footprint type' }, 400
        
        # Use footprint capabilities to add attributes to the footprint
        # object before we ask for capacity.
        if 'capabilities' in json:
            for cap in json['capabilities']:
                fp.constrain(cap['capability-type'], cap['capability-value'])

        try:
            surrogates = fp.available()
        except ScopeError as e:
            # We could add a hint to the reply showing the closest covering
            # footprint if applicable.  For example, if they ask for US-NY,
            # we might respond with US, or 192.168.100.64/25 may respond
            # with 192.168.0.0/16.  Since this is a mockup, I'm just
            # throwing the error.
            return { 'error': e.message }, 400
        
        # Things like the capacity units, and content class are more
        # about scaling that value, and should be applied after the raw
        # capacity is determined.

        sl = []
        for s in surrogates:
            sl.append(s.toJson())
        return sl, 200, {'Cache-Control': 'max-age=600'}
    
api.add_resource(Capacity, '/capacity', '/capacity/<type>/<path:value>')

if __name__ == '__main__':
    app.run(debug=True) 

If you are interested in contributing to this, and other LABS projects, you should consider joining the Streaming Video Alliance.

About the Author

Jason Thibeault
Executive Director at

Jason is the Executive Director of the Streaming Video Alliance, a worldwide consortium of companies dedicated to helping shape the future of online video. In this role, he runs day-to-day operations, finances, member recruitment, strategy, and evangelizes the organization at events around the world. He is also the co-founder of a big data startup, datazoom.io. Jason is a contributing editor at Streaming Media Magazine and has written several books.

Scroll to Top