Adding database instance details for show/create/delete/list

* expanding the create instance api data input
* added all the fields to the view and added exceptions to be thrown correctly
* added a few things to .gitignore
* adding some testing tests
This commit is contained in:
Craig Vyvial 2012-03-08 15:34:16 -06:00
parent cf7814abfb
commit e58fc33cc1
8 changed files with 233 additions and 23 deletions

2
.gitignore vendored
View File

@ -8,3 +8,5 @@ guest-agent-files.txt
reddwarf.egg*
reddwarf/vcsversion.py
*py*.egg
.coverage
covhtml/

View File

@ -35,22 +35,52 @@ keystone --endpoint http://localhost:35357/v2.0 --token be19c524ddc92109a224 use
# These are the values
REDDWARF_TENANT=reddwarf
echo $REDDWARF_TENANT
REDDWARF_USER=$(mysql keystone -e "select id from user where name='reddwarf';" | awk 'NR==2')
echo $REDDWARF_USER
REDDWARF_ROLE=$(mysql keystone -e "select id from role where name='reddwarf';" | awk 'NR==2')
echo $REDDWARF_ROLE
# These all need to be set tenant did not work with the id but the name did match in the auth shim.
# REDDWARF_TOKEN=
REDDWARF_TOKEN=$(curl -d '{"auth":{"passwordCredentials":{"username": "reddwarf", "password": "REDDWARF-PASS"},"tenantName":"reddwarf"}}' -H "Content-type: application/json" http://localhost:35357/v2.0/tokens | python -mjson.tool | grep id | tr -s ' ' | cut -d ' ' -f 3 | sed s/\"/''/g | awk 'NR==2' | cut -d ',' -f 1)
echo $REDDWARF_TOKEN
# Now attempt a login
curl -d '{"auth":{"passwordCredentials":{"username": "reddwarf", "password": "REDDWARF-PASS"},"tenantName":"reddwarf"}}' \
-H "Content-type: application/json" http://localhost:35357/v2.0/tokens | python -mjson.tool
#curl -d '{"auth":{"passwordCredentials":{"username": "reddwarf", "password": "REDDWARF-PASS"},"tenantName":"reddwarf"}}' \
# -H "Content-type: application/json" http://localhost:35357/v2.0/tokens | python -mjson.tool
# now get a list of instances, which connects over python-novaclient to nova
# NOTE THIS AUTH TOKEN NEEDS TO BE CHANGED
# Also note that keystone uses the tenant id now and _not_ the name
# curl -H"X-Auth-Token:$REDDWARF_TOKEN" http://0.0.0.0:8779/v0.1/$REDDWARF_TENANT/instances
# curl -H"Content-type:application/json" -H"X-Auth-Token:$REDDWARF_TOKEN" \
# http://0.0.0.0:8779/v0.1/$REDDWARF_TENANT/instances -d '{"name":"my_test","flavor":"1"}'
# list instances
# curl -H"X-Auth-Token:$REDDWARF_TOKEN" http://0.0.0.0:8779/v0.1/$REDDWARF_TENANT/instances | python -mjson.tool
# old create instance:
# curl -H"Content-type:application/json" -H"X-Auth-Token:$REDDWARF_TOKEN" http://0.0.0.0:8779/v0.1/$REDDWARF_TENANT/instances -d '{"name":"my_test","flavor":"1"}' | python -mjson.tool
# create instance:
# curl -H"Content-type:application/json" -H"X-Auth-Token:$REDDWARF_TOKEN" http://0.0.0.0:8779/v0.1/$REDDWARF_TENANT/instances -d '{"instance": {"databases": [{"character_set": "utf8", "collate": "utf8_general_ci", "name": "sampledb"}, {"name": "nextround"}], "flavorRef": "http://0.0.0.0:8779/v0.1/$REDDWARF_TENANT/flavors/1", "name": "json_rack_instance", "volume": {"size": "2"}}}'| python -mjson.tool
# {
# "instance": {
# "databases": [
# {
# "character_set": "utf8",
# "collate": "utf8_general_ci",
# "name": "sampledb"
# },
# {
# "name": "nextround"
# }
# ],
# "flavorRef": "http://0.0.0.0:8779/v0.1/$REDDWARF_TENANT/flavors/1",
# "name": "json_rack_instance",
# "volume": {
# "size": "2"
# }
# }
# }
# DELETE INSTANCE
# curl -H"X-Auth-Token:$REDDWARF_TOKEN" http://0.0.0.0:8779/v0.1/$REDDWARF_TENANT/instances/id -X DELETE | python -mjson.tool
# update the etc/reddwarf/reddwarf.conf.sample
# add this config setting
@ -67,9 +97,24 @@ curl -d '{"auth":{"passwordCredentials":{"username": "reddwarf", "password": "RE
# ssh-keygen
# build the image for reddwarf
# first time build the image for reddwarf
# ./bootstrap/bootstrap.sh
##### re-add image manually #####
VM_PATH=~/oneiric_mysql_image
UBUNTU_DISTRO="ubuntu 11.10"
UBUNTU_DISTRO_NAME=oneiric
QCOW_IMAGE=`find $VM_PATH -name '*.qcow2'`
function get_glance_id () {
echo `$@ | awk '{print $6}'`
}
glance add name="oneiric_mysql_image" is_public=true container_format=ovf disk_format=qcow2 distro='"ubuntu 11.10"' -A $REDDWARF_TOKEN < $QCOW_IMAGE
# GLANCE_IMAGEID=
echo "updating your database - $GLANCE_IMAGEID"
sqlite3 /src/reddwarf_test.sqlite "INSERT INTO service_images VALUES('1', 'database', '$GLANCE_IMAGEID');"
#sqlite3 /src/reddwarf_test.sqlite "UPDATE service_images set image_id='$GLANCE_IMAGEID';"
echo "done GLANCE IMAGE ID = $GLANCE_IMAGEID"
# add the image to the reddwarf database
# get the image id from glance
# glance index -A $REDDWARF_TOKEN

View File

@ -42,6 +42,13 @@ def stringify_keys(dictionary):
return dict((str(key), value) for key, value in dictionary.iteritems())
def exclude(key_values, *exclude_keys):
if key_values is None:
return None
return dict((key, value) for key, value in key_values.iteritems()
if key not in exclude_keys)
def generate_uuid():
return str(uuid.uuid4())

View File

@ -116,7 +116,8 @@ class RemoteModelBase(ModelBase):
class Instance(RemoteModelBase):
_data_fields = ['name', 'status', 'updated', 'id', 'flavor']
_data_fields = ['name', 'status', 'id', 'created', 'updated',
'flavor', 'links', 'addresses']
def __init__(self, server=None, context=None, uuid=None):
if server is None and context is None and uuid is None:
@ -124,7 +125,12 @@ class Instance(RemoteModelBase):
msg = "server, content, and uuid are not defined"
raise InvalidModelError(msg)
elif server is None:
self._data_object = self.get_client(context).servers.get(uuid)
try:
self._data_object = self.get_client(context).servers.get(uuid)
except nova_exceptions.NotFound, e:
raise rd_exceptions.NotFound(uuid=uuid)
except nova_exceptions.ClientException, e:
raise rd_exceptions.ReddwarfError()
else:
self._data_object = server
@ -138,10 +144,13 @@ class Instance(RemoteModelBase):
raise rd_exceptions.ReddwarfError()
@classmethod
def create(cls, context, name, image_id, flavor):
srv = cls.get_client(context).servers.create(name,
def create(cls, context, image_id, body):
# self.is_valid()
LOG.info("instance body : '%s'\n\n" % body)
flavorRef = body['instance']['flavorRef']
srv = cls.get_client(context).servers.create(body['instance']['name'],
image_id,
flavor)
flavorRef)
return Instance(server=srv)

View File

@ -23,20 +23,42 @@ from reddwarf import rpc
from reddwarf.common import config
from reddwarf.common import context as rd_context
from reddwarf.common import exception
from reddwarf.common import utils
from reddwarf.common import wsgi
from reddwarf.database import models
from reddwarf.database import views
CONFIG = config.Config
LOG = logging.getLogger('reddwarf.database.service')
LOG = logging.getLogger(__name__)
class BaseController(wsgi.Controller):
"""Base controller class."""
exclude_attr = []
exception_map = {
webob.exc.HTTPUnprocessableEntity: [
],
webob.exc.HTTPBadRequest: [
models.InvalidModelError,
],
webob.exc.HTTPNotFound: [
exception.NotFound,
models.ModelNotFoundError,
],
webob.exc.HTTPConflict: [
],
}
def __init__(self):
pass
def _extract_required_params(self, params, model_name):
params = params or {}
model_params = params.get(model_name, {})
return utils.stringify_keys(utils.exclude(model_params,
*self.exclude_attr))
class InstanceController(BaseController):
"""Controller for instance functionality"""
@ -67,10 +89,11 @@ class InstanceController(BaseController):
context = rd_context.ReddwarfContext(
auth_tok=req.headers["X-Auth-Token"],
tenant=tenant_id)
# TODO(cp16net) : need to handle exceptions here if the delete fails
models.Instance.delete(context=context, uuid=id)
# TODO(hub-cap): fixgure out why the result is coming back as None
LOG.info("result of delete %s" % result)
# LOG.info("result of delete %s" % result)
# TODO(cp16net): need to set the return code correctly
return wsgi.Result(202)
@ -87,15 +110,17 @@ class InstanceController(BaseController):
# code. Or maybe we shouldnt due to the nature of changing images.
# This needs discussion.
# TODO(hub-cap): turn this into middleware
LOG.info("Creating a database instance for tenant '%s'" % tenant_id)
LOG.info("req : '%s'\n\n" % req)
LOG.info("body : '%s'\n\n" % body)
context = rd_context.ReddwarfContext(
auth_tok=req.headers["X-Auth-Token"],
tenant=tenant_id)
database = models.ServiceImage.find_by(service_name="database")
image_id = database['image_id']
server = models.Instance.create(context,
body['name'],
image_id,
body['flavor']).data()
body).data()
# Now wait for the response from the create to do additional work
#TODO(cp16net): need to set the return code correctly

