Mimic

A verified fake of the Rackspace API

Slides: http://lekhajee.github.io/mimic-workshop
Source: https://github.com/rackerlabs/mimic
Chat: ##mimic on Freenode

Introductions

Lekha Jeevan

Glyph Lefkotiwz

Ying Li

Thomas Walton

Install :

Python (2.7 preferred)

pip

virtualenv

REST Client : Chrome plugin or CURL

What?

Pretending

to authenticate

Pretending

to Boot Servers

Pretending

is faster

in-memory

minimal
dependencies

(almost entirely pure Python)

Service Dependencies

Configuration

self-contained

Frontend

Cloud Intelligence via Protractor

Reach via capybara

(Selenium web driver)



APIs supported today

Identity

Compute

Load balancers

Cloud Monitoring

Cloud Queues

Swift

RCV3

Demo

Install and run Mimic

Start Your Virtualenvs!

Now You Try!

Demo

Nova command-line client

config.sh

export OS_USERNAME=username
export OS_PASSWORD=password
export OS_TENANT_NAME=11111
export OS_AUTH_URL=http://localhost:8900/identity/v2.0/tokens

config.sh

export OS_USERNAME=username
export OS_PASSWORD=password
export OS_TENANT_NAME=11111
export OS_AUTH_URL=http://localhost:8900/identity/v2.0/tokens

config.sh

export OS_USERNAME=username
export OS_PASSWORD=password
export OS_TENANT_NAME=11111
export OS_AUTH_URL=http://localhost:8900/identity/v2.0/tokens

Now You Try!

BUILD → ACTIVE ERROR ACTIVE

unknown errors

Mimic
simulates errors

Error injection using metadata

/mimic/v1.1/tick

{
    "amount": 30.0
}
{
    "advanced": 30.0,
    "now": "1970-01-01T00:00:30.000000Z"
}
{
  "server": {
    "status": "BUILD",
    "updated": "1970-01-01T00:00:00.000000Z",
    "OS-EXT-STS:task_state": null,
    "user_id": "170454",
    "addresses": {},
    "...": "..."
  }
}
{
  "server": {
    "status": "ACTIVE",
    "updated": "1970-01-01T00:00:01.000000Z",
    "OS-EXT-STS:task_state": null,
    "user_id": "170454",
    "addresses": {},
    "...": "..."
  }
}

--realtime

FORK Mimic

https://github.com/rackerlabs/mimic

Running Mimic's tests

git clone https://github.com/[you]/mimic

cd mimic

pip install tox

tox -e lint -e py27

Klein

twisted.web.iweb.IRequest

Klein Demo

# my-server.py
from klein import run, route

@route('/')
def home(request):
    return b'Hello, world!'

run("localhost", 8080)
... and then ...

python my-server.py

curl http://localhost:8080/

Klein Demo

import json
from klein import run, route
@route('/')
def home(request):
    request.setResponseCode(200)
    body = {"hello": "world"}
    return json.dumps(body)
run("localhost", 8080)
... and then ...

python my-server.py

curl http://localhost:8080/

Plugins!

# mimic/plugins/my_plugin.py

from mimic.rest.my_api import MyAPIMock
the_mock_plugin = MyAPIMock()

# mimic/test/test_core.py
# CoreBuildingTests.test_from_plugin_includes_all_plugins

plugin_apis = set((nova_plugin.nova,
                   loadbalancer_plugin.loadbalancer,
                   swift_plugin.swift,
                   queue_plugin.queue,
                   maas_plugin.maas,
                   rackconnect_v3_plugin.rackconnect,
                   my_plugin.the_mock_plugin))

Run The Tests

tox -e py27

(Watch Them Fail)

Traceback (most recent call last):
  File ".../site-packages/twisted/plugin.py", line 167, in getCache
    provider = pluginModule.load()
  File ".../site-packages/twisted/python/modules.py", line 383, in load
    return self.pathEntry.pythonPath.moduleLoader(self.name)
  File ".../site-packages/twisted/python/reflect.py", line 303, in namedAny
    topLevelPackage = _importAndCheckStack(trialname)
  File ".../site-packages/twisted/python/reflect.py", line 250, in _importAndCheckStack
    reraise(excValue, excTraceback)
  File ".../Mimic/mimic/plugins/my_plugin.py", line 3, in <module>
    from mimic.rest.my_api import MyAPIMock
exceptions.ImportError: No module named my_api

Make Them Pass

Identity

Is the Entry Point

(Not A Plugin)

http://localhost:8900/mimicking/ NovaApi-78bc54/ORD/ v2/tenant_id_f15c1028/servers

Plugin Interface:
“API Mock”

class MyAPIMock():
  def catalog_entries(...)
  def resource_for_region(...)

(that's it!)

def catalog_entries(self,
                    tenant_id):
return [
    Entry(
        tenant_id, "compute", "cloudServersOpenStack",
        [
            Endpoint(tenant_id, region="ORD",
                     endpoint_id=text_type(uuid4()),
                     prefix="v2"),
            Endpoint(tenant_id, region="DFW",
                     endpoint_id=text_type(uuid4()),
                     prefix="v2")
        ]
    )
]
def resource_for_region(
    self, region, uri_prefix,
    session_store
):
    return (MyRegion(...)
            .app.resource())
class MyRegion():

    app = MimicApp()

    @app.route('/v2/<string:tenant_id>/servers',
               methods=['GET'])

    def list_servers(self, request, tenant_id):
        return json.dumps({"servers": []})

Tell Mimic

To Load It

# mimic/plugins/my_plugin.py

from my_api import MyAPIMock
the_mock_plugin = MyAPIMock()

mimic/rest/nova_api.py

mimic/rest/maas_api.py

mimic/rest/swift_api.py

mimic/rest/rackconnect_v3_api.py

tox -e py27

Tenant Session

(remembered until restart)

session = session_store.session_for_tenant_id(tenant_id)

class MyMockData():
    "..."

my_data = session.data_for_api(my_api_mock,
                                MyMockData)
session = session_store.session_for_tenant_id(tenant_id)

from mimic.plugins.other_mock import (other_api_mock,
                                      OtherMockData)

other_data = session.data_for_api(other_api_mock,
                                  OtherMockData)

Errors As A Service

Error Injection

Error Injection

Metadata-Based

Error Injection

Metadata-Based: In-Band

Error Injection

Control-Plane-Based

Error Injection

Control-Plane-Based: Out-Of-Band

Error Injection

Future: With Your Help