Merge branch 'master' of github.com:NOPping/gstack into refactor
diff --git a/.coveragerc b/.coveragerc
index c35bfbc..ac30c60 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -6,6 +6,7 @@
     *__main__*
     gstack/models/*.py
     gstack/configure.py
+    gstack/core.py
 
 [report]
 exclude_lines =
diff --git a/alembic.ini b/alembic.ini
new file mode 100644
index 0000000..f34bc98
--- /dev/null
+++ b/alembic.ini
@@ -0,0 +1,59 @@
+# A generic, single database configuration.
+
+[alembic]
+# path to migration scripts
+script_location = migrations
+
+# template used to generate migration files
+# file_template = %%(rev)s_%%(slug)s
+
+# max length of characters to apply to the
+# "slug" field
+#truncate_slug_length = 40
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+# set to 'true' to allow .pyc and .pyo files without
+# a source .py file to be detected as revisions in the
+# versions/ directory
+# sourceless = false
+
+sqlalchemy.url = driver://user:pass@localhost/dbname
+
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/gstack/__init__.py b/gstack/__init__.py
index acc141d..276ebbf 100644
--- a/gstack/__init__.py
+++ b/gstack/__init__.py
@@ -21,7 +21,7 @@
 import sys
 
 from flask import Flask
-from flask.ext.sqlalchemy import SQLAlchemy
+from gstack.core import db
 
 
 def _load_config_file():
@@ -29,34 +29,42 @@
         os.path.expanduser('~'),
         '.gstack/gstack.conf'
     )
-
     if not os.path.exists(config_file):
         sys.exit('No configuration found, please run gstack-configure')
 
     return config_file
 
 
+def _load_database():
+    database_file = os.path.join(
+        os.path.expanduser('~'),
+        '.gstack/gstack.sqlite'
+    )
+
+    if not os.path.exists(database_file):
+        sys.exit('No database found, please run gstack-configure')
+
+    return 'sqlite:///' + database_file
+
+
 def configure_app(settings=None):
     app.config['DATA'] = os.path.abspath(os.path.dirname(__file__)) + '/data'
 
+    db.init_app(app)
+
     if settings:
         app.config.from_object(settings)
     else:
         config_file = _load_config_file()
+        database_uri = _load_database()
         app.config.from_pyfile(config_file)
-
-    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + \
-        os.path.join(app.config['DATA'], 'app.db')
+        app.config['SQLALCHEMY_DATABASE_URI'] = database_uri
 
 
 app = Flask(__name__)
 
-db = SQLAlchemy(app)
 publickey_storage = {}
 
 from gstack.controllers import *
 
-
 basedir = os.path.abspath(os.path.dirname(__file__))
-
-db.create_all()
diff --git a/gstack/configure.py b/gstack/configure.py
index 436dbd0..e4d042b 100644
--- a/gstack/configure.py
+++ b/gstack/configure.py
@@ -19,10 +19,14 @@
 
 import os
 
+from alembic import command
+from alembic.config import Config as AlembicConfig
+
 
 def main():
     config_folder = _create_config_folder()
     _create_config_file(config_folder)
+    _create_database()
 
 
 def _create_config_folder():
@@ -69,3 +73,13 @@
     config_file.write('CLOUDSTACK_PATH = \'%s\'\n' % cloudstack_path)
 
     config_file.close()
+
+
+def _create_database():
+    directory = os.path.join(os.path.dirname(__file__), '../migrations')
+    config = AlembicConfig(os.path.join(
+        directory,
+        'alembic.ini'
+    ))
+    config.set_main_option('script_location', directory)
+    command.upgrade(config, 'head', sql=False, tag=None)
diff --git a/gstack/controllers/__init__.py b/gstack/controllers/__init__.py
index 47e309d..97f7847 100644
--- a/gstack/controllers/__init__.py
+++ b/gstack/controllers/__init__.py
@@ -20,5 +20,118 @@
 import os
 import glob
 
-__all__ = [os.path.basename(
-    f)[:-3] for f in glob.glob(os.path.dirname(__file__) + '/*.py')]
+from gstack import helpers
+from flask import request
+from gstack.services import requester
+from gstack.controllers import errors
+
+__all__ = [os.path.basename(f)[:-3] for f in glob.glob(os.path.dirname(__file__) + '/*.py')]
+
+
+def filter_by_name(data, name):
+    for item in data:
+        if item['name'] == name:
+            return item
+        elif 'displayname' in item and item['displayname'] == name:
+            return item
+
+    return None
+
+
+def _get_items(authorization, args=None):
+    args['listAll'] = 'true'
+
+    response = requester.make_request(
+        args['command'],
+        args,
+        authorization.client_id,
+        authorization.client_secret
+    )
+    response = response[response.keys()[0]]
+
+    return response
+
+
+def get_item_with_name(authorization, name, args, type):
+    response = _get_items(
+        authorization=authorization,
+        args=args
+    )
+
+    if 'count' in response:
+        response = filter_by_name(
+            data=response[type],
+            name=name
+        )
+        return response
+    else:
+        return None
+
+
+def get_item_with_name_or_error(authorization, name, args, type, func_route, to_cloudstack, **kwargs):
+    cloudstack_item = get_item_with_name(authorization, name, args, type)
+
+    if cloudstack_item:
+        return helpers.create_response(to_cloudstack(
+            cloudstack_response=cloudstack_item, **kwargs
+        ))
+    else:
+        return errors.resource_not_found(func_route)
+
+
+def _get_requested_items(authorization, args, type, to_cloudstack, **kwargs):
+    name = None
+    filter = helpers.get_filter(request.args)
+
+    if 'name' in filter:
+        name = filter['name']
+
+    items = []
+
+    if name:
+        cloudstack_item = get_item_with_name(
+            authorization=authorization,
+            name=name,
+            args=args,
+            type=type
+        )
+        if cloudstack_item:
+            items.append(
+                to_cloudstack(
+                    cloudstack_response=cloudstack_item, **kwargs
+                )
+            )
+    else:
+        cloudstack_items = _get_items(authorization=authorization, args=args)
+        if cloudstack_items:
+            for cloudstack_item in cloudstack_items[type]:
+                items.append(
+                    to_cloudstack(
+                        cloudstack_response=cloudstack_item, **kwargs)
+                )
+
+    return items
+
+
+def describe_items_aggregated(authorization, args, type, gce_type, to_cloudstack, **kwargs):
+    from gstack.controllers import zones
+    items = {}
+
+    zone_list = zones.get_zone_names(authorization=authorization)
+
+    for zone in zone_list:
+        kwargs['zone'] = zone
+        zone_items = _get_requested_items(authorization, args, type, to_cloudstack, **kwargs)
+        items['zone/' + zone] = {}
+        if zone_items:
+            items['zone/' + zone][gce_type] = zone_items
+        else:
+            items['zone/' + zone] = errors.no_results_found(zone)
+
+    return items
+
+
+def describe_items(authorization, args, type, to_cloudstack, **kwargs):
+    items = _get_requested_items(authorization, args, type, to_cloudstack, **kwargs)
+
+    return items
diff --git a/gstack/controllers/disks.py b/gstack/controllers/disks.py
index 0743a48..c0bbbb0 100644
--- a/gstack/controllers/disks.py
+++ b/gstack/controllers/disks.py
@@ -20,40 +20,8 @@
 import urllib
 from flask import request, url_for
 from gstack import app, authentication
-from gstack.services import requester
-from gstack.controllers import zones, helper, errors
-
-
-def _get_disks(authorization, args=None):
-    command = 'listVolumes'
-    if not args:
-        args = {}
-    cloudstack_response = requester.make_request(
-        command,
-        args,
-        authorization.client_id,
-        authorization.client_secret
-    )
-
-    return cloudstack_response
-
-
-def get_disk_by_name(authorization, disk):
-    disk_list = _get_disks(
-        authorization=authorization,
-        args={
-            'keyword': disk
-        }
-    )
-
-    if disk_list['listvolumesresponse']:
-        response = helper.filter_by_name(
-            data=disk_list['listvolumesresponse']['volume'],
-            name=disk
-        )
-        return response
-    else:
-        return None
+from gstack import helpers
+from gstack import controllers
 
 
 def _cloudstack_volume_to_gce(cloudstack_response, projectid, zone):
@@ -66,17 +34,13 @@
     response['description'] = cloudstack_response['name']
     response['sizeGb'] = cloudstack_response['size']
 
-    response['selfLink'] = urllib.unquote_plus(helper.get_root_url() + url_for(
+    response['selfLink'] = urllib.unquote_plus(helpers.get_root_url() + url_for(
         'getmachinetype',
         projectid=projectid,
         machinetype=cloudstack_response['name'],
         zone=zone
     ))
-
-    if not zone:
-        response['zone'] = cloudstack_response['zonename']
-    else:
-        response['zone'] = zone
+    response['zone'] = zone
 
     return response
 
@@ -84,44 +48,11 @@
 @app.route('/compute/v1/projects/<projectid>/aggregated/disks', methods=['GET'])
 @authentication.required
 def aggregatedlistdisks(projectid, authorization):
-    disk_list = _get_disks(authorization=authorization)
-    zone_list = zones.get_zone_names(authorization=authorization)
-
-    disk = None
-    filter = helper.get_filter(request.args)
-
-    if 'name' in filter:
-        disk = filter['name']
-
-    items = {}
-
-    for zone in zone_list:
-        zone_disks = []
-        if disk:
-            disk = get_disk_by_name(
-                authorization=authorization,
-                disk=disk
-            )
-            if disk:
-                zone_disks.append(
-                    _cloudstack_volume_to_gce(
-                        cloudstack_response=disk,
-                        projectid=projectid,
-                        zone=zone,
-                    )
-                )
-
-        elif disk_list['listvolumesresponse']:
-            for disk in disk_list['listvolumesresponse']['volume']:
-                zone_disks.append(
-                    _cloudstack_volume_to_gce(
-                        cloudstack_response=disk,
-                        projectid=projectid,
-                        zone=zone,
-                    )
-                )
-        items['zone/' + zone] = {}
-        items['zone/' + zone]['disks'] = zone_disks
+    args = {'command': 'listVolumes'}
+    kwargs = {'projectid': projectid}
+    items = controllers.describe_items_aggregated(
+        authorization, args, 'volume', 'disk',
+        _cloudstack_volume_to_gce, **kwargs)
 
     populated_response = {
         'kind': 'compute#diskAggregatedList',
@@ -130,49 +61,17 @@
         'items': items
     }
 
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/zones/<zone>/disks', methods=['GET'])
 @authentication.required
 def listdisks(projectid, authorization, zone):
-    disk = None
-    filter = helper.get_filter(request.args)
-
-    if 'name' in filter:
-        disk = filter['name']
-
-    items = []
-
-    if disk:
-        disk_list = _get_disks(
-            authorization=authorization,
-            args={'keyword': disk}
-        )
-        if disk_list['listvolumesresponse']:
-            disk = helper.filter_by_name(
-                data=disk_list['listvolumesresponse']['volume'],
-                name=disk
-            )
-            if disk:
-                items.append(
-                    _cloudstack_volume_to_gce(
-                        cloudstack_response=disk,
-                        projectid=projectid,
-                        zone=zone
-                    )
-                )
-    else:
-        disk_list = _get_disks(authorization=authorization)
-        if disk_list['listvolumesresponse']:
-            for disk in disk_list['listvolumesresponse']['volume']:
-                items.append(
-                    _cloudstack_volume_to_gce(
-                        cloudstack_response=disk,
-                        projectid=projectid,
-                        zone=zone
-                    )
-                )
+    args = {'command': 'listVolumes'}
+    kwargs = {'projectid': projectid, 'zone': zone}
+    items = controllers.describe_items(
+        authorization, args, 'volume',
+        _cloudstack_volume_to_gce, **kwargs)
 
     populated_response = {
         'kind': 'compute#imageList',
@@ -181,30 +80,15 @@
         'items': items
     }
 
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/zones/<zone>/disks/<disk>', methods=['GET'])
 @authentication.required
 def getdisk(projectid, authorization, zone, disk):
-    response = get_disk_by_name(
-        authorization=authorization,
-        disk=disk
-    )
-
-    if response:
-        return helper.create_response(
-            data=_cloudstack_volume_to_gce(
-                cloudstack_response=response,
-                projectid=projectid,
-                zone=zone
-            )
-        )
-    else:
-        func_route = url_for(
-            'getdisk',
-            projectid=projectid,
-            zone=zone,
-            disk=disk
-        )
-        return errors.resource_not_found(func_route)
+    func_route = url_for('getdisk', projectid=projectid, zone=zone, disk=disk)
+    args = {'command': 'listVolumes'}
+    kwargs = {'projectid': projectid, 'zone': zone}
+    return controllers.get_item_with_name_or_error(
+        authorization, disk, args, 'volume', func_route,
+        _cloudstack_volume_to_gce, **kwargs)
diff --git a/gstack/controllers/errors.py b/gstack/controllers/errors.py
index 4ddbb57..9805ca9 100644
--- a/gstack/controllers/errors.py
+++ b/gstack/controllers/errors.py
@@ -19,17 +19,13 @@
 
 import urllib
 from gstack import app
-from flask import jsonify, Response
-
-
-@app.errorhandler(404)
-def not_found(e):
-    return Response('Not Found', status=404, mimetype='text/html')
+from gstack import helpers
+from flask import Response
 
 
 @app.errorhandler(401)
 def unauthorized(e):
-    res = jsonify({
+    res = {
         'error': {
             'errors': [
                 {
@@ -43,14 +39,13 @@
         },
         'code': 401,
         'message': 'Login Required',
-    })
+    }
 
-    res.status_code = 401
-    return res
+    return helpers.create_errored_response(res, 401)
 
 
 def resource_not_found(func_url):
-    res = jsonify({
+    res = {
         'error': {
             'errors': [
                 {
@@ -62,16 +57,16 @@
             'code': 404,
             'message': 'The resource \'' + urllib.unquote_plus(func_url) + '\' was not found'
         }
-    })
-    res.status_code = 404
-    return res
+    }
+
+    return helpers.create_errored_response(res, 404)
 
 
 def no_results_found(scope):
     return ({
         "warning": {
             "code": "NO_RESULTS_ON_PAGE",
-            "message": "There are no results for scope" + scope + " on this page.",
+            "message": "There are no results for scope " + scope + " on this page.",
             "data": [{
                 "key": "scope",
                 "value": scope
diff --git a/gstack/controllers/firewalls.py b/gstack/controllers/firewalls.py
index 34fb736..b2264ac 100755
--- a/gstack/controllers/firewalls.py
+++ b/gstack/controllers/firewalls.py
@@ -19,15 +19,17 @@
 
 from gstack import app
 from gstack import authentication
+from gstack import controllers
+from gstack import helpers
 from gstack.services import requester
 from gstack.controllers import errors
 from flask import jsonify, request, url_for
 import json
 
 
-def _cloudstack_securitygroup_to_gce(response_item):
-    if 'ingressrule' in response_item:
-        rules = response_item['ingressrule']
+def _cloudstack_securitygroup_to_gce(cloudstack_response):
+    if 'ingressrule' in cloudstack_response:
+        rules = cloudstack_response['ingressrule']
         allowed = []
         sourceranges = []
         for rule in rules:
@@ -44,16 +46,16 @@
         return ({
             "kind": "compute#firewall",
             "selfLink": '',
-            "id": response_item['id'],
+            "id": cloudstack_response['id'],
             "creationTimestamp": '',
-            "name": response_item['name'],
-            "description": response_item['description'],
+            "name": cloudstack_response['name'],
+            "description": cloudstack_response['description'],
             "network": '',
             "sourceRanges": sourceranges,
             "sourceTags": [
                 ''
             ],
-            "targetTags": response_item['tags'],
+            "targetTags": cloudstack_response['tags'],
             "allowed": allowed
         })
 
@@ -61,32 +63,10 @@
 @app.route('/compute/v1/projects/<projectid>/global/firewalls', methods=['GET'])
 @authentication.required
 def listsecuritygroups(projectid, authorization):
-    command = 'listSecurityGroups'
-    args = {}
-    cloudstack_response = requester.make_request(
-        command,
-        args,
-        authorization.client_id,
-        authorization.client_secret
-    )
-
-    cloudstack_response = cloudstack_response
-
-    app.logger.debug(
-        'Processing request for aggregated list Firewalls\n'
-        'Project: ' + projectid + '\n' +
-        json.dumps(cloudstack_response, indent=4, separators=(',', ': '))
-    )
-
-    firewalls = []
-    if cloudstack_response['listsecuritygroupsresponse']:
-        res = cloudstack_response['listsecuritygroupsresponse']
-        for response_item in res['securitygroup']:
-            firewalls.append(response_item)
-
-    items = []
-    for fw in firewalls:
-        items.append(_cloudstack_securitygroup_to_gce(fw))
+    args = {'command': 'listSecurityGroups'}
+    items = controllers.describe_items(
+        authorization, args, 'securitygroup',
+        _cloudstack_securitygroup_to_gce, **{})
 
     populated_response = {
         'kind': 'compute#firewallList',
@@ -95,9 +75,7 @@
         'items': items
     }
 
-    res = jsonify(populated_response)
-    res.status_code = 200
-    return res
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/global/firewalls/<firewall>', methods=['GET'])
@@ -158,7 +136,6 @@
         json.dumps(cloudstack_response, indent=4, separators=(',', ': '))
     )
 
-    # return Global Operations
     populated_response = {}
 
     res = jsonify(populated_response)
diff --git a/gstack/controllers/helper.py b/gstack/controllers/helper.py
deleted file mode 100644
index 05830ce..0000000
--- a/gstack/controllers/helper.py
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import urllib
-from gstack import app
-from flask import jsonify
-
-
-def create_response(data):
-    res = jsonify(data)
-    res.status_code = 200
-
-    return res
-
-
-def get_filter(data):
-    filter = {}
-
-    if 'filter' in data:
-        filter = urllib.unquote_plus(data['filter'])
-        filter = dict(filter.split(' eq ') for values in filter)
-
-    return filter
-
-
-def filter_by_name(data, name):
-    for item in data:
-        if item['name'] == name:
-            return item
-    return None
-
-
-def get_root_url():
-    return 'https://' + \
-        app.config['GSTACK_BIND_ADDRESS'] + ':' + app.config['GSTACK_PORT']
diff --git a/gstack/controllers/images.py b/gstack/controllers/images.py
index 25c400a..c31af2b 100755
--- a/gstack/controllers/images.py
+++ b/gstack/controllers/images.py
@@ -19,49 +19,20 @@
 
 import urllib
 from gstack import app, authentication
-from gstack.services import requester
-from gstack.controllers import helper, errors
+from gstack import helpers
+from gstack import controllers
 from flask import request, url_for
 
 
-def _get_templates(authorization, args=None):
-    command = 'listTemplates'
-    if not args:
-        args = {}
-
-    if 'templatefilter' not in args:
-        args['templatefilter'] = 'executable'
-
-    cloudstack_response = requester.make_request(
-        command,
-        args,
-        authorization.client_id,
-        authorization.client_secret
-    )
-    return cloudstack_response
-
-
 def get_template_by_name(authorization, image):
-    image_list = _get_templates(
-        authorization=authorization,
-        args={
-            'keyword': image
-        }
-    )
-
-    if image_list['listtemplatesresponse']:
-        response = helper.filter_by_name(
-            data=image_list['listtemplatesresponse']['template'],
-            name=image
-        )
-        return response
-    else:
-        return None
+    args = {'templatefilter': 'executable', 'command': 'listTemplates'}
+    return controllers.get_item_with_name(authorization, image, args, 'template')
 
 
 def _create_populated_image_response(projectid, images=None):
     if not images:
         images = []
+
     populated_response = {
         'kind': 'compute#imageList',
         'selfLink': request.base_url,
@@ -71,25 +42,15 @@
     return populated_response
 
 
-def _cloudstack_template_to_gce(cloudstack_response, selfLink=None):
-    translate_image_status = {
-        'True': 'Ready',
-        'False': 'Failed'
-    }
-
+def _cloudstack_template_to_gce(cloudstack_response):
     response = {}
     response['kind'] = 'compute#image'
     response['id'] = cloudstack_response['id']
     response['creationTimestamp'] = cloudstack_response['created']
     response['name'] = cloudstack_response['name']
     response['description'] = cloudstack_response['displaytext']
-    response['status'] = translate_image_status[
-        str(cloudstack_response['isready'])]
-
-    if selfLink:
-        response['selfLink'] = urllib.unquote_plus(selfLink)
-    else:
-        response['selfLink'] = urllib.unquote_plus(request.base_url)
+    response['status'] = cloudstack_response['isready']
+    response['selfLink'] = urllib.unquote_plus(request.base_url) + '/' + response['name']
 
     return response
 
@@ -98,46 +59,33 @@
 @authentication.required
 def listnocentoscloudimages(authorization):
     populated_response = _create_populated_image_response('centos-cloud')
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/debian-cloud/global/images', methods=['GET'])
 @authentication.required
 def listnodebiancloudimages(authorization):
     populated_response = _create_populated_image_response('debian-cloud')
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/global/images', methods=['GET'])
 @authentication.required
 def listimages(projectid, authorization):
-    image_list = _get_templates(
-        authorization=authorization
-    )
+    args = {'templatefilter': 'executable', 'command': 'listTemplates'}
+    items = controllers.describe_items(
+        authorization, args, 'template',
+        _cloudstack_template_to_gce, **{})
 
-    images = []
-    if image_list['listtemplatesresponse']:
-        for image in image_list['listtemplatesresponse']['template']:
-            images.append(_cloudstack_template_to_gce(
-                cloudstack_response=image,
-                selfLink=request.base_url + '/' + image['name']))
-
-    populated_response = _create_populated_image_response(projectid, images)
-    return helper.create_response(data=populated_response)
+    populated_response = _create_populated_image_response(projectid, items)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/global/images/<image>', methods=['GET'])
 @authentication.required
 def getimage(projectid, authorization, image):
-    response = get_template_by_name(
-        authorization=authorization,
-        image=image
-    )
-
-    if response:
-        return helper.create_response(
-            data=_cloudstack_template_to_gce(response)
-        )
-    else:
-        func_route = url_for('getimage', projectid=projectid, image=image)
-        return errors.resource_not_found(func_route)
+    func_route = url_for('getimage', projectid=projectid, image=image)
+    args = {'templatefilter': 'executable', 'command': 'listTemplates'}
+    return controllers.get_item_with_name_or_error(
+        authorization, image, args, 'template', func_route,
+        _cloudstack_template_to_gce, **{})
diff --git a/gstack/controllers/index.py b/gstack/controllers/index.py
index 293c6ac..fc1f925 100755
--- a/gstack/controllers/index.py
+++ b/gstack/controllers/index.py
@@ -19,7 +19,7 @@
 
 
 from gstack import app
-from gstack.controllers import helper
+from gstack import helpers
 import json
 
 
@@ -28,9 +28,9 @@
     with open(app.config['DATA'] + '/v1.json') as template:
         discovery_template = json.loads(template.read())
 
-    discovery_template['baseUrl'] = helper.get_root_url() + '/' + app.config['PATH']
+    discovery_template['baseUrl'] = helpers.get_root_url() + '/' + app.config['PATH']
     discovery_template['basePath'] = '/' + app.config['PATH']
-    discovery_template['rootUrl'] = helper.get_root_url() + '/'
+    discovery_template['rootUrl'] = helpers.get_root_url() + '/'
     discovery_template['servicePath'] = app.config['PATH']
 
-    return helper.create_response(data=discovery_template)
+    return helpers.create_response(data=discovery_template)
diff --git a/gstack/controllers/instances.py b/gstack/controllers/instances.py
index 8bb0e14..1eb0e6a 100755
--- a/gstack/controllers/instances.py
+++ b/gstack/controllers/instances.py
@@ -19,25 +19,12 @@
 
 import json
 import urllib
+from gstack import helpers
+from gstack import controllers
 from flask import request, url_for
 from gstack import app, authentication
 from gstack.services import requester
-from gstack.controllers import zones, helper, operations, images, errors, machine_type, networks
-
-
-def _get_virtual_machines(authorization, args=None):
-    command = 'listVirtualMachines'
-    if not args:
-        args = {}
-
-    cloudstack_response = requester.make_request(
-        command,
-        args,
-        authorization.client_id,
-        authorization.client_secret
-    )
-
-    return cloudstack_response
+from gstack.controllers import zones, operations, images, errors, machine_type, networks
 
 
 def _deploy_virtual_machine(authorization, args, projectid):
@@ -65,7 +52,7 @@
     if 'network' in args:
         network = networks.get_network_by_name(
             authorization=authorization,
-            securitygroup=args['network']
+            network=args['network']
         )
         converted_args['securitygroupids'] = network['id']
 
@@ -83,27 +70,7 @@
     return cloudstack_response
 
 
-def _destroy_virtual_machine(authorization, instance):
-    virtual_machine_id = _get_virtual_machine_by_name(
-        authorization,
-        instance)['id']
-
-    if virtual_machine_id is None:
-        func_route = url_for('_destroy_virtual_machine', instance=instance)
-        return errors.resource_not_found(func_route)
-
-    args = {
-        'id': virtual_machine_id
-    }
-    return requester.make_request(
-        'destroyVirtualMachine',
-        args,
-        authorization.client_id,
-        authorization.client_secret
-    )
-
-
-def _cloudstack_virtual_machine_to_gce(cloudstack_response, zone, projectid):
+def _cloudstack_virtual_machine_to_gce(cloudstack_response, projectid, zone, **kwargs):
     response = {}
     response['kind'] = 'compute#instance'
     response['id'] = cloudstack_response['id']
@@ -118,23 +85,24 @@
     response['disks'] = []
 
     networking = {}
-    accessconfig = {}
+    accessconfig = []
+    accessconfig.append({})
     if 'securitygroup' in cloudstack_response:
         networking['network'] = cloudstack_response['securitygroup'][0]['name']
         networking['networkIP'] = cloudstack_response['nic'][0]['ipaddress']
         networking['name'] = cloudstack_response['nic'][0]['id']
-        accessconfig['natIP'] = cloudstack_response['nic'][0]['ipaddress']
+        accessconfig[0]['natIP'] = cloudstack_response['nic'][0]['ipaddress']
         networking['accessConfigs'] = []
 
-    accessconfig['kind'] = 'compute#accessConfig'
-    accessconfig['type'] = 'ONE_TO_ONE_NAT'
-    accessconfig['name'] = 'External NAT'
+    accessconfig[0]['kind'] = 'compute#accessConfig'
+    accessconfig[0]['type'] = 'ONE_TO_ONE_NAT'
+    accessconfig[0]['name'] = 'External NAT'
 
     networking['accessConfigs'] = accessconfig
 
     response['networkInterfaces'].append(networking)
 
-    response['selfLink'] = urllib.unquote_plus(helper.get_root_url() + url_for(
+    response['selfLink'] = urllib.unquote_plus(helpers.get_root_url() + url_for(
         'getinstance',
         projectid=projectid,
         instance=cloudstack_response['name'],
@@ -145,65 +113,14 @@
     return response
 
 
-def _get_virtual_machine_by_name(authorization, instance):
-    virtual_machine_list = _get_virtual_machines(
-        authorization=authorization,
-        args={
-            'keyword': instance
-        }
-    )
-
-    if virtual_machine_list['listvirtualmachinesresponse']:
-        response = helper.filter_by_name(
-            data=virtual_machine_list['listvirtualmachinesresponse']['virtualmachine'],
-            name=instance
-        )
-        return response
-    else:
-        return None
-
-
 @app.route('/compute/v1/projects/<projectid>/aggregated/instances', methods=['GET'])
 @authentication.required
 def aggregatedlistinstances(authorization, projectid):
-    zone_list = zones.get_zone_names(authorization=authorization)
-    virtual_machine_list = _get_virtual_machines(authorization=authorization)
-
-    instance = None
-    filter = helper.get_filter(request.args)
-
-    if 'name' in filter:
-        instance = filter['name']
-
-    items = {}
-
-    for zone in zone_list:
-        zone_instances = []
-        if instance:
-            virtual_machine = _get_virtual_machine_by_name(
-                authorization=authorization,
-                instance=instance
-            )
-            if virtual_machine:
-                zone_instances.append(
-                    _cloudstack_virtual_machine_to_gce(
-                        cloudstack_response=virtual_machine,
-                        projectid=projectid,
-                        zone=zone
-                    )
-                )
-
-        elif virtual_machine_list['listvirtualmachinesresponse']:
-            for instance in virtual_machine_list['listvirtualmachinesresponse']['virtualmachine']:
-                zone_instances.append(
-                    _cloudstack_virtual_machine_to_gce(
-                        cloudstack_response=instance,
-                        projectid=projectid,
-                        zone=zone
-                    )
-                )
-        items['zone/' + zone] = {}
-        items['zone/' + zone]['instances'] = zone_instances
+    args = {'command': 'listVirtualMachines'}
+    kwargs = {'projectid': projectid}
+    items = controllers.describe_items_aggregated(
+        authorization, args, 'virtualmachine', 'instances',
+        _cloudstack_virtual_machine_to_gce, **kwargs)
 
     populated_response = {
         'kind': 'compute#instanceAggregatedList',
@@ -211,45 +128,17 @@
         'selfLink': request.base_url,
         'items': items
     }
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/zones/<zone>/instances', methods=['GET'])
 @authentication.required
 def listinstances(authorization, projectid, zone):
-    instance = None
-    filter = helper.get_filter(request.args)
-
-    if 'name' in filter:
-        instance = filter['name']
-
-    items = []
-
-    if instance:
-        virtual_machine = _get_virtual_machine_by_name(
-            authorization=authorization,
-            instance=instance
-        )
-        if virtual_machine:
-            items.append(
-                _cloudstack_virtual_machine_to_gce(
-                    cloudstack_response=virtual_machine,
-                    projectid=projectid,
-                    zone=zone
-                )
-            )
-    else:
-        virtual_machine_list = _get_virtual_machines(
-            authorization=authorization)
-        if virtual_machine_list['listvirtualmachinesresponse']:
-            for instance in virtual_machine_list['listvirtualmachinesresponse']['virtualmachine']:
-                items.append(
-                    _cloudstack_virtual_machine_to_gce(
-                        cloudstack_response=instance,
-                        projectid=projectid,
-                        zone=zone,
-                    )
-                )
+    args = {'command': 'listVirtualMachines'}
+    kwargs = {'projectid': projectid, 'zone': zone}
+    items = controllers.describe_items(
+        authorization, args, 'virtualmachine',
+        _cloudstack_virtual_machine_to_gce, **kwargs)
 
     populated_response = {
         'kind': 'compute#instance_list',
@@ -258,32 +147,7 @@
         'items': items
     }
 
-    return helper.create_response(data=populated_response)
-
-
-@app.route('/compute/v1/projects/<projectid>/zones/<zone>/instances/<instance>', methods=['GET'])
-@authentication.required
-def getinstance(projectid, authorization, zone, instance):
-    response = _get_virtual_machine_by_name(
-        authorization=authorization,
-        instance=instance
-    )
-
-    if response:
-        return helper.create_response(
-            data=_cloudstack_virtual_machine_to_gce(
-                cloudstack_response=response,
-                projectid=projectid,
-                zone=zone
-            )
-        )
-    else:
-        function_route = url_for(
-            'getinstance',
-            projectid=projectid,
-            zone=zone,
-            instance=instance)
-        return errors.resource_not_found(function_route)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/zones/<zone>/instances', methods=['POST'])
@@ -293,7 +157,6 @@
     args = {}
     args['name'] = data['name']
     args['serviceoffering'] = data['machineType'].rsplit('/', 1)[1]
-    print args['serviceoffering']
     args['template'] = data['disks'][0]['initializeParams']['sourceImage'].rsplit('/', 1)[1]
     args['zone'] = zone
 
@@ -304,20 +167,9 @@
     deployment_result = _deploy_virtual_machine(authorization, args, projectid)
 
     if 'errortext' in deployment_result['deployvirtualmachineresponse']:
-        populated_response = {
-            'kind': 'compute#operation',
-            'operationType': 'insert',
-            'targetLink': '',
-            'status': 'DONE',
-            'progress': 100,
-            'error': {
-                'errors': [{
-                    'code': 'RESOURCE_ALREADY_EXISTS',
-                    'message': 'the resource \'projects/\'' + projectid +
-                               '/zones/' + zone + '/instances/' + args['name']
-                }]
-            }
-        }
+        func_route = url_for('addinstance', projectid=projectid, zone=zone)
+        return errors.resource_not_found(func_route)
+
     else:
         populated_response = operations.create_response(
             projectid=projectid,
@@ -325,13 +177,24 @@
             authorization=authorization
         )
 
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/zones/<zone>/instances/<instance>', methods=['DELETE'])
 @authentication.required
 def deleteinstance(projectid, authorization, zone, instance):
-    deletion_result = _destroy_virtual_machine(authorization, instance)
+    args = {'command': 'listVirtualMachines'}
+    virtual_machine = controllers.get_item_with_name(authorization, instance, args, 'virtualmachine')
+
+    virtual_machine_id = virtual_machine['id']
+    args = {'id': virtual_machine_id}
+
+    deletion_result = requester.make_request(
+        'destroyVirtualMachine',
+        args,
+        authorization.client_id,
+        authorization.client_secret
+    )
 
     populated_response = operations.create_response(
         projectid=projectid,
@@ -339,4 +202,15 @@
         authorization=authorization
     )
 
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
+
+
+@app.route('/compute/v1/projects/<projectid>/zones/<zone>/instances/<instance>', methods=['GET'])
+@authentication.required
+def getinstance(projectid, authorization, zone, instance):
+    func_route = url_for('getinstance', projectid=projectid, zone=zone, instance=instance)
+    args = {'command': 'listVirtualMachines'}
+    kwargs = {'projectid': projectid, 'zone': zone}
+    return controllers.get_item_with_name_or_error(
+        authorization, instance, args, 'virtualmachine', func_route,
+        _cloudstack_virtual_machine_to_gce, **kwargs)
diff --git a/gstack/controllers/machine_type.py b/gstack/controllers/machine_type.py
index f55e211..b78a6c5 100755
--- a/gstack/controllers/machine_type.py
+++ b/gstack/controllers/machine_type.py
@@ -20,42 +20,17 @@
 import urllib
 from gstack import app
 from gstack import authentication
-from gstack.services import requester
-from gstack.controllers import errors, zones, helper
+from gstack import helpers
+from gstack import controllers
 from flask import request, url_for
 
 
-def _get_machinetypes(authorization, args=None):
-    command = 'listServiceOfferings'
-    if not args:
-        args = {}
-
-    cloudstack_response = requester.make_request(
-        command,
-        args,
-        authorization.client_id,
-        authorization.client_secret
-    )
-    return cloudstack_response
-
-
 def get_machinetype_by_name(authorization, machinetype):
-    machinetype_list = _get_machinetypes(
-        authorization=authorization
-    )
-
-    if machinetype_list['listserviceofferingsresponse']:
-        response = helper.filter_by_name(
-            data=machinetype_list['listserviceofferingsresponse'][
-                'serviceoffering'],
-            name=machinetype
-        )
-        return response
-    else:
-        return None
+    args = {'command': 'listServiceOfferings'}
+    return controllers.get_item_with_name(authorization, machinetype, args, 'serviceoffering')
 
 
-def _cloudstack_machinetype_to_gce(cloudstack_response, projectid, zone):
+def _cloudstack_service_offering_to_gce(cloudstack_response, projectid, zone):
     response = {}
     response['kind'] = 'compute#machineType'
     response['name'] = cloudstack_response['name']
@@ -65,7 +40,7 @@
     response['guestCpus'] = cloudstack_response['cpunumber']
     response['memoryMb'] = cloudstack_response['memory']
 
-    response['selfLink'] = urllib.unquote_plus(helper.get_root_url() + url_for(
+    response['selfLink'] = urllib.unquote_plus(helpers.get_root_url() + url_for(
         'getmachinetype',
         projectid=projectid,
         machinetype=cloudstack_response['name'],
@@ -79,27 +54,11 @@
 @app.route('/compute/v1/projects/<projectid>/aggregated/machineTypes', methods=['GET'])
 @authentication.required
 def aggregatedlistmachinetypes(projectid, authorization):
-    machine_types = _get_machinetypes(authorization)
-    zonelist = zones.get_zone_names(authorization)
-
-    items = {}
-    for zone in zonelist:
-        zone_machine_types = []
-        if machine_types['listserviceofferingsresponse']:
-            for machineType in machine_types['listserviceofferingsresponse']['serviceoffering']:
-                zone_machine_types.append(
-                    _cloudstack_machinetype_to_gce(
-                        cloudstack_response=machineType,
-                        projectid=projectid,
-                        zone=zone
-                    )
-                )
-        else:
-            zone_machine_types.append(errors.no_results_found(zone))
-
-        items['zone/' + zone] = {}
-        items['zone/' + zone]['zone'] = zone
-        items['zone/' + zone]['machineTypes'] = zone_machine_types
+    args = {'command': 'listServiceOfferings'}
+    kwargs = {'projectid': projectid}
+    items = controllers.describe_items_aggregated(
+        authorization, args, 'serviceoffering', 'machineTypes',
+        _cloudstack_service_offering_to_gce, **kwargs)
 
     populated_response = {
         'kind': 'compute#machineTypeAggregatedList',
@@ -107,50 +66,17 @@
         'selfLink': urllib.unquote_plus(request.base_url),
         'items': items
     }
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/zones/<zone>/machineTypes', methods=['GET'])
 @authentication.required
 def listmachinetype(projectid, authorization, zone):
-    machinetype = None
-    filter = helper.get_filter(request.args)
-
-    if 'name' in filter:
-        machinetype = filter['name']
-
-    items = []
-
-    if machinetype:
-        machinetype_list = _get_machinetypes(
-            authorization=authorization,
-            args={'keyword': machinetype}
-        )
-        if machinetype_list['listserviceofferingsresponse']:
-            machinetype = helper.filter_by_name(
-                data=machinetype_list['listserviceofferingsresponse'][
-                    'serviceoffering'],
-                name=machinetype
-            )
-            if machinetype:
-                items.append(
-                    _cloudstack_machinetype_to_gce(
-                        cloudstack_response=machinetype,
-                        projectid=projectid,
-                        zone=zone
-                    )
-                )
-    else:
-        machinetype_list = _get_machinetypes(authorization=authorization)
-        if machinetype_list['listserviceofferingsresponse']:
-            for machinetype in machinetype_list['listserviceofferingsresponse']['serviceoffering']:
-                items.append(
-                    _cloudstack_machinetype_to_gce(
-                        cloudstack_response=machinetype,
-                        projectid=projectid,
-                        zone=zone
-                    )
-                )
+    args = {'command': 'listServiceOfferings'}
+    kwargs = {'projectid': projectid, 'zone': zone}
+    items = controllers.describe_items(
+        authorization, args, 'serviceoffering',
+        _cloudstack_service_offering_to_gce, **kwargs)
 
     populated_response = {
         'kind': 'compute#imageList',
@@ -159,29 +85,15 @@
         'items': items
     }
 
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/zones/<zone>/machineTypes/<machinetype>', methods=['GET'])
 @authentication.required
 def getmachinetype(projectid, authorization, zone, machinetype):
-    response = get_machinetype_by_name(
-        authorization=authorization,
-        machinetype=machinetype
-    )
-
-    if response:
-        return helper.create_response(
-            data=_cloudstack_machinetype_to_gce(
-                cloudstack_response=response,
-                projectid=projectid,
-                zone=zone
-            )
-        )
-    else:
-        func_route = url_for(
-            'getmachinetype',
-            projectid=projectid,
-            machinetype=machinetype,
-            zone=zone)
-        return errors.resource_not_found(func_route)
+    func_route = url_for('getmachinetype', projectid=projectid, zone=zone, machinetype=machinetype)
+    args = {'command': 'listServiceOfferings'}
+    kwargs = {'projectid': projectid, 'zone': zone}
+    return controllers.get_item_with_name_or_error(
+        authorization, machinetype, args, 'serviceoffering', func_route,
+        _cloudstack_service_offering_to_gce, **kwargs)
diff --git a/gstack/controllers/networks.py b/gstack/controllers/networks.py
index 03a1799..d2b5fd4 100644
--- a/gstack/controllers/networks.py
+++ b/gstack/controllers/networks.py
@@ -19,42 +19,17 @@
 
 import urllib
 import json
+from gstack import helpers
+from gstack import controllers
 from flask import request, url_for
 from gstack import app, authentication
 from gstack.services import requester
-from gstack.controllers import helper, errors
+from gstack.controllers import errors
 
 
-def _get_networks(authorization, args=None):
-    command = 'listSecurityGroups'
-    if not args:
-        args = {}
-    cloudstack_response = requester.make_request(
-        command,
-        args,
-        authorization.client_id,
-        authorization.client_secret
-    )
-
-    return cloudstack_response
-
-
-def get_network_by_name(authorization, securitygroup):
-    securitygroup_list = _get_networks(
-        authorization=authorization,
-        args={
-            'keyword': securitygroup
-        }
-    )
-
-    if securitygroup_list['listsecuritygroupsresponse']:
-        response = helper.filter_by_name(
-            data=securitygroup_list['listsecuritygroupsresponse']['securitygroup'],
-            name=securitygroup
-        )
-        return response
-    else:
-        return None
+def get_network_by_name(authorization, network):
+    args = {'command': 'listSecurityGroups'}
+    return controllers.get_item_with_name(authorization, network, args, 'securitygroup')
 
 
 def _add_network(authorization, args=None):
@@ -73,8 +48,8 @@
 
 
 def _delete_network(authorization, projectid, network):
-    network_response = get_network_by_name(authorization, network)
-
+    args = {'command': 'listSecurityGroups'}
+    network_response = controllers.get_item_with_name(authorization, network, args, 'securitygroup')
     if not network_response:
         return None
 
@@ -92,17 +67,13 @@
     )
 
 
-def _cloudstack_network_to_gce(cloudstack_response, selfLink=None):
+def _cloudstack_network_to_gce(cloudstack_response):
     response = {}
     response['kind'] = 'compute#network'
     response['id'] = cloudstack_response['id']
     response['name'] = cloudstack_response['name']
     response['description'] = cloudstack_response['description']
-
-    if selfLink:
-        response['selfLink'] = urllib.unquote_plus(selfLink)
-    else:
-        response['selfLink'] = urllib.unquote_plus(request.base_url)
+    response['selfLink'] = urllib.unquote_plus(request.base_url) + '/' + response['name']
 
     return response
 
@@ -123,42 +94,28 @@
 @app.route('/compute/v1/projects/<projectid>/global/networks', methods=['GET'])
 @authentication.required
 def listnetworks(projectid, authorization):
-    securitygroup_list = _get_networks(
-        authorization=authorization
-    )
-
-    networks = []
-    if securitygroup_list['listsecuritygroupsresponse']:
-        for network in securitygroup_list['listsecuritygroupsresponse']['securitygroup']:
-            networks.append(_cloudstack_network_to_gce(
-                cloudstack_response=network,
-                selfLink=request.base_url + '/' + network['name']))
+    args = {'command': 'listSecurityGroups'}
+    kwargs = {}
+    items = controllers.describe_items(
+        authorization, args, 'securitygroup',
+        _cloudstack_network_to_gce, **kwargs)
 
     populated_response = _create_populated_network_response(
         projectid,
-        networks
+        items
     )
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/global/networks/<network>', methods=['GET'])
 @authentication.required
 def getnetwork(projectid, authorization, network):
-    response = get_network_by_name(
-        authorization=authorization,
-        securitygroup=network
-    )
-
-    if response:
-        return helper.create_response(
-            data=_cloudstack_network_to_gce(response)
-        )
-    else:
-        func_route = url_for(
-            'getnetwork',
-            projectid=projectid,
-            network=network)
-        return errors.resource_not_found(func_route)
+    func_route = url_for('getnetwork', projectid=projectid, network=network)
+    args = {'command': 'listSecurityGroups'}
+    kwargs = {}
+    return controllers.get_item_with_name_or_error(
+        authorization, network, args, 'securitygroup', func_route,
+        _cloudstack_network_to_gce, **kwargs)
 
 
 @app.route('/compute/v1/projects/<projectid>/global/networks', methods=['POST'])
@@ -190,7 +147,7 @@
             'kind': 'compute#operation',
             'operationType': 'insert',
             'targetLink': urllib.unquote_plus(
-                helper.get_root_url() + url_for(
+                helpers.get_root_url() + url_for(
                     'getnetwork',
                     projectid=projectid,
                     network=data['name']
@@ -200,7 +157,7 @@
             'progress': 100
         }
 
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/global/networks/<network>', methods=['DELETE'])
@@ -224,4 +181,4 @@
         'progress': 100
     }
 
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
diff --git a/gstack/controllers/operations.py b/gstack/controllers/operations.py
index de31b32..9bbdd46 100644
--- a/gstack/controllers/operations.py
+++ b/gstack/controllers/operations.py
@@ -20,7 +20,7 @@
 import urllib
 from gstack import app, publickey_storage
 from gstack import authentication
-from gstack.controllers import helper
+from gstack import helpers
 from gstack.services import requester
 from flask import url_for
 
@@ -43,7 +43,7 @@
         'operationType': 'delete',
         'name': async_result['jobid'],
         'startTime': async_result['created'],
-        'selfLink': urllib.unquote_plus(helper.get_root_url() + url_for(
+        'selfLink': urllib.unquote_plus(helpers.get_root_url() + url_for(
             'getoperations',
             projectid=projectid,
             operationid=async_result['jobid']
@@ -57,14 +57,14 @@
     elif async_result['jobstatus'] is 1:
         populated_response['status'] = 'DONE'
         populated_response['zone'] = urllib.unquote_plus(
-            helper.get_root_url() +
+            helpers.get_root_url() +
             url_for(
                 'getzone',
                 projectid=projectid,
                 zone=async_result['jobresult']['virtualmachine']['zonename'],
             ))
         populated_response['targetLink'] = urllib.unquote_plus(
-            helper.get_root_url() +
+            helpers.get_root_url() +
             url_for(
                 'getinstance',
                 projectid=projectid,
@@ -112,7 +112,7 @@
         'user': async_result['userid'],
         'insertTime': async_result['created'],
         'startTime': async_result['created'],
-        'selfLink': urllib.unquote_plus(helper.get_root_url() + url_for(
+        'selfLink': urllib.unquote_plus(helpers.get_root_url() + url_for(
             'getoperations',
             projectid=projectid,
             operationid=async_result['jobid']
@@ -129,14 +129,14 @@
         populated_response['status'] = 'DONE'
         populated_response['id'] = async_result['jobid']
         populated_response['zone'] = urllib.unquote_plus(
-            helper.get_root_url() +
+            helpers.get_root_url() +
             url_for(
                 'getzone',
                 projectid=projectid,
                 zone=async_result['jobresult']['virtualmachine']['zonename'],
             ))
         populated_response['targetLink'] = urllib.unquote_plus(
-            helper.get_root_url() +
+            helpers.get_root_url() +
             url_for(
                 'getinstance',
                 projectid=projectid,
@@ -182,7 +182,7 @@
 @app.route('/compute/v1/projects/<projectid>/global/operations/<operationid>', methods=['GET'])
 @authentication.required
 def getoperations(authorization, operationid, projectid):
-    return helper.create_response(create_response(
+    return helpers.create_response(create_response(
         authorization=authorization,
         operationid=operationid,
         projectid=projectid
diff --git a/gstack/controllers/project.py b/gstack/controllers/project.py
index d6e0776..35408bd 100755
--- a/gstack/controllers/project.py
+++ b/gstack/controllers/project.py
@@ -19,14 +19,21 @@
 
 from gstack import app, publickey_storage
 from gstack import authentication
+from gstack import helpers
+from gstack import controllers
 from gstack.services import requester
-from gstack.controllers import errors, helper
+from gstack.controllers import errors
 from flask import jsonify, request, url_for
 import json
 import urllib
 import collections
 
 
+def _get_account_by_name(authorization, projectid):
+    args = {'command': 'listAccounts'}
+    return controllers.get_item_with_name(authorization, projectid, args, 'account')
+
+
 def _list_ssh_keys(authorization):
     command = 'listTags'
     args = {
@@ -63,39 +70,6 @@
     return sshkeys
 
 
-def _get_accounts(authorization, args=None):
-    command = 'listAccounts'
-    if not args:
-        args = {}
-
-    cloudstack_response = requester.make_request(
-        command,
-        args,
-        authorization.client_id,
-        authorization.client_secret
-    )
-
-    return cloudstack_response
-
-
-def _get_account_by_name(authorization, projectid):
-    account_list = _get_accounts(
-        authorization=authorization,
-        args={
-            'keyword': projectid
-        }
-    )
-
-    if account_list['listaccountsresponse']:
-        response = helper.filter_by_name(
-            data=account_list['listaccountsresponse']['account'],
-            name=projectid
-        )
-        return response
-    else:
-        return None
-
-
 def _format_quota(metric, limit, usage):
     quota = {}
     quota['metric'] = metric
@@ -213,7 +187,7 @@
     res = jsonify({
         "kind": "compute#operation",
         'operationType': 'setMetadata',
-        'targetLink': urllib.unquote_plus(helper.get_root_url() + url_for(
+        'targetLink': urllib.unquote_plus(helpers.get_root_url() + url_for(
             'getproject',
             projectid=projectid
         )),
diff --git a/gstack/controllers/regions.py b/gstack/controllers/regions.py
index 50f3e5d..dbaf5b7 100755
--- a/gstack/controllers/regions.py
+++ b/gstack/controllers/regions.py
@@ -19,31 +19,18 @@
 
 
 from gstack import app
+from gstack import helpers
+from gstack import controllers
 from gstack import authentication
-from gstack.services import requester
-from gstack.controllers import errors, helper
 from flask import request, url_for
 
 
-def _get_regions(authorization, args=None):
-    command = 'listRegions'
-    if not args:
-        args = {}
-
-    cloudstack_response = requester.make_request(
-        command,
-        args,
-        authorization.client_id,
-        authorization.client_secret
-    )
-    return cloudstack_response
-
-
-def _cloudstack_region_to_gce(response_item):
+def _cloudstack_account_to_gce(cloudstack_response):
     response = {}
     response['kind'] = 'compute#region'
-    response['description'] = response_item['name']
-    response['id'] = response_item['id']
+    response['description'] = cloudstack_response['name']
+    response['name'] = cloudstack_response['name']
+    response['id'] = cloudstack_response['id']
     response['status'] = 'UP'
     return response
 
@@ -51,37 +38,26 @@
 @app.route('/compute/v1/projects/<projectid>/regions', methods=['GET'])
 @authentication.required
 def listregions(projectid, authorization):
-    cloudstack_response = _get_regions(authorization)
-
-    regions = []
-
-    if cloudstack_response['listregionsresponse']:
-        for region in cloudstack_response['listregionsresponse']['region']:
-            regions.append(_cloudstack_region_to_gce(region))
+    args = {'command': 'listRegions'}
+    kwargs = {}
+    items = controllers.describe_items(
+        authorization, args, 'region',
+        _cloudstack_account_to_gce, **kwargs)
 
     populated_response = {
         'kind': 'compute#regionList',
         'id': 'projects/' + projectid + '/regions',
         'selfLink': request.base_url,
-        'items': regions
+        'items': items
     }
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/regions/<region>', methods=['GET'])
 @authentication.required
 def getregion(projectid, authorization, region):
-    cloudstack_response = _get_regions(
-        authorization=authorization,
-        args={'name': region}
-    )
-
-    if region == cloudstack_response['listregionsresponse']['region'][0]['name']:
-        return helper.create_response(
-            data=_cloudstack_region_to_gce(
-                cloudstack_response['listregionsresponse']['region'][0]
-            )
-        )
-    else:
-        function_route = url_for('getregion', projectid=projectid, region=region)
-        return errors.resource_not_found(function_route)
+    func_route = url_for('getregion', projectid=projectid, region=region)
+    args = {'command': 'listRegions'}
+    return controllers.get_item_with_name_or_error(
+        authorization, region, args, 'region', func_route,
+        _cloudstack_account_to_gce, **{})
diff --git a/gstack/controllers/zones.py b/gstack/controllers/zones.py
index 8e433f3..8e9985c 100755
--- a/gstack/controllers/zones.py
+++ b/gstack/controllers/zones.py
@@ -18,15 +18,20 @@
 # under the License.
 
 from flask import request, url_for
+from gstack import helpers
+from gstack import controllers
 from gstack import app, authentication
 from gstack.services import requester
-from gstack.controllers import helper, errors
 
 
-def _get_zones(authorization, args=None):
+def get_zone_by_name(authorization, zone):
+    args = {'command': 'listZones'}
+    return controllers.get_item_with_name(authorization, zone, args, 'zone')
+
+
+def _get_zones(authorization):
     command = 'listZones'
-    if not args:
-        args = {}
+    args = {}
     cloudstack_response = requester.make_request(
         command,
         args,
@@ -37,29 +42,9 @@
     return cloudstack_response
 
 
-def get_zone_by_name(authorization, zone):
-    zone_list = _get_zones(
-        authorization=authorization,
-        args={
-            'keyword': zone
-        }
-    )
-
-    if zone_list['listzonesresponse']:
-        response = helper.filter_by_name(
-            data=zone_list['listzonesresponse']['zone'],
-            name=zone
-        )
-        return response
-    else:
-        return None
-
-
 def get_zone_names(authorization):
     zone_list = _get_zones(authorization)
 
-    print zone_list
-
     zones = []
     if zone_list['listzonesresponse']:
         for zone in zone_list['listzonesresponse']['zone']:
@@ -68,17 +53,13 @@
     return zones
 
 
-def _cloudstack_zone_to_gce(response_item):
-    translate_zone_status = {
-        'Enabled': 'UP',
-        'Disabled': 'DOWN'
-    }
+def _cloudstack_zone_to_gce(cloudstack_response):
     return ({
         'kind': 'compute#zone',
-        'name': response_item['name'],
-        'description': response_item['name'],
-        'id': response_item['id'],
-        'status': translate_zone_status[str(response_item['allocationstate'])]
+        'name': cloudstack_response['name'],
+        'description': cloudstack_response['name'],
+        'id': cloudstack_response['id'],
+        'status': cloudstack_response['allocationstate']
     })
 
 
@@ -99,21 +80,14 @@
         'items': items
     }
 
-    return helper.create_response(data=populated_response)
+    return helpers.create_response(data=populated_response)
 
 
 @app.route('/compute/v1/projects/<projectid>/zones/<zone>', methods=['GET'])
 @authentication.required
 def getzone(projectid, authorization, zone):
-    response = get_zone_by_name(
-        authorization=authorization,
-        zone=zone
-    )
-
-    if response:
-        return helper.create_response(
-            data=_cloudstack_zone_to_gce(response)
-        )
-    else:
-        func_route = url_for('getzone', projectid=projectid, zone=zone)
-        return errors.resource_not_found(func_route)
+    func_route = url_for('getzone', projectid=projectid, zone=zone)
+    args = {'command': 'listZones'}
+    return controllers.get_item_with_name_or_error(
+        authorization, zone, args, 'zone', func_route,
+        _cloudstack_zone_to_gce, **{})
diff --git a/gstack/core.py b/gstack/core.py
new file mode 100644
index 0000000..2fb3de1
--- /dev/null
+++ b/gstack/core.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# encoding: utf-8
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from flask_sqlalchemy import SQLAlchemy
+
+db = SQLAlchemy()
+
+
+class Service(object):
+    __model__ = None
+
+    def _isinstance(self, model, raise_error=True):
+        valid_type = isinstance(model, self.__model__)
+        if not valid_type and raise_error:
+            raise ValueError('%s is not of type %s' % (model, self.__model__))
+        return valid_type
+
+    def save(self, model):
+        self._isinstance(model)
+        db.session.add(model)
+        db.session.commit()
+        return model
+
+    def get(self, primarykey):
+        return self.__model__.query.get(primarykey)
+
+    def create(self, **kwargs):
+        return self.save(self.__model__(**kwargs))
+
+    def delete(self, model):
+        self._isinstance(model)
+        db.session.delete(model)
+        db.session.commit()
diff --git a/gstack/data/app.db b/gstack/data/app.db
deleted file mode 100644
index 5d29940..0000000
--- a/gstack/data/app.db
+++ /dev/null
Binary files differ
diff --git a/gstack/helpers.py b/gstack/helpers.py
index d8dddd9..76e73c2 100644
--- a/gstack/helpers.py
+++ b/gstack/helpers.py
@@ -18,6 +18,38 @@
 # under the License.
 
 import os
+import urllib
+from gstack import app
+from flask import jsonify
+
+
+def create_response(data):
+    res = jsonify(data)
+    res.status_code = 200
+
+    return res
+
+
+def create_errored_response(data, status_code):
+    res = jsonify(data)
+    res.status_code = status_code
+
+    return res
+
+
+def get_filter(data):
+    filter = {}
+
+    if 'filter' in data:
+        filter = urllib.unquote_plus(data['filter'])
+        filter = dict(filter.split(' eq ') for values in filter)
+
+    return filter
+
+
+def get_root_url():
+    return 'https://' + \
+        app.config['GSTACK_BIND_ADDRESS'] + ':' + app.config['GSTACK_PORT']
 
 
 def read_file(name):
diff --git a/gstack/models/__init__.py b/gstack/models/__init__.py
index 7cf0837..a9d480d 100644
--- a/gstack/models/__init__.py
+++ b/gstack/models/__init__.py
@@ -16,3 +16,20 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+
+from gstack.core import Service
+from gstack.models.accesstoken import AccessToken
+from gstack.models.refreshtoken import RefreshToken
+from gstack.models.client import Client
+
+
+class AccessTokenService(Service):
+    __model__ = AccessToken
+
+
+class RefreshTokenService(Service):
+    __model__ = RefreshToken
+
+
+class ClientService(Service):
+    __model__ = Client
diff --git a/gstack/models/accesstoken.py b/gstack/models/accesstoken.py
index 8acac57..cb4d159 100644
--- a/gstack/models/accesstoken.py
+++ b/gstack/models/accesstoken.py
@@ -21,7 +21,14 @@
 
 
 class AccessToken(db.Model):
+    __tablename__ = 'accesstoken'
     access_token = db.Column(db.String(100), primary_key=True, unique=True)
     client_id = db.Column(db.String(100), unique=True)
     expires_in = db.Column(db.Integer)
     data = db.Column(db.String(500))
+
+    def __init__(self, access_token, client_id, expires_in, data):
+        self.access_token = access_token
+        self.client_id = client_id
+        self.expires_in = expires_in
+        self.data = data
diff --git a/gstack/models/client.py b/gstack/models/client.py
index f6d03dd..00a9fe6 100644
--- a/gstack/models/client.py
+++ b/gstack/models/client.py
@@ -21,5 +21,10 @@
 
 
 class Client(db.Model):
+    __tablename__ = 'client'
     client_id = db.Column(db.String(100), primary_key=True, unique=True)
     client_secret = db.Column(db.String(100), unique=True)
+
+    def __init__(self, client_id, client_secret):
+        self.client_id = client_id
+        self.client_secret = client_secret
diff --git a/gstack/models/refreshtoken.py b/gstack/models/refreshtoken.py
index 0fba279..9b8bab0 100644
--- a/gstack/models/refreshtoken.py
+++ b/gstack/models/refreshtoken.py
@@ -21,6 +21,12 @@
 
 
 class RefreshToken(db.Model):
+    __tablename__ = 'refreshtoken'
     refresh_token = db.Column(db.String(100), primary_key=True, unique=True)
     client_id = db.Column(db.String(100), unique=True)
     data = db.Column(db.String(500))
+
+    def __init__(self, refresh_token, client_id, data):
+        self.refresh_token = refresh_token
+        self.client_id = client_id
+        self.data = data
diff --git a/gstack/oauth2provider.py b/gstack/oauth2provider.py
index 8cc2654..ce08aa1 100644
--- a/gstack/oauth2provider.py
+++ b/gstack/oauth2provider.py
@@ -55,8 +55,8 @@
                 existing_client.client_secret = client_secret
             else:
                 client = Client(
-                    client_id=client_id,
-                    client_secret=client_secret
+                    client_id,
+                    client_secret
                 )
                 db.session.add(client)
 
@@ -77,15 +77,8 @@
     def persist_authorization_code(self, client_id, code, scope):
         return
 
-    def persist_token_information(
-            self,
-            client_id,
-            scope,
-            access_token,
-            token_type,
-            expires_in,
-            refresh_token,
-            data):
+    def persist_token_information(self, client_id, scope, access_token, token_type,
+                                  expires_in, refresh_token, data):
         client = Client.query.get(client_id)
         if client is not None:
             existing_access_token = AccessToken.query.filter_by(
@@ -100,8 +93,7 @@
             else:
                 db.session.add(
                     AccessToken(
-                        access_token=access_token, client_id=client_id,
-                        expires_in=expires_in, data=json.dumps(data)
+                        access_token, client_id, expires_in, json.dumps(data)
                     )
                 )
 
@@ -110,11 +102,7 @@
                 existing_refresh_token.data = json.dumps(data)
             else:
                 db.session.add(
-                    RefreshToken(
-                        refresh_token=refresh_token, client_id=client_id,
-                        data=json.dumps(data)
-                    )
-                )
+                    RefreshToken(refresh_token, client_id, json.dumps(data)))
 
             db.session.commit()
             return True
diff --git a/migrations/README b/migrations/README
new file mode 100644
index 0000000..98e4f9c
--- /dev/null
+++ b/migrations/README
@@ -0,0 +1 @@
+Generic single-database configuration.
\ No newline at end of file
diff --git a/migrations/alembic.ini b/migrations/alembic.ini
new file mode 100644
index 0000000..a2a4775
--- /dev/null
+++ b/migrations/alembic.ini
@@ -0,0 +1,57 @@
+# A generic, single database configuration.
+
+[alembic]
+# path to migration scripts
+script_location = .
+
+# template used to generate migration files
+# file_template = %%(rev)s_%%(slug)s
+
+# max length of characters to apply to the
+# "slug" field
+#truncate_slug_length = 40
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+# set to 'true' to allow .pyc and .pyo files without
+# a source .py file to be detected as revisions in the
+# versions/ directory
+# sourceless = false
+
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/migrations/env.py b/migrations/env.py
new file mode 100644
index 0000000..cab0b22
--- /dev/null
+++ b/migrations/env.py
@@ -0,0 +1,77 @@
+from __future__ import with_statement
+from alembic import context
+from sqlalchemy import engine_from_config, pool
+from logging.config import fileConfig
+
+import os
+from gstack.core import db
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+fileConfig(config.config_file_name)
+
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+database_file = os.path.join(
+    os.path.expanduser('~'),
+    '.gstack/gstack.sqlite'
+)
+
+config.set_main_option('sqlalchemy.url', 'sqlite:///' + database_file)
+
+
+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.
+
+    """
+    url = config.get_main_option("sqlalchemy.url")
+    context.configure(url=url, target_metadata=target_metadata)
+
+    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 = engine_from_config(
+                config.get_section(config.config_ini_section),
+                prefix='sqlalchemy.',
+                poolclass=pool.NullPool)
+
+    connection = engine.connect()
+    context.configure(
+                connection=connection,
+                target_metadata=db.metadata
+                )
+
+    try:
+        with context.begin_transaction():
+            context.run_migrations()
+    finally:
+        connection.close()
+
+if context.is_offline_mode():
+    run_migrations_offline()
+else:
+    run_migrations_online()
+
diff --git a/migrations/script.py.mako b/migrations/script.py.mako
new file mode 100644
index 0000000..9570201
--- /dev/null
+++ b/migrations/script.py.mako
@@ -0,0 +1,22 @@
+"""${message}
+
+Revision ID: ${up_revision}
+Revises: ${down_revision}
+Create Date: ${create_date}
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = ${repr(up_revision)}
+down_revision = ${repr(down_revision)}
+
+from alembic import op
+import sqlalchemy as sa
+${imports if imports else ""}
+
+def upgrade():
+    ${upgrades if upgrades else "pass"}
+
+
+def downgrade():
+    ${downgrades if downgrades else "pass"}
diff --git a/migrations/versions/76bb287a37d_.py b/migrations/versions/76bb287a37d_.py
new file mode 100644
index 0000000..27e6df6
--- /dev/null
+++ b/migrations/versions/76bb287a37d_.py
@@ -0,0 +1,60 @@
+"""empty message
+
+Revision ID: 76bb287a37d
+Revises: None
+Create Date: 2014-06-16 19:15:05.475000
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '76bb287a37d'
+down_revision = None
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.create_table('accesstoken',
+        sa.Column('access_token', sa.String(length=255), nullable=False),
+        sa.Column(
+            'client_id',
+            sa.String(length=255),
+            nullable=True),
+        sa.Column(
+            'expires_in',
+            sa.String(length=255),
+            nullable=True),
+        sa.Column(
+            'data',
+            sa.String(length=255),
+            nullable=True),
+        sa.PrimaryKeyConstraint('access_token'),
+        sa.UniqueConstraint('client_id')
+    )
+    op.create_table('client',
+        sa.Column('client_id', sa.String(length=255), nullable=False),
+        sa.Column(
+            'client_secret',
+            sa.String(length=255),
+            nullable=True),
+        sa.PrimaryKeyConstraint('client_id'),
+        sa.UniqueConstraint('client_secret')
+    )
+    op.create_table('refreshtoken',
+        sa.Column('refresh_token', sa.String(length=255), nullable=False),
+        sa.Column(
+            'client_id',
+            sa.String(length=255),
+            nullable=True),
+        sa.Column(
+            'data',
+            sa.String(length=255),
+            nullable=True),
+        sa.PrimaryKeyConstraint('refresh_token'),
+        sa.UniqueConstraint('client_id')
+    )
+
+
+def downgrade():
+    pass
diff --git a/pylint.rc b/pylint.rc
index 315faa8..6173b69 100644
--- a/pylint.rc
+++ b/pylint.rc
@@ -24,33 +24,7 @@
 
 [MESSAGES CONTROL]
 
-# Enable the message, report, category or checker with the given id(s). You can
-# either give multiple identifier separated by comma (,) or put this option
-# multiple time.
-#enable=
-
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifier separated by comma (,) or put this option
-# multiple time (only on the command line, not in the configuration file where
-# it should appear only once).
-#    F0401: *Unable to import %r*
-#    E0611: *No name %r in module %r*
-#    E1101: *%s %r has no %r member*
-#    W0142: *Used * or ** magic*
-#    W0212: *Access to a protected member %s of a client class*
-#   :R0201: *Method could be a function*
-#    w0703: Allow catching Exception
-#    R0801:  1: Similar lines in 2 files, badamson: had trouble disabling this locally
-#       FIXME: should be re-enabled after it's fixed
-#       hbrown: I don't think R0801 can be disabled locally
-#          http://www.logilab.org/ticket/6905
-#          pylint #6905: R0801 message cannot be disabled locally [open]
-#    R0901: Too many ancestors
-#    C0111: missing docstring
-#    C0301: line too long (too many to fix right now)
-#    C0103: invalid variable name (too many to fix right now)
-#    I0011: disabling pylint error
-disable=F0401,E0611,E1101,W0142,W0212,R0201,W0703,R0801,R0901,C0111,C0301,C0103,E1002,W0231,W0232,W0401,W0402,W0511,W0603,W0611,W0612,W0613,W0614,W0621,W0622,W0702,W0710,R0922,I0011,E1103
+disable=F0401,E0611,E1120,W0110,E1121,R0401,E1101,W0142,W0141,E1003,E1102,W0212,R0201,W0703,R0801,R0901,C0111,C0301,C0103,E1002,W0231,W0232,W0401,W0402,W0511,W0603,W0611,W0612,W0613,W0614,W0621,W0622,W0702,W0710,R0922,I0011,E1103
 
 
 
diff --git a/setup.py b/setup.py
index 576eb24..13600cf 100755
--- a/setup.py
+++ b/setup.py
@@ -45,7 +45,8 @@
     url="https://github.com/NOPping/gstack",
     platforms=("Any"),
     license="LICENSE.txt",
-    package_data={'': ['LICENSE.txt', 'data/*']},
+    package_data={'': ['LICENSE.txt', 'data/*'],
+                  'ec2stack': ['templates/*.json']},
     packages=[
         "gstack", "gstack.controllers", "gstack.models",
         "gstack.services", "gstack.data", "pyoauth2"],
@@ -55,6 +56,17 @@
         "pyopenssl",
         "Flask-SQLAlchemy",
         "flask",
+        "alembic"
+    ],
+    classifiers=[
+        'Development Status :: 3 - Alpha',
+        'Environment :: Console',
+        'Intended Audience :: System Administrators',
+        'License :: OSI Approved :: Apache Software License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Utilities',
+        'Programming Language :: Python :: 2.7',
     ],
     zip_safe=False,
     entry_points="""
diff --git a/tests/__init__.py b/tests/__init__.py
index b7a5e33..6308895 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -4,12 +4,12 @@
 from unittest import TestCase
 
 import mock
-
 import json
 
 from gstack import app, configure_app
 from gstack.helpers import read_file
 from . import settings
+from gstack.core import db
 from .utils import FlaskTestCaseMixin
 
 class GStackTestCase(TestCase):
@@ -22,6 +22,11 @@
     def _configure_app(self):
         configure_app(settings=settings)
 
+    def _unauthed_user(self):
+        response = self.get('/compute/v1/projects/exampleproject/global/images')
+        self.assert_unauthorized(response)
+
+
     def _auth_example_user(self):
         data = {}
         data['code'] = 'hjrZryvgLYo3R833NkHHV8jYmxQhsD8TjKWzOm2f'
@@ -49,11 +54,14 @@
         self._configure_app()
         self.app = app
         self.client = self.app.test_client()
-        self._auth_example_user()
         self.app_context = self.app.app_context()
         self.app_context.push()
+        db.create_all()
+        self._unauthed_user()
+        self._auth_example_user()
+
 
     def tearDown(self):
         super(GStackTestCase, self).tearDown()
+        db.drop_all()
         self.app_context.pop()
-
diff --git a/tests/data/valid_get_account.json b/tests/data/valid_get_account.json
new file mode 100644
index 0000000..65f3fcf
--- /dev/null
+++ b/tests/data/valid_get_account.json
@@ -0,0 +1,66 @@
+{
+    "secondarystoragetotal": 0,
+    "primarystorageavailable": "Unlimited",
+    "domain": "brogand93@darrenbrogan.ie",
+    "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+    "vpclimit": "Unlimited",
+    "iplimit": "20",
+    "memorytotal": 0,
+    "secondarystorageavailable": "Unlimited",
+    "vmtotal": 0,
+    "cputotal": 0,
+    "vpctotal": 0,
+    "id": "ddbdf378-e8d9-47e0-964b-661d0d8414b8",
+    "networkavailable": "Unlimited",
+    "projectlimit": "Unlimited",
+    "networklimit": "Unlimited",
+    "iptotal": -27,
+    "volumetotal": 0,
+    "snapshotlimit": "0",
+    "state": "enabled",
+    "networktotal": 0,
+    "accounttype": 0,
+    "cpuavailable": "Unlimited",
+    "primarystoragetotal": 0,
+    "templatelimit": "0",
+    "snapshottotal": 0,
+    "templateavailable": "0",
+    "vmlimit": "20",
+    "vpcavailable": "0",
+    "primarystoragelimit": "Unlimited",
+    "volumelimit": "20",
+    "templatetotal": 0,
+    "secondarystoragelimit": "Unlimited",
+    "user": [
+        {
+            "username": "accountname",
+            "account": "brogand93@darrenbrogan.ie",
+            "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+            "firstname": "brogand93",
+            "created": "2013-09-17T15:35:34+0200",
+            "lastname": "darrenbrogan.ie",
+            "iscallerchilddomain": false,
+            "domain": "brogand93@darrenbrogan.ie",
+            "email": "brogand93@darrenbrogan.ie",
+            "secretkey": "lM9fLm8XQwezvLOd10Qt3wXH7j9mRgaKbEg3nRDnj7FtlF3yx54EWd9mR5sB1ec5LQDV6gjpy6sfDo6ndUeeww",
+            "state": "enabled",
+            "apikey": "0ZyexOgzlfTx076LYBFz4oT_ShJvxUcezgvsW6gvRZ_BloSsb5uUTAm-FtHvKBvAUP0S7ZN6bIcRf4zEtbm3PQ",
+            "accounttype": 0,
+            "timezone": "CET",
+            "id": "26a772da-dc25-4f2b-b0f1-e095e3717a30",
+            "isdefault": false,
+            "accountid": "ddbdf378-e8d9-47e0-964b-661d0d8414b8"
+        }
+    ],
+    "projectavailable": "Unlimited",
+    "isdefault": false,
+    "memoryavailable": "Unlimited",
+    "projecttotal": 0,
+    "volumeavailable": "20",
+    "name": "accountname",
+    "vmavailable": "20",
+    "ipavailable": "0",
+    "memorylimit": "Unlimited",
+    "cpulimit": "Unlimited",
+    "snapshotavailable": "0"
+}
\ No newline at end of file
diff --git a/tests/data/valid_get_image.json b/tests/data/valid_get_image.json
new file mode 100644
index 0000000..3348483
--- /dev/null
+++ b/tests/data/valid_get_image.json
@@ -0,0 +1,28 @@
+{
+    "domain": "ROOT",
+    "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+    "ostypename": "CentOS 5.3 (32-bit)",
+    "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+    "displaytext": "CentOS 5.3(64-bit) no GUI (Simulator)",
+    "ostypeid": "2e678976-95e4-11e3-b2e4-d19c9d3e5e1d",
+    "passwordenabled": false,
+    "id": "a32d70ee-95e4-11e3-b2e4-d19c9d3e5e1d",
+    "size": 2147483648,
+    "isready": true,
+    "format": "VHD",
+    "templatetype": "BUILTIN",
+    "crosszones": true,
+    "zonename": "Sandbox-simulator",
+    "status": "Download Complete",
+    "isdynamicallyscalable": false,
+    "tags": [],
+    "isfeatured": true,
+    "sshkeyenabled": false,
+    "isextractable": false,
+    "account": "system",
+    "name": "imagename",
+    "created": "2014-02-15T02:50:13+0000",
+    "hypervisor": "Simulator",
+    "ispublic": true,
+    "checksum": ""
+}
\ No newline at end of file
diff --git a/tests/data/valid_get_instance.json b/tests/data/valid_get_instance.json
new file mode 100644
index 0000000..528cba4
--- /dev/null
+++ b/tests/data/valid_get_instance.json
@@ -0,0 +1,104 @@
+{
+                "domain": "brogand93@darrenbrogan.ie",
+                "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                "haenable": false,
+                "templatename": "Linux CentOS 6.5 64-bit",
+                "securitygroup": [
+                    {
+                        "egressrule": [],
+                        "account": "brogand93@darrenbrogan.ie",
+                        "description": "Default Security Group",
+                        "tags": [],
+                        "ingressrule": [],
+                        "id": "6033ff41-53ff-4443-b0bb-f6c5c0191c34",
+                        "name": "default"
+                    }
+                ],
+                "zoneid": "1128bd56-b4d9-4ac6-a7b9-c715b187ce11",
+                "keypair": "brogand93@darrenbrogan.ie",
+                "cpunumber": 1,
+                "passwordenabled": true,
+                "id": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                "displayvm": true,
+                "state": "Running",
+                "guestosid": "113038d0-a8cd-4d20-92be-ea313f87c3ac",
+                "memory": 1024,
+                "serviceofferingid": "b6cd1ff5-3a2f-4e9d-a4d1-8988c1191fe8",
+                "zonename": "ch-gva-2",
+                "isdynamicallyscalable": false,
+                "displayname": "foobar",
+                "tags": [
+                    {
+                        "account": "brogand93@darrenbrogan.ie",
+                        "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                        "resourcetype": "UserVm",
+                        "resourceid": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                        "domain": "brogand93@darrenbrogan.ie",
+                        "value": "root:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDqAuui+xCVPXaFD4cP2MuWnDlktg9vMT/SNzF17UiAzKEbxT/mNayTDAr",
+                        "key": "0-sshkey-segment"
+                    },
+                    {
+                        "account": "brogand93@darrenbrogan.ie",
+                        "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                        "resourcetype": "UserVm",
+                        "resourceid": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                        "domain": "brogand93@darrenbrogan.ie",
+                        "value": "DbY/BgGYC5bHuGlb/eE1r4EGpwSXZitGkTI4ThldrSp0Em7psO8AibdpYrFxlOmDFp9wKVD6xbY2HT1ySwvKi+ZwSR5yHcEKq15e",
+                        "key": "1-sshkey-segment"
+                    },
+                    {
+                        "account": "brogand93@darrenbrogan.ie",
+                        "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                        "resourcetype": "UserVm",
+                        "resourceid": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                        "domain": "brogand93@darrenbrogan.ie",
+                        "value": "V4eez/3qC1vIcssKmwu5+ZBneZAvWAfxHEKsQU0dsCVvHdn8g7tFXXtg4QCGtE4yzK5v3/+f1AdtIi4hvJoMyi8MV0KSa8e/ravd",
+                        "key": "2-sshkey-segment"
+                    },
+                    {
+                        "account": "brogand93@darrenbrogan.ie",
+                        "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                        "resourcetype": "UserVm",
+                        "resourceid": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                        "domain": "brogand93@darrenbrogan.ie",
+                        "value": "Hbgj44PncFBB8O6epVdXPbClZwtkz9D6GEQaOArxk9tX8YEgTFnmsnNuaoZgs7giMj2N7jQe2qXh5R0nsTTuH brogand@microv",
+                        "key": "3-sshkey-segment"
+                    },
+                    {
+                        "account": "brogand93@darrenbrogan.ie",
+                        "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                        "resourcetype": "UserVm",
+                        "resourceid": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                        "domain": "brogand93@darrenbrogan.ie",
+                        "value": "ac",
+                        "key": "4-sshkey-segment"
+                    }
+                ],
+                "nic": [
+                    {
+                        "networkid": "00304a04-c7ea-4e77-a786-18bc64347bf7",
+                        "macaddress": "06:b0:2e:00:01:04",
+                        "isolationuri": "ec2://untagged",
+                        "networkname": "guestNetworkForBasicZone",
+                        "gateway": "185.19.28.1",
+                        "traffictype": "Guest",
+                        "broadcasturi": "vlan://untagged",
+                        "netmask": "255.255.254.0",
+                        "type": "Shared",
+                        "ipaddress": "185.19.28.199",
+                        "id": "2b5cc781-d310-43cf-9d68-66719f43855d",
+                        "isdefault": true
+                    }
+                ],
+                "cpuspeed": 2198,
+                "templateid": "c34bf3f0-318b-4d77-b0ca-f20585d05d32",
+                "affinitygroup": [],
+                "account": "brogand93@darrenbrogan.ie",
+                "name": "instancename",
+                "created": "2014-06-02T21:11:52+0200",
+                "hypervisor": "KVM",
+                "rootdevicetype": "ROOT",
+                "rootdeviceid": 0,
+                "serviceofferingname": "Tiny",
+                "templatedisplaytext": "Linux CentOS 6.5 64-bit 10GB Disk"
+            }
\ No newline at end of file
diff --git a/tests/data/valid_get_security_group.json b/tests/data/valid_get_security_group.json
new file mode 100644
index 0000000..14a7412
--- /dev/null
+++ b/tests/data/valid_get_security_group.json
@@ -0,0 +1,33 @@
+{
+    "egressrule": [],
+    "account": "example-account",
+    "domainid": "66d69e46-a95b-437b-ac6c-bcaa5331999d",
+    "description": "Default Security Group",
+    "tags": [],
+    "domain": "example-account",
+    "ingressrule": [
+        {
+            "protocol": "tcp",
+            "cidr": "0.0.0.0/0",
+            "startport": 22,
+            "endport": 22,
+            "ruleid": "3d92cc70-8c84-4e8a-9989-6efcd7ff7905"
+        },
+        {
+            "protocol": "icmp",
+            "cidr": "0.0.0.0/0",
+            "ruleid": "2ba7dd7b-13b2-49ae-bf8e-26ffadd32c9e",
+            "icmpcode": 0,
+            "icmptype": 0
+        },
+        {
+            "protocol": "tcp",
+            "cidr": "0.0.0.0/0",
+            "startport": 8080,
+            "endport": 8080,
+            "ruleid": "c4562b3c-d1b0-4844-a771-3c3434e1a5d0"
+        }
+    ],
+    "id": "1f95ee9b-b291-48c1-9492-0eee632677e3",
+    "name": "networkname"
+}
\ No newline at end of file
diff --git a/tests/data/valid_get_service_offering.json b/tests/data/valid_get_service_offering.json
new file mode 100644
index 0000000..a5381c0
--- /dev/null
+++ b/tests/data/valid_get_service_offering.json
@@ -0,0 +1,16 @@
+{
+    "iscustomized": false,
+    "name": "machinetypename",
+    "created": "2013-02-08T17:20:17+0100",
+    "storagetype": "local",
+    "limitcpuuse": false,
+    "cpuspeed": 2198,
+    "offerha": false,
+    "isvolatile": false,
+    "cpunumber": 1,
+    "memory": 512,
+    "displaytext": "Micro 512mb 1cpu",
+    "issystem": false,
+    "id": "71004023-bb72-4a97-b1e9-bc66dfce9470",
+    "defaultuse": false
+}
\ No newline at end of file
diff --git a/tests/data/valid_get_zone.json b/tests/data/valid_get_zone.json
new file mode 100644
index 0000000..4f438e3
--- /dev/null
+++ b/tests/data/valid_get_zone.json
@@ -0,0 +1,10 @@
+{
+    "localstorageenabled": true,
+    "name": "zonename",
+    "zonetoken": "ccb0a60c-79c8-3230-ab8b-8bdbe8c45bb7",
+    "securitygroupsenabled": true,
+    "allocationstate": "Enabled",
+    "dhcpprovider": "VirtualRouter",
+    "networktype": "Basic",
+    "id": "1128bd56-b4d9-4ac6-a7b9-c715b187ce11"
+}
\ No newline at end of file
diff --git a/tests/instances_tests.py b/tests/instances_tests.py
index 9420adc..6f548ae 100644
--- a/tests/instances_tests.py
+++ b/tests/instances_tests.py
@@ -11,7 +11,6 @@
 class InstancesTestCase(GStackAppTestCase):
 
     def test_list_instances(self):
-
         get = mock.Mock()
         get.return_value.text = read_file('tests/data/valid_describe_instances.json')
         get.return_value.status_code = 200
@@ -23,7 +22,6 @@
         self.assert_ok(response)
 
     def test_aggregated_list_instances(self):
-
         get = mock.Mock()
         get.return_value.text = read_file('tests/data/valid_describe_instances.json')
         get.return_value.status_code = 200
@@ -42,7 +40,6 @@
         self.assert_ok(response)
 
     def test_list_instances_with_name_filter(self):
-
         get = mock.Mock()
         get.return_value.text = read_file('tests/data/valid_describe_instance.json')
         get.return_value.status_code = 200
@@ -56,7 +53,6 @@
         self.assert_ok(response)
 
     def test_aggregated_list_instances_with_name_filter(self):
-
         get = mock.Mock()
         get.return_value.text = read_file('tests/data/valid_describe_instance.json')
         get.return_value.status_code = 200
@@ -77,7 +73,6 @@
         self.assert_ok(response)
 
     def test_get_instance(self):
-
         get = mock.Mock()
         get.return_value.text = read_file('tests/data/valid_describe_instance.json')
         get.return_value.status_code = 200
@@ -89,7 +84,6 @@
         self.assert_ok(response)
 
     def test_get_instance_instance_not_found(self):
-
         get = mock.Mock()
         get.return_value.text = read_file('tests/data/empty_describe_instances.json')
         get.return_value.status_code = 200
@@ -103,13 +97,12 @@
                 in response.data
 
     def test_delete_instance(self):
-
         get = mock.Mock()
         get.return_value.text = read_file('tests/data/valid_async_destroy_vm.json')
         get.return_value.status_code = 200
 
-        get_instance_id = mock.Mock()
-        get_instance_id.return_value = {'id':'virtualmachineid'}
+        get_instance = mock.Mock()
+        get_instance.return_value = json.loads(read_file('tests/data/valid_get_instance.json'))
 
         get_async_result = mock.Mock()
         get_async_result.return_value = json.loads(read_file('tests/data/valid_run_instance.json'))
@@ -118,8 +111,8 @@
 
         with mock.patch('requests.get', get):
             with mock.patch(
-                    'gstack.controllers.instances._get_virtual_machine_by_name',
-                    get_instance_id
+                    'gstack.controllers.get_item_with_name',
+                    get_instance
                 ):
                 with mock.patch(
                     'gstack.controllers.operations._get_async_result',
@@ -172,35 +165,35 @@
         get.return_value.status_code = 200
 
         get_templates = mock.Mock()
-        get_templates.return_value = json.loads(read_file('tests/data/valid_describe_images.json'))
+        get_templates.return_value = json.loads(read_file('tests/data/valid_get_image.json'))
 
         get_zones = mock.Mock()
-        get_zones.return_value = json.loads(read_file('tests/data/valid_describe_zone.json'))
+        get_zones.return_value = json.loads(read_file('tests/data/valid_get_zone.json'))
 
         get_service_offerings = mock.Mock()
-        get_service_offerings.return_value = json.loads(read_file('tests/data/valid_describe_service_offering.json'))
+        get_service_offerings.return_value = json.loads(read_file('tests/data/valid_get_service_offering.json'))
 
         get_networks = mock.Mock()
-        get_networks.return_value = json.loads(read_file('tests/data/valid_describe_security_group.json'))
+        get_networks.return_value = json.loads(read_file('tests/data/valid_get_security_group.json'))
 
         get_async_result = mock.Mock()
         get_async_result.return_value = json.loads(read_file('tests/data/valid_run_instance.json'))
 
         with mock.patch('requests.get', get):
             with mock.patch(
-                'gstack.controllers.images._get_templates',
+                'gstack.controllers.images.get_template_by_name',
                 get_templates
             ):
                 with mock.patch(
-                    'gstack.controllers.zones._get_zones',
+                    'gstack.controllers.zones.get_zone_by_name',
                     get_zones
                 ):
                      with mock.patch(
-                        'gstack.controllers.machine_type._get_machinetypes',
+                        'gstack.controllers.machine_type.get_machinetype_by_name',
                         get_service_offerings
                     ):
                          with mock.patch(
-                            'gstack.controllers.networks._get_networks',
+                            'gstack.controllers.networks.get_network_by_name',
                             get_networks
                         ):
                             with mock.patch(
diff --git a/tests/networks_tests.py b/tests/networks_tests.py
index edb8d33..d88aa82 100644
--- a/tests/networks_tests.py
+++ b/tests/networks_tests.py
@@ -100,10 +100,10 @@
         get.return_value.status_code = 200
 
         get_networks = mock.Mock()
-        get_networks.return_value = json.loads(read_file('tests/data/valid_describe_security_group.json'))
+        get_networks.return_value = json.loads(read_file('tests/data/valid_get_security_group.json'))
 
         with mock.patch('requests.get', get):
-            with mock.patch('gstack.controllers.networks._get_networks', get_networks):
+            with mock.patch('gstack.controllers.get_item_with_name', get_networks):
              headers = {
                  'authorization': 'Bearer ' + str(GStackAppTestCase.access_token),
              }
@@ -119,10 +119,10 @@
         get.return_value.status_code = 200
 
         get_networks = mock.Mock()
-        get_networks.return_value = json.loads(read_file('tests/data/valid_describe_security_group.json'))
+        get_networks.return_value = None
 
         with mock.patch('requests.get', get):
-            with mock.patch('gstack.controllers.networks._get_networks', get_networks):
+            with mock.patch('gstack.controllers.get_item_with_name', get_networks):
              headers = {
                  'authorization': 'Bearer ' + str(GStackAppTestCase.access_token),
              }
diff --git a/tests/project_tests.py b/tests/project_tests.py
index d3a58ee..9576ffd 100644
--- a/tests/project_tests.py
+++ b/tests/project_tests.py
@@ -11,14 +11,14 @@
 
     def test_get_project(self):
         get = mock.Mock()
-        get.return_value = json.loads(read_file('tests/data/valid_describe_account.json'))
+        get.return_value = json.loads(read_file('tests/data/valid_get_account.json'))
 
         get_tags = mock.Mock()
         get_tags.return_value.text = read_file('tests/data/valid_describe_tags.json')
         get_tags.return_value.status_code = 200
 
         with mock.patch('requests.get', get_tags):
-            with(mock.patch('gstack.controllers.project._get_accounts', get)):
+            with(mock.patch('gstack.controllers.get_item_with_name', get)):
                 headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
                 response = self.get('/compute/v1/projects/accountname', headers=headers)
 
@@ -33,7 +33,7 @@
         get_tags.return_value.status_code = 200
 
         with mock.patch('requests.get', get_tags):
-            with(mock.patch('gstack.controllers.project._get_accounts', get)):
+            with(mock.patch('gstack.controllers._get_items', get)):
                 headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
                 response = self.get('/compute/v1/projects/invalidaccountname', headers=headers)
 
diff --git a/tests/settings.py b/tests/settings.py
index 867bf91..278c50d 100644
--- a/tests/settings.py
+++ b/tests/settings.py
@@ -7,5 +7,6 @@
 CLOUDSTACK_PATH = '/compute'
 
 DEBUG=False
-
 TESTING = True
+
+SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
diff --git a/tests/utils.py b/tests/utils.py
index 60f57b1..c442322 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -55,3 +55,6 @@
     def assert_not_found(self, response):
         return self.assert_status_code(response, 404)
 
+    def assert_unauthorized(self, response):
+        return self.assert_status_code(response, 401)
+