View File

@ -21,16 +21,26 @@ class InstanceView(object):
def __init__(self, instance):
self.instance = instance
#TODO(hub-cap): fix the link generation
def data(self):
return {"instance": {
"id": self.instance['id'],
"name": self.instance['name'],
"status": self.instance['status'],
"links": "Links will be coming in the future"
"created": self.instance['created'],
"updated": self.instance['updated'],
"flavor": self.instance['flavor'],
"links": self._build_links(self.instance['links']),
"addresses": self.instance['addresses'],
},
}
@staticmethod
def _build_links(links):
"""Build the links for the instance"""
for link in links:
link['href'] = link['href'].replace('servers', 'instances')
return links
class InstancesView(object):

View File

@ -41,9 +41,26 @@ class TestInstance(tests.BaseTest):
self.FAKE_SERVER.name = 'my_name'
self.FAKE_SERVER.status = 'ACTIVE'
self.FAKE_SERVER.updated = utils.utcnow()
self.FAKE_SERVER.created = utils.utcnow()
self.FAKE_SERVER.id = utils.generate_uuid()
self.FAKE_SERVER.flavor = ('http://localhost/1234/flavors/',
'52415800-8b69-11e0-9b19-734f1195ff37')
self.FAKE_SERVER.links = [{
"href": "http://localhost/1234/instances/123",
"rel": "self"
},
{
"href": "http://localhost/1234/instances/123",
"rel": "bookmark"
}]
self.FAKE_SERVER.addresses = {
"private": [
{
"addr": "10.0.0.4",
"version": 4
}
]
}
client = self.mock.CreateMock(novaclient.v1_1.Client)
servers = self.mock.CreateMock(novaclient.v1_1.servers.ServerManager)
@ -64,5 +81,8 @@ class TestInstance(tests.BaseTest):
self.assertEqual(instance['name'], self.FAKE_SERVER.name)
self.assertEqual(instance['status'], self.FAKE_SERVER.status)
self.assertEqual(instance['updated'], self.FAKE_SERVER.updated)
self.assertEqual(instance['created'], self.FAKE_SERVER.created)
self.assertEqual(instance['id'], self.FAKE_SERVER.id)
self.assertEqual(instance['flavor'], self.FAKE_SERVER.flavor)
self.assertEqual(instance['links'], self.FAKE_SERVER.links)
self.assertEqual(instance['addresses'], self.FAKE_SERVER.addresses)

