<html>
  <head>
    <meta content="text/html; charset=ISO-8859-1"
      http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <div class="moz-cite-prefix">On 12/24/2013 04:19 AM, Mark Wu wrote:<br>
    </div>
    <blockquote cite="mid:52B92773.4010900@linux.vnet.ibm.com"
      type="cite">On 12/24/2013 02:41 AM, Aline Manera wrote:
      <br>
      <blockquote type="cite">From: Aline Manera
        <a class="moz-txt-link-rfc2396E" href="mailto:alinefm@br.ibm.com">&lt;alinefm@br.ibm.com&gt;</a>
        <br>
        <br>
        Resource(), Collection() and AsyncCollection classes are base
        for all Kimchi
        <br>
        resources.
        <br>
        Move them to a separated file as they should not be changed with
        high frequency
        <br>
        <br>
        Signed-off-by: Aline Manera <a class="moz-txt-link-rfc2396E" href="mailto:alinefm@br.ibm.com">&lt;alinefm@br.ibm.com&gt;</a>
        <br>
        ---
        <br>
        &nbsp; Makefile.am&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp; 1 +
        <br>
        &nbsp; src/kimchi/control/base.py |&nbsp; 290
        ++++++++++++++++++++++++++++++++++++++++++++
        <br>
        &nbsp; 2 files changed, 291 insertions(+)
        <br>
        &nbsp; create mode 100644 src/kimchi/control/base.py
        <br>
        <br>
        diff --git a/Makefile.am b/Makefile.am
        <br>
        index 65b39b8..997d4cc 100644
        <br>
        --- a/Makefile.am
        <br>
        +++ b/Makefile.am
        <br>
        @@ -45,6 +45,7 @@ PEP8_WHITELIST = \
        <br>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; src/kimchi/disks.py \
        <br>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; src/kimchi/root.py \
        <br>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; src/kimchi/server.py \
        <br>
        +&nbsp;&nbsp;&nbsp; src/kimchi/control/base.py \
        <br>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; src/kimchi/control/utils.py \
        <br>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; plugins/__init__.py \
        <br>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; plugins/sample/__init__.py \
        <br>
        diff --git a/src/kimchi/control/base.py
        b/src/kimchi/control/base.py
        <br>
        new file mode 100644
        <br>
        index 0000000..22452fc
        <br>
        --- /dev/null
        <br>
        +++ b/src/kimchi/control/base.py
        <br>
        @@ -0,0 +1,290 @@
        <br>
        +#
        <br>
        +# Project Kimchi
        <br>
        +#
        <br>
        +# Copyright IBM, Corp. 2013
        <br>
        +#
        <br>
        +# Authors:
        <br>
        +#&nbsp; Adam Litke <a class="moz-txt-link-rfc2396E" href="mailto:agl@linux.vnet.ibm.com">&lt;agl@linux.vnet.ibm.com&gt;</a>
        <br>
        +#&nbsp; Aline Manera <a class="moz-txt-link-rfc2396E" href="mailto:alinefm@linix.vnet.ibm.com">&lt;alinefm@linix.vnet.ibm.com&gt;</a>
        <br>
        +#
        <br>
        +# This library is free software; you can redistribute it and/or
        <br>
        +# modify it under the terms of the GNU Lesser General Public
        <br>
        +# License as published by the Free Software Foundation; either
        <br>
        +# version 2.1 of the License, or (at your option) any later
        version.
        <br>
        +#
        <br>
        +# This library is distributed in the hope that it will be
        useful,
        <br>
        +# but WITHOUT ANY WARRANTY; without even the implied warranty
        of
        <br>
        +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&nbsp; See the
        GNU
        <br>
        +# Lesser General Public License for more details.
        <br>
        +#
        <br>
        +# You should have received a copy of the GNU Lesser General
        Public
        <br>
        +# License along with this library; if not, write to the Free
        Software
        <br>
        +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
        MA&nbsp; 02110-1301 USA
        <br>
        +
        <br>
        +import cherrypy
        <br>
        +import urllib2
        <br>
        +
        <br>
        +
        <br>
        +import kimchi.template
        <br>
        +from kimchi.control.utils import get_class_name,
        internal_redirect, model_fn
        <br>
        +from kimchi.control.utils import parse_request, validate_method
        <br>
        +from kimchi.control.utils import validate_params
        <br>
        +from kimchi.exception import InvalidOperation, InvalidParameter
        <br>
        +from kimchi.exception import MissingParameter, NotFoundError,&nbsp;
        OperationFailed
        <br>
        +
        <br>
        +
        <br>
        +class Resource(object):
        <br>
        +&nbsp;&nbsp;&nbsp; """
        <br>
        +&nbsp;&nbsp;&nbsp; A Resource represents a single entity in the API (such as a
        Virtual
        <br>
        +&nbsp;&nbsp;&nbsp; Machine)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; To create new Resource types, subclass this and change the
        following things
        <br>
        +&nbsp;&nbsp;&nbsp; in the child class:
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; - If the Resource requires more than one identifier set
        self.model_args as
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; appropriate.&nbsp; This should only be necessary if this
        Resource is logically
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nested.&nbsp; For example: A Storage Volume belongs to a
        Storage Pool so the
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Storage Volume would set model args to (pool_ident,
        volume_ident).
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; - Implement the base operations of 'lookup' and 'delete' in
        the model(s).
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; - Set the 'data' property to a JSON-serializable
        representation of the
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Resource.
        <br>
        +&nbsp;&nbsp;&nbsp; """
        <br>
        +&nbsp;&nbsp;&nbsp; def __init__(self, model, ident=None):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.model = model
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.ident = ident
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.model_args = (ident,)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.update_params = []
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; def generate_action_handler(self, instance, action_name,
        action_args=None):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def wrapper(*args, **kwargs):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; validate_method(('POST'))
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; model_args = list(instance.model_args)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if action_args is not None:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; model_args.extend(parse_request()[key]
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for key in action_args)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fn = getattr(instance.model, model_fn(instance,
        action_name))
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fn(*model_args)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; uri_params = tuple(instance.model_args)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise internal_redirect(instance.uri_fmt %
        uri_params)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except MissingParameter, param:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = "Missing parameter: '%s'" % param
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(400, error)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except InvalidParameter, param:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = "Invalid parameter: '%s'" % param
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(400, error)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except InvalidOperation, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(400, "Invalid
        operation: '%s'" % msg)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except OperationFailed, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(500, "Operation
        Failed: '%s'" % msg)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except NotFoundError, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(404, "Not found: '%s'"
        % msg)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wrapper.__name__ = action_name
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wrapper.exposed = True
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return wrapper
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; def lookup(self):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lookup = getattr(self.model, model_fn(self,
        'lookup'))
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.info = lookup(*self.model_args)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except AttributeError:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.info = {}
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; def delete(self):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fn = getattr(self.model, model_fn(self, 'delete'))
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fn(*self.model_args)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cherrypy.response.status = 204
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except AttributeError:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = "Delete is not allowed for %s" %
        get_class_name(self)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(405, error)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except OperationFailed, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(500, "Operation Failed:
        '%s'" % msg)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except InvalidOperation, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(400, "Invalid operation:
        '%s'" % msg)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; @cherrypy.expose
        <br>
        +&nbsp;&nbsp;&nbsp; def index(self):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method = validate_method(('GET', 'DELETE', 'PUT'))
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if method == 'GET':
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return self.get()
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except NotFoundError, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(404, "Not found: '%s'"
        % msg)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except InvalidOperation, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(400, "Invalid
        operation: '%s'" % msg)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except OperationFailed, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(406, "Operation
        failed: '%s'" % msg)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elif method == 'DELETE':
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return self.delete()
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except NotFoundError, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(404, "Not found: '%s'"
        % msg)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elif method == 'PUT':
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return self.update()
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except InvalidParameter, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(400, "Invalid
        parameter: '%s'" % msg)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except InvalidOperation, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(400, "Invalid
        operation: '%s'" % msg)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except NotFoundError, msg:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(404, "Not found: '%s'"
        % msg)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; def update(self):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; update = getattr(self.model, model_fn(self,
        'update'))
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except AttributeError:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = "%s does not implement update method"
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(405, error %
        get_class_name(self))
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params = parse_request()
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; validate_params(params, self, 'update')
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if self.update_params is not None:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invalids = [v for v in params.keys() if
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v not in self.update_params]
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if invalids:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = "%s are not allowed to be updated" %
        invalids
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(405, error)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ident = update(self.ident, params)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ident != self.ident:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; uri_params = list(self.model_args[:-1])
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; uri_params += [urllib2.quote(ident.encode('utf8'))]
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPRedirect(self.uri_fmt %
        tuple(uri_params), 303)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return self.get()
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; def get(self):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.lookup()
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return kimchi.template.render(get_class_name(self),
        self.data)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; @property
        <br>
        +&nbsp;&nbsp;&nbsp; def data(self):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; """
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Override this in inherited classes to provide the
        Resource
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; representation as a python dictionary.
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; """
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return {}
        <br>
        +
        <br>
        +
        <br>
        +class Collection(object):
        <br>
        +&nbsp;&nbsp;&nbsp; """
        <br>
        +&nbsp;&nbsp;&nbsp; A Collection is a container for Resource objects.&nbsp; To
        create a new
        <br>
        +&nbsp;&nbsp;&nbsp; Collection type, subclass this and make the following
        changes to the child
        <br>
        +&nbsp;&nbsp;&nbsp; class:
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; - Set self.resource to the type of Resource that this
        Collection contains
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; - Set self.resource_args.&nbsp; This can remain an empty list if
        the Resources
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; can be initialized with only one identifier.&nbsp; Otherwise,
        include
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; additional values as needed (eg. to identify a parent
        resource).
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; - Set self.model_args.&nbsp; Similar to above, this is needed
        only if the model
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; needs additional information to identify this Collection.
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; - Implement the base operations of 'create' and 'get_list'
        in the model.
        <br>
        +&nbsp;&nbsp;&nbsp; """
        <br>
        +&nbsp;&nbsp;&nbsp; def __init__(self, model):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.model = model
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.resource = Resource
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.resource_args = []
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.model_args = []
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; def create(self, *args):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create = getattr(self.model, model_fn(self,
        'create'))
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except AttributeError:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = 'Create is not allowed for %s' %
        get_class_name(self)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(405, error)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params = parse_request()
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; validate_params(params, self, 'create')
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; args = self.model_args + [params]
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name = create(*args)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cherrypy.response.status = 201
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; args = self.resource_args + [name]
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; res = self.resource(self.model, *args)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return res.get()
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; def _get_resources(self):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get_list = getattr(self.model, model_fn(self,
        'get_list'))
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; idents = get_list(*self.model_args)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; res_list = []
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for ident in idents:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # internal text, get_list changes ident to
        unicode for sorted
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; args = self.resource_args + [ident]
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; res = self.resource(self.model, *args)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; res.lookup()
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; res_list.append(res)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return res_list
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except AttributeError:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return []
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; def _cp_dispatch(self, vpath):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if vpath:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ident = vpath.pop(0)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # incoming text, from URL, is not unicode, need
        decode
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; args = self.resource_args + [ident.decode("utf-8")]
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return self.resource(self.model, *args)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; def get(self):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resources = self._get_resources()
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data = []
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for res in resources:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data.append(res.data)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return kimchi.template.render(get_class_name(self),
        data)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; @cherrypy.expose
        <br>
        +&nbsp;&nbsp;&nbsp; def index(self, *args):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method = validate_method(('GET', 'POST'))
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if method == 'GET':
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return self.get()
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except InvalidOperation, param:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = "Invalid operation: '%s'" % param
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(400, error)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except NotFoundError, param:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(404, "Not found: '%s'"
        % param)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elif method == 'POST':
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return self.create(*args)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except MissingParameter, param:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = "Missing parameter: '%s'" % param
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(400, error)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except InvalidParameter, param:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = "Invalid parameter: '%s'" % param
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(400, error)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except OperationFailed, param:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(500, "Operation
        Failed: '%s'" % param)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except InvalidOperation, param:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = "Invalid operation: '%s'" % param
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(400, error)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except NotFoundError, param:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(404, "Not found: '%s'"
        % param)
        <br>
        +
        <br>
        +
        <br>
        +class AsyncCollection(Collection):
        <br>
        +&nbsp;&nbsp;&nbsp; """
        <br>
        +&nbsp;&nbsp;&nbsp; A Collection to create it's resource by asynchronous task
        <br>
        +&nbsp;&nbsp;&nbsp; """
        <br>
        +&nbsp;&nbsp;&nbsp; def __init__(self, model):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(AsyncCollection, self).__init__(model)
        <br>
        +
        <br>
        +&nbsp;&nbsp;&nbsp; def create(self, *args):
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create = getattr(self.model, model_fn(self,
        'create'))
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except AttributeError:
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise cherrypy.HTTPError(405,
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'Create is not allowed for %s' %
        get_class_name(self))
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params = parse_request()
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; args = self.model_args + [params]
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; task = create(*args)
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cherrypy.response.status = 202
        <br>
        +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return kimchi.template.render("Task", task)
        <br>
      </blockquote>
      Pep8 error:
      <br>
      src/kimchi/control/base.py:285:17: E128 continuation line
      under-indented for visual indent
      <br>
      <br>
    </blockquote>
    <font face="DejaVu Sans Mono"><br>
      Mark, I didn't get this error. <br>
      Maybe may pep8 version is different from yours.<br>
      <br>
      alinefm@alinefm:~/kimchi$ sudo make check-local<br>
      /usr/bin/pep8 --version<br>
      1.2<br>
      /usr/bin/pep8 --filename '*.py,*.py.in' src/kimchi/asynctask.py
      src/kimchi/auth.py src/kimchi/cachebust.py src/kimchi/config.py.in
      src/kimchi/disks.py src/kimchi/root.py src/kimchi/server.py
      src/kimchi/control/base.py src/kimchi/control/config.py
      src/kimchi/control/debugreports.py src/kimchi/control/host.py
      src/kimchi/control/interfaces.py src/kimchi/control/networks.py
      src/kimchi/control/plugins.py src/kimchi/control/storagepools.py
      src/kimchi/control/storagevolumes.py src/kimchi/control/tasks.py
      src/kimchi/control/templates.py src/kimchi/control/utils.py
      src/kimchi/control/vms.py plugins/__init__.py
      plugins/sample/__init__.py plugins/sample/model.py
      tests/test_plugin.py <br>
      <br>
    </font>
  </body>
</html>