View File

@ -16,9 +16,12 @@
import mox
import logging
import json
import novaclient
from reddwarf import tests
from reddwarf.common import config
from reddwarf.common import utils
from reddwarf.common import wsgi
from reddwarf.database import models
from reddwarf.database import service
@ -49,18 +52,28 @@ class DummyApp(wsgi.Router):
class TestInstanceController(ControllerTestBase):
DUMMY_INSTANCE_ID = "123"
DUMMY_INSTANCE = {"id": DUMMY_INSTANCE_ID,
"name": "DUMMY_NAME",
"status": "BUILD",
"created": "createtime",
"updated": "updatedtime",
"flavor": {},
"links": [],
"addresses": {}}
def setUp(self):
self.instances_path = "/tenant/instances"
super(TestInstanceController, self).setUp()
def test_show(self):
# block = factory_models.IpBlockFactory()
instance = mox.MockAnything()
response = self.app.get("%s/%s" % (self.instances_path,
self.DUMMY_INSTANCE_ID),
headers={'X-Auth-Token': '123'})
self.assertEqual(response.status_int, 404)
def test_show(self):
self.mock.StubOutWithMock(models.Instance, 'data')
models.Instance.data().AndReturn({"id": self.DUMMY_INSTANCE_ID,
"name": "DUMMY_NAME",
"status": "BUILD"})
models.Instance.data().AndReturn(self.DUMMY_INSTANCE)
self.mock.StubOutWithMock(models.Instance, '__init__')
models.Instance.__init__(context=mox.IgnoreArg(), uuid=mox.IgnoreArg())
self.mock.ReplayAll()
@ -70,3 +83,82 @@ class TestInstanceController(ControllerTestBase):
headers={'X-Auth-Token': '123'})
self.assertEqual(response.status_int, 201)
def test_index(self):
self.mock.StubOutWithMock(models.Instances, 'data')
models.Instances.data().AndReturn([self.DUMMY_INSTANCE])
self.mock.StubOutWithMock(models.Instances, '__init__')
models.Instances.__init__(mox.IgnoreArg())
self.mock.ReplayAll()
response = self.app.get("%s" % (self.instances_path),
headers={'X-Auth-Token': '123'})
self.assertEqual(response.status_int, 201)
def mock_out_client_create(self):
"""Stubs out a fake server returned from novaclient.
This is akin to calling Client.servers.get(uuid)
and getting the server object back."""
self.FAKE_SERVER = self.mock.CreateMock(object)
self.FAKE_SERVER.name = 'my_name'
self.FAKE_SERVER.status = 'ACTIVE'
self.FAKE_SERVER.updated = utils.utcnow()
self.FAKE_SERVER.created = utils.utcnow()
self.FAKE_SERVER.id = utils.generate_uuid()
self.FAKE_SERVER.flavor = 'http://localhost/1234/flavors/1234'
self.FAKE_SERVER.links = [{
"href": "http://localhost/1234/instances/123",
"rel": "self"
},
{
"href": "http://localhost/1234/instances/123",
"rel": "bookmark"
}]
self.FAKE_SERVER.addresses = {
"private": [
{
"addr": "10.0.0.4",
"version": 4
}
]
}
client = self.mock.CreateMock(novaclient.v1_1.Client)
servers = self.mock.CreateMock(novaclient.v1_1.servers.ServerManager)
servers.create(mox.IgnoreArg(),
mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn(self.FAKE_SERVER)
client.servers = servers
self.mock.StubOutWithMock(models.RemoteModelBase, 'get_client')
models.RemoteModelBase.get_client(mox.IgnoreArg()).AndReturn(client)
def test_create(self):
self.mock.StubOutWithMock(models.Instance, 'data')
models.Instance.data().AndReturn(self.DUMMY_INSTANCE)
self.mock.StubOutWithMock(models.ServiceImage, 'find_by')
models.ServiceImage.find_by(service_name=mox.IgnoreArg()).AndReturn(
{'image_id': 1234})
self.mock_out_client_create()
self.mock.ReplayAll()
body = {
"instance": {
"databases": [
{
"character_set": "utf8",
"collate": "utf8_general_ci",
"name": "sampledb"
},
{
"name": "nextround"
}
],
"flavorRef": "http://localhost/v0.1/tenant/flavors/1",
"name": "json_rack_instance",
}
}
response = self.app.post_json("%s" % (self.instances_path), body=body,
headers={'X-Auth-Token': '123'},
)
self.assertEqual(response.status_int, 201)