[PATCH 00/16] Reorganize controller module

From: Aline Manera <alinefm@br.ibm.com> The current implemenation put all resources implementation into controller.py file. That way this file tends to increase to infinity as more resources are added to Kimchi. This patch splits controller module (controller.py) into small modules. So each resource will have its own controller implementation under /control It will make maintenance easier and anyone can easily identify where any resource is implemented. This patch set does not change any logic - just move classes from controller to a new module. Aline Manera (16): Move generate_action_handler() function to Resource() class Move common functions for Resource and Collection to control/utils.py Move login() and logout() functions from controller.py to root.py Move basic controller resources to control/base.py Move all resources related to vms to control/vms.py Move all resources related to templates to control/templates.py Move all resources related to debug reports to control/debugreports.py Move all resources related to storage pools to control/storagepools.py Move all resources related to storage volume to control/storagevolumes.py Move all resources related to interfaces to control/interfaces.py Move all resources related to networks to control/networks.py Move all resources related to config to control/config.py Move all resources related to host to control/host.py Move all resources related to plugins to control/plugins.py Move all resources related to tasks to control/tasks.py Use new control modules in root.py Makefile.am | 13 + src/kimchi/control/__init__.py | 21 + src/kimchi/control/base.py | 290 +++++++++++++ src/kimchi/control/config.py | 65 +++ src/kimchi/control/debugreports.py | 52 +++ src/kimchi/control/host.py | 61 +++ src/kimchi/control/interfaces.py | 44 ++ src/kimchi/control/networks.py | 48 +++ src/kimchi/control/plugins.py | 44 ++ src/kimchi/control/storagepools.py | 125 ++++++ src/kimchi/control/storagevolumes.py | 79 ++++ src/kimchi/control/tasks.py | 41 ++ src/kimchi/control/templates.py | 51 +++ src/kimchi/control/utils.py | 103 +++++ src/kimchi/control/vms.py | 64 +++ src/kimchi/controller.py | 755 ---------------------------------- src/kimchi/root.py | 61 ++- 17 files changed, 1147 insertions(+), 770 deletions(-) create mode 100644 src/kimchi/control/__init__.py create mode 100644 src/kimchi/control/base.py create mode 100644 src/kimchi/control/config.py create mode 100644 src/kimchi/control/debugreports.py create mode 100644 src/kimchi/control/host.py create mode 100644 src/kimchi/control/interfaces.py create mode 100644 src/kimchi/control/networks.py create mode 100644 src/kimchi/control/plugins.py create mode 100644 src/kimchi/control/storagepools.py create mode 100644 src/kimchi/control/storagevolumes.py create mode 100644 src/kimchi/control/tasks.py create mode 100644 src/kimchi/control/templates.py create mode 100644 src/kimchi/control/utils.py create mode 100644 src/kimchi/control/vms.py delete mode 100644 src/kimchi/controller.py -- 1.7.10.4

From: Aline Manera <alinefm@br.ibm.com> generate_action_handler() function is only used by Resources instances so move it to Resource() Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/controller.py | 75 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index 2940278..ba8b53d 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -103,33 +103,6 @@ def validate_params(params, instance, action): e.message for e in validator.iter_errors(request))) -def generate_action_handler(instance, action_name, action_args=None): - def wrapper(*args, **kwargs): - validate_method(('POST')) - try: - model_args = list(instance.model_args) - if action_args is not None: - model_args.extend(parse_request()[key] for key in action_args) - fn = getattr(instance.model, model_fn(instance, action_name)) - fn(*model_args) - raise internal_redirect(instance.uri_fmt % - tuple(instance.model_args)) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - wrapper.__name__ = action_name - wrapper.exposed = True - return wrapper - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine) @@ -153,6 +126,32 @@ class Resource(object): self.model_args = (ident,) self.update_params = [] + def generate_action_handler(self, instance, action_name, action_args=None): + def wrapper(*args, **kwargs): + validate_method(('POST')) + try: + model_args = list(instance.model_args) + if action_args is not None: + model_args.extend(parse_request()[key] for key in action_args) + fn = getattr(instance.model, model_fn(instance, action_name)) + fn(*model_args) + raise internal_redirect(instance.uri_fmt % + tuple(instance.model_args)) + except MissingParameter, param: + raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) + except InvalidParameter, param: + raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) + except InvalidOperation, msg: + raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) + except OperationFailed, msg: + raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) + except NotFoundError, msg: + raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) + + wrapper.__name__ = action_name + wrapper.exposed = True + return wrapper + def lookup(self): try: lookup = getattr(self.model, model_fn(self, 'lookup')) @@ -369,9 +368,9 @@ class VM(Resource): self.update_params = ["name"] self.screenshot = VMScreenShot(model, ident) self.uri_fmt = '/vms/%s' - self.start = generate_action_handler(self, 'start') - self.stop = generate_action_handler(self, 'stop') - self.connect = generate_action_handler(self, 'connect') + self.start = self.generate_action_handler(self, 'start') + self.stop = self.generate_action_handler(self, 'stop') + self.connect = self.generate_action_handler(self, 'connect') @property def data(self): @@ -453,8 +452,8 @@ class Network(Resource): def __init__(self, model, ident): super(Network, self).__init__(model, ident) self.uri_fmt = "/networks/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate') @property def data(self): @@ -475,8 +474,8 @@ class StorageVolume(Resource): self.info = {} self.model_args = [self.pool, self.ident] self.uri_fmt = '/storagepools/%s/storagevolumes/%s' - self.resize = generate_action_handler(self, 'resize', ['size']) - self.wipe = generate_action_handler(self, 'wipe') + self.resize = self.generate_action_handler(self, 'resize', ['size']) + self.wipe = self.generate_action_handler(self, 'wipe') @property def data(self): @@ -522,8 +521,8 @@ class StoragePool(Resource): super(StoragePool, self).__init__(model, ident) self.update_params = ["autostart"] self.uri_fmt = "/storagepools/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate') @property def data(self): @@ -689,8 +688,8 @@ class Host(Resource): self.stats = HostStats(self.model) self.stats.exposed = True self.uri_fmt = '/host/%s' - self.reboot = generate_action_handler(self, 'reboot') - self.shutdown = generate_action_handler(self, 'shutdown') + self.reboot = self.generate_action_handler(self, 'reboot') + self.shutdown = self.generate_action_handler(self, 'shutdown') self.partitions = Partitions(self.model) self.partitions.exposed = True -- 1.7.10.4

Reviewed-By: Ramon Medeiros <ramonn@br.ibm.com>

On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
generate_action_handler() function is only used by Resources instances so move it to Resource()
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/controller.py | 75 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 38 deletions(-)
diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index 2940278..ba8b53d 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -103,33 +103,6 @@ def validate_params(params, instance, action): e.message for e in validator.iter_errors(request)))
-def generate_action_handler(instance, action_name, action_args=None): - def wrapper(*args, **kwargs): - validate_method(('POST')) - try: - model_args = list(instance.model_args) - if action_args is not None: - model_args.extend(parse_request()[key] for key in action_args) - fn = getattr(instance.model, model_fn(instance, action_name)) - fn(*model_args) - raise internal_redirect(instance.uri_fmt % - tuple(instance.model_args)) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - wrapper.__name__ = action_name - wrapper.exposed = True - return wrapper - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine) @@ -153,6 +126,32 @@ class Resource(object): self.model_args = (ident,) self.update_params = []
+ def generate_action_handler(self, instance, action_name, action_args=None): do we still need instance? Here instance is same with self.
+ def wrapper(*args, **kwargs): + validate_method(('POST')) + try: + model_args = list(instance.model_args) + if action_args is not None: + model_args.extend(parse_request()[key] for key in action_args) + fn = getattr(instance.model, model_fn(instance, action_name)) + fn(*model_args) + raise internal_redirect(instance.uri_fmt % + tuple(instance.model_args)) + except MissingParameter, param: + raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) + except InvalidParameter, param: + raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) + except InvalidOperation, msg: + raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) + except OperationFailed, msg: + raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) + except NotFoundError, msg: + raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) + + wrapper.__name__ = action_name + wrapper.exposed = True + return wrapper + def lookup(self): try: lookup = getattr(self.model, model_fn(self, 'lookup')) @@ -369,9 +368,9 @@ class VM(Resource): self.update_params = ["name"] self.screenshot = VMScreenShot(model, ident) self.uri_fmt = '/vms/%s' - self.start = generate_action_handler(self, 'start') - self.stop = generate_action_handler(self, 'stop') - self.connect = generate_action_handler(self, 'connect') + self.start = self.generate_action_handler(self, 'start')
I think it is OK. + def generate_action_handler(self, action_name, action_args=None): then you can call + self.start = self.generate_action_handler('start')
+ self.stop = self.generate_action_handler(self, 'stop') + self.connect = self.generate_action_handler(self, 'connect')
@property def data(self): @@ -453,8 +452,8 @@ class Network(Resource): def __init__(self, model, ident): super(Network, self).__init__(model, ident) self.uri_fmt = "/networks/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate')
@property def data(self): @@ -475,8 +474,8 @@ class StorageVolume(Resource): self.info = {} self.model_args = [self.pool, self.ident] self.uri_fmt = '/storagepools/%s/storagevolumes/%s' - self.resize = generate_action_handler(self, 'resize', ['size']) - self.wipe = generate_action_handler(self, 'wipe') + self.resize = self.generate_action_handler(self, 'resize', ['size']) + self.wipe = self.generate_action_handler(self, 'wipe')
@property def data(self): @@ -522,8 +521,8 @@ class StoragePool(Resource): super(StoragePool, self).__init__(model, ident) self.update_params = ["autostart"] self.uri_fmt = "/storagepools/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate')
@property def data(self): @@ -689,8 +688,8 @@ class Host(Resource): self.stats = HostStats(self.model) self.stats.exposed = True self.uri_fmt = '/host/%s' - self.reboot = generate_action_handler(self, 'reboot') - self.shutdown = generate_action_handler(self, 'shutdown') + self.reboot = self.generate_action_handler(self, 'reboot') + self.shutdown = self.generate_action_handler(self, 'shutdown') self.partitions = Partitions(self.model) self.partitions.exposed = True
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 12/24/2013 10:26 AM, Sheldon wrote:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
generate_action_handler() function is only used by Resources instances so move it to Resource()
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/controller.py | 75 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 38 deletions(-)
diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index 2940278..ba8b53d 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -103,33 +103,6 @@ def validate_params(params, instance, action): e.message for e in validator.iter_errors(request)))
-def generate_action_handler(instance, action_name, action_args=None): - def wrapper(*args, **kwargs): - validate_method(('POST')) - try: - model_args = list(instance.model_args) - if action_args is not None: - model_args.extend(parse_request()[key] for key in action_args) - fn = getattr(instance.model, model_fn(instance, action_name)) - fn(*model_args) - raise internal_redirect(instance.uri_fmt % - tuple(instance.model_args)) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - wrapper.__name__ = action_name - wrapper.exposed = True - return wrapper - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine) @@ -153,6 +126,32 @@ class Resource(object): self.model_args = (ident,) self.update_params = []
+ def generate_action_handler(self, instance, action_name, action_args=None): do we still need instance? Here instance is same with self.
I think it is OK. It's good to move it to Resource class, but you need remove the redundant parameter 'instance' as Sheldon suggested.
+ def wrapper(*args, **kwargs): + validate_method(('POST')) + try: + model_args = list(instance.model_args) + if action_args is not None: + model_args.extend(parse_request()[key] for key in action_args) + fn = getattr(instance.model, model_fn(instance, action_name)) + fn(*model_args) + raise internal_redirect(instance.uri_fmt % + tuple(instance.model_args)) + except MissingParameter, param: + raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) + except InvalidParameter, param: + raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) + except InvalidOperation, msg: + raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) + except OperationFailed, msg: + raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) + except NotFoundError, msg: + raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) + + wrapper.__name__ = action_name + wrapper.exposed = True + return wrapper + def lookup(self): try: lookup = getattr(self.model, model_fn(self, 'lookup')) @@ -369,9 +368,9 @@ class VM(Resource): self.update_params = ["name"] self.screenshot = VMScreenShot(model, ident) self.uri_fmt = '/vms/%s' - self.start = generate_action_handler(self, 'start') - self.stop = generate_action_handler(self, 'stop') - self.connect = generate_action_handler(self, 'connect') + self.start = self.generate_action_handler(self, 'start')
+ def generate_action_handler(self, action_name, action_args=None): then you can call + self.start = self.generate_action_handler('start')
+ self.stop = self.generate_action_handler(self, 'stop') + self.connect = self.generate_action_handler(self, 'connect')
@property def data(self): @@ -453,8 +452,8 @@ class Network(Resource): def __init__(self, model, ident): super(Network, self).__init__(model, ident) self.uri_fmt = "/networks/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate')
@property def data(self): @@ -475,8 +474,8 @@ class StorageVolume(Resource): self.info = {} self.model_args = [self.pool, self.ident] self.uri_fmt = '/storagepools/%s/storagevolumes/%s' - self.resize = generate_action_handler(self, 'resize', ['size']) - self.wipe = generate_action_handler(self, 'wipe') + self.resize = self.generate_action_handler(self, 'resize', ['size']) + self.wipe = self.generate_action_handler(self, 'wipe')
@property def data(self): @@ -522,8 +521,8 @@ class StoragePool(Resource): super(StoragePool, self).__init__(model, ident) self.update_params = ["autostart"] self.uri_fmt = "/storagepools/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate')
@property def data(self): @@ -689,8 +688,8 @@ class Host(Resource): self.stats = HostStats(self.model) self.stats.exposed = True self.uri_fmt = '/host/%s' - self.reboot = generate_action_handler(self, 'reboot') - self.shutdown = generate_action_handler(self, 'shutdown') + self.reboot = self.generate_action_handler(self, 'reboot') + self.shutdown = self.generate_action_handler(self, 'shutdown') self.partitions = Partitions(self.model) self.partitions.exposed = True

On 12/24/2013 01:16 AM, Mark Wu wrote:
On 12/24/2013 10:26 AM, Sheldon wrote:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
generate_action_handler() function is only used by Resources instances so move it to Resource()
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/controller.py | 75 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 38 deletions(-)
diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index 2940278..ba8b53d 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -103,33 +103,6 @@ def validate_params(params, instance, action): e.message for e in validator.iter_errors(request)))
-def generate_action_handler(instance, action_name, action_args=None): - def wrapper(*args, **kwargs): - validate_method(('POST')) - try: - model_args = list(instance.model_args) - if action_args is not None: - model_args.extend(parse_request()[key] for key in action_args) - fn = getattr(instance.model, model_fn(instance, action_name)) - fn(*model_args) - raise internal_redirect(instance.uri_fmt % - tuple(instance.model_args)) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - wrapper.__name__ = action_name - wrapper.exposed = True - return wrapper - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine) @@ -153,6 +126,32 @@ class Resource(object): self.model_args = (ident,) self.update_params = []
+ def generate_action_handler(self, instance, action_name, action_args=None): do we still need instance? Here instance is same with self.
I think it is OK. It's good to move it to Resource class, but you need remove the redundant parameter 'instance' as Sheldon suggested.
ACK.
+ def wrapper(*args, **kwargs): + validate_method(('POST')) + try: + model_args = list(instance.model_args) + if action_args is not None: + model_args.extend(parse_request()[key] for key in action_args) + fn = getattr(instance.model, model_fn(instance, action_name)) + fn(*model_args) + raise internal_redirect(instance.uri_fmt % + tuple(instance.model_args)) + except MissingParameter, param: + raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) + except InvalidParameter, param: + raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) + except InvalidOperation, msg: + raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) + except OperationFailed, msg: + raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) + except NotFoundError, msg: + raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) + + wrapper.__name__ = action_name + wrapper.exposed = True + return wrapper + def lookup(self): try: lookup = getattr(self.model, model_fn(self, 'lookup')) @@ -369,9 +368,9 @@ class VM(Resource): self.update_params = ["name"] self.screenshot = VMScreenShot(model, ident) self.uri_fmt = '/vms/%s' - self.start = generate_action_handler(self, 'start') - self.stop = generate_action_handler(self, 'stop') - self.connect = generate_action_handler(self, 'connect') + self.start = self.generate_action_handler(self, 'start')
+ def generate_action_handler(self, action_name, action_args=None): then you can call + self.start = self.generate_action_handler('start')
+ self.stop = self.generate_action_handler(self, 'stop') + self.connect = self.generate_action_handler(self, 'connect')
@property def data(self): @@ -453,8 +452,8 @@ class Network(Resource): def __init__(self, model, ident): super(Network, self).__init__(model, ident) self.uri_fmt = "/networks/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate')
@property def data(self): @@ -475,8 +474,8 @@ class StorageVolume(Resource): self.info = {} self.model_args = [self.pool, self.ident] self.uri_fmt = '/storagepools/%s/storagevolumes/%s' - self.resize = generate_action_handler(self, 'resize', ['size']) - self.wipe = generate_action_handler(self, 'wipe') + self.resize = self.generate_action_handler(self, 'resize', ['size']) + self.wipe = self.generate_action_handler(self, 'wipe')
@property def data(self): @@ -522,8 +521,8 @@ class StoragePool(Resource): super(StoragePool, self).__init__(model, ident) self.update_params = ["autostart"] self.uri_fmt = "/storagepools/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate')
@property def data(self): @@ -689,8 +688,8 @@ class Host(Resource): self.stats = HostStats(self.model) self.stats.exposed = True self.uri_fmt = '/host/%s' - self.reboot = generate_action_handler(self, 'reboot') - self.shutdown = generate_action_handler(self, 'shutdown') + self.reboot = self.generate_action_handler(self, 'reboot') + self.shutdown = self.generate_action_handler(self, 'shutdown') self.partitions = Partitions(self.model) self.partitions.exposed = True

on 2013/12/24 02:41, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
generate_action_handler() function is only used by Resources instances so move it to Resource()
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/controller.py | 75 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 38 deletions(-)
diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index 2940278..ba8b53d 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -103,33 +103,6 @@ def validate_params(params, instance, action): e.message for e in validator.iter_errors(request)))
-def generate_action_handler(instance, action_name, action_args=None): - def wrapper(*args, **kwargs): - validate_method(('POST')) - try: - model_args = list(instance.model_args) - if action_args is not None: - model_args.extend(parse_request()[key] for key in action_args) - fn = getattr(instance.model, model_fn(instance, action_name)) - fn(*model_args) - raise internal_redirect(instance.uri_fmt % - tuple(instance.model_args)) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - wrapper.__name__ = action_name - wrapper.exposed = True - return wrapper - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine) @@ -153,6 +126,32 @@ class Resource(object): self.model_args = (ident,) self.update_params = []
+ def generate_action_handler(self, instance, action_name, action_args=None):
Hi, it seems the instance argument is redundant here. While we call "obj.method(a, b, c)", the "self" is passed to "def method(self, a, b, c)" as the first argument implicitly. Here "instance" and "self" serves the same purposes and refer to the same thing. So I think, "instance" in the function argument list can be removed, and change all "instance" in the function body to "self". This method should be called like following. self.start = self.generate_action_handler('start') This is how object works in Python.
+ def wrapper(*args, **kwargs): + validate_method(('POST')) + try: + model_args = list(instance.model_args) + if action_args is not None: + model_args.extend(parse_request()[key] for key in action_args) + fn = getattr(instance.model, model_fn(instance, action_name)) + fn(*model_args) + raise internal_redirect(instance.uri_fmt % + tuple(instance.model_args)) + except MissingParameter, param: + raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) + except InvalidParameter, param: + raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) + except InvalidOperation, msg: + raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) + except OperationFailed, msg: + raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) + except NotFoundError, msg: + raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) + + wrapper.__name__ = action_name + wrapper.exposed = True + return wrapper + def lookup(self): try: lookup = getattr(self.model, model_fn(self, 'lookup')) @@ -369,9 +368,9 @@ class VM(Resource): self.update_params = ["name"] self.screenshot = VMScreenShot(model, ident) self.uri_fmt = '/vms/%s' - self.start = generate_action_handler(self, 'start') - self.stop = generate_action_handler(self, 'stop') - self.connect = generate_action_handler(self, 'connect') + self.start = self.generate_action_handler(self, 'start') + self.stop = self.generate_action_handler(self, 'stop') + self.connect = self.generate_action_handler(self, 'connect')
@property def data(self): @@ -453,8 +452,8 @@ class Network(Resource): def __init__(self, model, ident): super(Network, self).__init__(model, ident) self.uri_fmt = "/networks/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate')
@property def data(self): @@ -475,8 +474,8 @@ class StorageVolume(Resource): self.info = {} self.model_args = [self.pool, self.ident] self.uri_fmt = '/storagepools/%s/storagevolumes/%s' - self.resize = generate_action_handler(self, 'resize', ['size']) - self.wipe = generate_action_handler(self, 'wipe') + self.resize = self.generate_action_handler(self, 'resize', ['size']) + self.wipe = self.generate_action_handler(self, 'wipe')
@property def data(self): @@ -522,8 +521,8 @@ class StoragePool(Resource): super(StoragePool, self).__init__(model, ident) self.update_params = ["autostart"] self.uri_fmt = "/storagepools/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate')
@property def data(self): @@ -689,8 +688,8 @@ class Host(Resource): self.stats = HostStats(self.model) self.stats.exposed = True self.uri_fmt = '/host/%s' - self.reboot = generate_action_handler(self, 'reboot') - self.shutdown = generate_action_handler(self, 'shutdown') + self.reboot = self.generate_action_handler(self, 'reboot') + self.shutdown = self.generate_action_handler(self, 'shutdown') self.partitions = Partitions(self.model) self.partitions.exposed = True
-- Thanks and best regards! Zhou Zheng Sheng / 周征晟 E-mail: zhshzhou@linux.vnet.ibm.com Telephone: 86-10-82454397

On 12/24/2013 01:28 AM, Zhou Zheng Sheng wrote:
From: Aline Manera <alinefm@br.ibm.com>
generate_action_handler() function is only used by Resources instances so move it to Resource()
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/controller.py | 75 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 38 deletions(-)
diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index 2940278..ba8b53d 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -103,33 +103,6 @@ def validate_params(params, instance, action): e.message for e in validator.iter_errors(request)))
-def generate_action_handler(instance, action_name, action_args=None): - def wrapper(*args, **kwargs): - validate_method(('POST')) - try: - model_args = list(instance.model_args) - if action_args is not None: - model_args.extend(parse_request()[key] for key in action_args) - fn = getattr(instance.model, model_fn(instance, action_name)) - fn(*model_args) - raise internal_redirect(instance.uri_fmt % - tuple(instance.model_args)) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - wrapper.__name__ = action_name - wrapper.exposed = True - return wrapper - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine) @@ -153,6 +126,32 @@ class Resource(object): self.model_args = (ident,) self.update_params = []
+ def generate_action_handler(self, instance, action_name, action_args=None): Hi, it seems the instance argument is redundant here. While we call "obj.method(a, b, c)", the "self" is passed to "def method(self, a, b, c)" as the first argument implicitly. Here "instance" and "self" serves
on 2013/12/24 02:41, Aline Manera wrote: the same purposes and refer to the same thing. So I think, "instance" in the function argument list can be removed, and change all "instance" in the function body to "self". This method should be called like following. self.start = self.generate_action_handler('start') This is how object works in Python.
ACK. I've just moved it inside the class and don't pay attention in the function itself. I will changed it on V2. Thanks
+ def wrapper(*args, **kwargs): + validate_method(('POST')) + try: + model_args = list(instance.model_args) + if action_args is not None: + model_args.extend(parse_request()[key] for key in action_args) + fn = getattr(instance.model, model_fn(instance, action_name)) + fn(*model_args) + raise internal_redirect(instance.uri_fmt % + tuple(instance.model_args)) + except MissingParameter, param: + raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) + except InvalidParameter, param: + raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) + except InvalidOperation, msg: + raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) + except OperationFailed, msg: + raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) + except NotFoundError, msg: + raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) + + wrapper.__name__ = action_name + wrapper.exposed = True + return wrapper + def lookup(self): try: lookup = getattr(self.model, model_fn(self, 'lookup')) @@ -369,9 +368,9 @@ class VM(Resource): self.update_params = ["name"] self.screenshot = VMScreenShot(model, ident) self.uri_fmt = '/vms/%s' - self.start = generate_action_handler(self, 'start') - self.stop = generate_action_handler(self, 'stop') - self.connect = generate_action_handler(self, 'connect') + self.start = self.generate_action_handler(self, 'start') + self.stop = self.generate_action_handler(self, 'stop') + self.connect = self.generate_action_handler(self, 'connect')
@property def data(self): @@ -453,8 +452,8 @@ class Network(Resource): def __init__(self, model, ident): super(Network, self).__init__(model, ident) self.uri_fmt = "/networks/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate')
@property def data(self): @@ -475,8 +474,8 @@ class StorageVolume(Resource): self.info = {} self.model_args = [self.pool, self.ident] self.uri_fmt = '/storagepools/%s/storagevolumes/%s' - self.resize = generate_action_handler(self, 'resize', ['size']) - self.wipe = generate_action_handler(self, 'wipe') + self.resize = self.generate_action_handler(self, 'resize', ['size']) + self.wipe = self.generate_action_handler(self, 'wipe')
@property def data(self): @@ -522,8 +521,8 @@ class StoragePool(Resource): super(StoragePool, self).__init__(model, ident) self.update_params = ["autostart"] self.uri_fmt = "/storagepools/%s" - self.activate = generate_action_handler(self, 'activate') - self.deactivate = generate_action_handler(self, 'deactivate') + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate')
@property def data(self): @@ -689,8 +688,8 @@ class Host(Resource): self.stats = HostStats(self.model) self.stats.exposed = True self.uri_fmt = '/host/%s' - self.reboot = generate_action_handler(self, 'reboot') - self.shutdown = generate_action_handler(self, 'shutdown') + self.reboot = self.generate_action_handler(self, 'reboot') + self.shutdown = self.generate_action_handler(self, 'shutdown') self.partitions = Partitions(self.model) self.partitions.exposed = True

On 12/24/2013 02:41 AM, Aline Manera wrote:
+ def generate_action_handler(self, instance, action_name, action_args=None):
since we don not need decorator from your code. + self.stop = self.generate_action_handler(self, 'stop') seems partial is more better than def wrapper(*args, **kwargs): from functools import partial
+ def wrapper(*args, **kwargs): + validate_method(('POST')) + try: + model_args = list(instance.model_args) + if action_args is not None: + model_args.extend(parse_request()[key] for key in action_args) + fn = getattr(instance.model, model_fn(instance, action_name)) + fn(*model_args) + raise internal_redirect(instance.uri_fmt % + tuple(instance.model_args)) + except MissingParameter, param: + raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) + except InvalidParameter, param: + raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) + except InvalidOperation, msg: + raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) + except OperationFailed, msg: + raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) + except NotFoundError, msg: + raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) + + wrapper.__name__ = action_name + wrapper.exposed = True + return wrapper +
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

From: Aline Manera <alinefm@br.ibm.com> The following functions: get_class_name, internal_redirect, model_fn, parse_request, validate_method, validate_params are used in Resource and Collection classes. Move them to a new module control/utils.py - which will have all common functions to API resources Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/__init__.py | 21 ++++++++ src/kimchi/control/utils.py | 103 ++++++++++++++++++++++++++++++++++++++++ src/kimchi/controller.py | 71 +-------------------------- 4 files changed, 127 insertions(+), 69 deletions(-) create mode 100644 src/kimchi/control/__init__.py create mode 100644 src/kimchi/control/utils.py diff --git a/Makefile.am b/Makefile.am index 0fd92c8..65b39b8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,6 +45,7 @@ PEP8_WHITELIST = \ src/kimchi/disks.py \ src/kimchi/root.py \ src/kimchi/server.py \ + src/kimchi/control/utils.py \ plugins/__init__.py \ plugins/sample/__init__.py \ plugins/sample/model.py \ diff --git a/src/kimchi/control/__init__.py b/src/kimchi/control/__init__.py new file mode 100644 index 0000000..55cd7bd --- /dev/null +++ b/src/kimchi/control/__init__.py @@ -0,0 +1,21 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/src/kimchi/control/utils.py b/src/kimchi/control/utils.py new file mode 100644 index 0000000..311b5a6 --- /dev/null +++ b/src/kimchi/control/utils.py @@ -0,0 +1,103 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +import cherrypy +import json + + +from jsonschema import Draft3Validator, ValidationError + + +from kimchi.exception import InvalidParameter + + +def get_class_name(cls): + try: + sub_class = cls.__subclasses__()[0] + except AttributeError: + sub_class = cls.__class__.__name__ + return sub_class.lower() + + +def model_fn(cls, fn_name): + return '%s_%s' % (get_class_name(cls), fn_name) + + +def validate_method(allowed): + method = cherrypy.request.method.upper() + if method not in allowed: + raise cherrypy.HTTPError(405) + return method + + +def mime_in_header(header, mime): + if not header in cherrypy.request.headers: + accepts = 'application/json' + else: + accepts = cherrypy.request.headers[header] + + if accepts.find(';') != -1: + accepts, _ = accepts.split(';', 1) + + if mime in accepts.split(','): + return True + + return False + + +def parse_request(): + if 'Content-Length' not in cherrypy.request.headers: + return {} + rawbody = cherrypy.request.body.read() + + if mime_in_header('Content-Type', 'application/json'): + try: + return json.loads(rawbody) + except ValueError: + raise cherrypy.HTTPError(400, "Unable to parse JSON request") + else: + raise cherrypy.HTTPError(415, "This API only supports" + " 'application/json'") + + +def internal_redirect(url): + raise cherrypy.InternalRedirect(url.encode("utf-8")) + + +def validate_params(params, instance, action): + root = cherrypy.request.app.root + + if hasattr(root, 'api_schema'): + api_schema = root.api_schema + else: + return + + operation = model_fn(instance, action) + validator = Draft3Validator(api_schema) + request = {operation: params} + + try: + validator.validate(request) + except ValidationError: + raise InvalidParameter('; '.join( + e.message for e in validator.iter_errors(request))) diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index ba8b53d..aec1579 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -21,88 +21,21 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import cherrypy -import json import urllib2 from functools import wraps -from jsonschema import Draft3Validator, ValidationError import kimchi.template from kimchi import auth +from kimchi.control.utils import get_class_name, internal_redirect, model_fn +from kimchi.control.utils import parse_request, validate_method, validate_params from kimchi.exception import InvalidOperation, InvalidParameter, MissingParameter from kimchi.exception import NotFoundError, OperationFailed from kimchi.model import ISO_POOL_NAME -def get_class_name(cls): - try: - sub_class = cls.__subclasses__()[0] - except AttributeError: - sub_class = cls.__class__.__name__ - return sub_class.lower() - - -def model_fn(cls, fn_name): - return '%s_%s' % (get_class_name(cls), fn_name) - - -def validate_method(allowed): - method = cherrypy.request.method.upper() - if method not in allowed: - raise cherrypy.HTTPError(405) - return method - - -def mime_in_header(header, mime): - if not header in cherrypy.request.headers: - accepts = 'application/json' - else: - accepts = cherrypy.request.headers[header] - - if accepts.find(';') != -1: - accepts, _ = accepts.split(';', 1) - - if mime in accepts.split(','): - return True - - return False - - -def parse_request(): - if 'Content-Length' not in cherrypy.request.headers: - return {} - rawbody = cherrypy.request.body.read() - - if mime_in_header('Content-Type', 'application/json'): - try: - return json.loads(rawbody) - except ValueError: - raise cherrypy.HTTPError(400, "Unable to parse JSON request") - else: - raise cherrypy.HTTPError(415, "This API only supports" - " 'application/json'") -def internal_redirect(url): - raise cherrypy.InternalRedirect(url.encode("utf-8")) - - -def validate_params(params, instance, action): - root = cherrypy.request.app.root - if hasattr(root, 'api_schema'): - api_schema = root.api_schema - else: - return - operation = model_fn(instance, action) - validator = Draft3Validator(api_schema) - request = {operation: params} - try: - validator.validate(request) - except ValidationError: - raise InvalidParameter('; '.join( - e.message for e in validator.iter_errors(request))) - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine) -- 1.7.10.4

On 12/23/2013 04:41 PM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
The following functions: get_class_name, internal_redirect, model_fn, parse_request, validate_method, validate_params are used in Resource and Collection classes. Move them to a new module control/utils.py - which will have all common functions to API resources
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/__init__.py | 21 ++++++++ src/kimchi/control/utils.py | 103 ++++++++++++++++++++++++++++++++++++++++ src/kimchi/controller.py | 71 +-------------------------- 4 files changed, 127 insertions(+), 69 deletions(-) create mode 100644 src/kimchi/control/__init__.py create mode 100644 src/kimchi/control/utils.py
diff --git a/Makefile.am b/Makefile.am index 0fd92c8..65b39b8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,6 +45,7 @@ PEP8_WHITELIST = \ src/kimchi/disks.py \ src/kimchi/root.py \ src/kimchi/server.py \ + src/kimchi/control/utils.py \ plugins/__init__.py \ plugins/sample/__init__.py \ plugins/sample/model.py \ diff --git a/src/kimchi/control/__init__.py b/src/kimchi/control/__init__.py new file mode 100644 index 0000000..55cd7bd --- /dev/null +++ b/src/kimchi/control/__init__.py @@ -0,0 +1,21 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/src/kimchi/control/utils.py b/src/kimchi/control/utils.py new file mode 100644 index 0000000..311b5a6 --- /dev/null +++ b/src/kimchi/control/utils.py @@ -0,0 +1,103 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +import cherrypy +import json + + +from jsonschema import Draft3Validator, ValidationError + + +from kimchi.exception import InvalidParameter + + +def get_class_name(cls): + try: + sub_class = cls.__subclasses__()[0] + except AttributeError: + sub_class = cls.__class__.__name__ + return sub_class.lower() + + +def model_fn(cls, fn_name): + return '%s_%s' % (get_class_name(cls), fn_name) + + +def validate_method(allowed): + method = cherrypy.request.method.upper() + if method not in allowed: + raise cherrypy.HTTPError(405) + return method + + +def mime_in_header(header, mime): + if not header in cherrypy.request.headers: + accepts = 'application/json' + else: + accepts = cherrypy.request.headers[header] + + if accepts.find(';') != -1: + accepts, _ = accepts.split(';', 1) + + if mime in accepts.split(','): + return True + + return False + + +def parse_request(): + if 'Content-Length' not in cherrypy.request.headers: would be nice to make Content-Length as constant, and, if more headers will be checked in the future, we can make a list of constants and iterate over it to check + return {} + rawbody = cherrypy.request.body.read() + + if mime_in_header('Content-Type', 'application/json'): + try: + return json.loads(rawbody) + except ValueError: + raise cherrypy.HTTPError(400, "Unable to parse JSON request") errors is nice to make a constant, still for translating purposes + else: + raise cherrypy.HTTPError(415, "This API only supports" + " 'application/json'") + + +def internal_redirect(url): + raise cherrypy.InternalRedirect(url.encode("utf-8")) + + +def validate_params(params, instance, action): + root = cherrypy.request.app.root + + if hasattr(root, 'api_schema'): + api_schema = root.api_schema + else: + return + + operation = model_fn(instance, action) + validator = Draft3Validator(api_schema) + request = {operation: params} + + try: + validator.validate(request) + except ValidationError: + raise InvalidParameter('; '.join( + e.message for e in validator.iter_errors(request))) diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index ba8b53d..aec1579 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -21,88 +21,21 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import cherrypy -import json import urllib2
from functools import wraps -from jsonschema import Draft3Validator, ValidationError
import kimchi.template from kimchi import auth +from kimchi.control.utils import get_class_name, internal_redirect, model_fn +from kimchi.control.utils import parse_request, validate_method, validate_params from kimchi.exception import InvalidOperation, InvalidParameter, MissingParameter from kimchi.exception import NotFoundError, OperationFailed from kimchi.model import ISO_POOL_NAME
-def get_class_name(cls): - try: - sub_class = cls.__subclasses__()[0] - except AttributeError: - sub_class = cls.__class__.__name__ - return sub_class.lower() - - -def model_fn(cls, fn_name): - return '%s_%s' % (get_class_name(cls), fn_name) - - -def validate_method(allowed): - method = cherrypy.request.method.upper() - if method not in allowed: - raise cherrypy.HTTPError(405) - return method - - -def mime_in_header(header, mime): - if not header in cherrypy.request.headers: - accepts = 'application/json' - else: - accepts = cherrypy.request.headers[header] - - if accepts.find(';') != -1: - accepts, _ = accepts.split(';', 1) - - if mime in accepts.split(','): - return True - - return False - - -def parse_request(): - if 'Content-Length' not in cherrypy.request.headers: - return {} - rawbody = cherrypy.request.body.read() - - if mime_in_header('Content-Type', 'application/json'): - try: - return json.loads(rawbody) - except ValueError: - raise cherrypy.HTTPError(400, "Unable to parse JSON request") - else: - raise cherrypy.HTTPError(415, "This API only supports" - " 'application/json'") -def internal_redirect(url): - raise cherrypy.InternalRedirect(url.encode("utf-8")) - - -def validate_params(params, instance, action): - root = cherrypy.request.app.root - if hasattr(root, 'api_schema'): - api_schema = root.api_schema - else: - return - operation = model_fn(instance, action) - validator = Draft3Validator(api_schema) - request = {operation: params} - try: - validator.validate(request) - except ValidationError: - raise InvalidParameter('; '.join( - e.message for e in validator.iter_errors(request))) - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine)
-- Ramon Nunes Medeiros Software Engineer - Linux Technology Center Brazil IBM Systems & Technology Group Phone : +55 19 2132 7878 ramonn@br.ibm.com

you also update spec file and makefile. or you can add another new patch to update spec files and makefiles On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
The following functions: get_class_name, internal_redirect, model_fn, parse_request, validate_method, validate_params are used in Resource and Collection classes. Move them to a new module control/utils.py - which will have all common functions to API resources
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/__init__.py | 21 ++++++++ src/kimchi/control/utils.py | 103 ++++++++++++++++++++++++++++++++++++++++ should also update contrib/kimchi.spec.fedora.in contrib/kimchi.spec.suse.in
src/kimchi/controller.py | 71 +-------------------------- 4 files changed, 127 insertions(+), 69 deletions(-) create mode 100644 src/kimchi/control/__init__.py create mode 100644 src/kimchi/control/utils.py
diff --git a/Makefile.am b/Makefile.am index 0fd92c8..65b39b8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,6 +45,7 @@ PEP8_WHITELIST = \ src/kimchi/disks.py \ src/kimchi/root.py \ src/kimchi/server.py \ + src/kimchi/control/utils.py \ plugins/__init__.py \ plugins/sample/__init__.py \ plugins/sample/model.py \ should also update src/kimchi/Makefile.am
diff --git a/src/kimchi/Makefile.am b/src/kimchi/Makefile.am index 47a3bd2..78af50f 100644 --- a/src/kimchi/Makefile.am +++ b/src/kimchi/Makefile.am @@ -20,6 +20,8 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +SUBDIRS = control + kimchi_PYTHON = \ asynctask.py \ auth.py \ should also add a new Makefile.am, src/kimchi/control/Makefile.am diff --git a/src/kimchi/control/Makefile.am b/src/kimchi/control/Makefile.am new file mode 100644 index 0000000..220811f --- /dev/null +++ b/src/kimchi/control/Makefile.am @@ -0,0 +1,27 @@ +# +# Kimchi +# +# Copyright IBM Corp, 2013 +# +# Authors: +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +kimchicontroldir = $(pythondir)/kimchi/control + +dist_kimchicontrol_PYTHON = \ + __init__.py \ + utils.py
diff --git a/src/kimchi/control/__init__.py b/src/kimchi/control/__init__.py new file mode 100644 index 0000000..55cd7bd --- /dev/null +++ b/src/kimchi/control/__init__.py @@ -0,0 +1,21 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/src/kimchi/control/utils.py b/src/kimchi/control/utils.py new file mode 100644 index 0000000..311b5a6 --- /dev/null +++ b/src/kimchi/control/utils.py @@ -0,0 +1,103 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +import cherrypy +import json + + +from jsonschema import Draft3Validator, ValidationError + + +from kimchi.exception import InvalidParameter + + +def get_class_name(cls): + try: + sub_class = cls.__subclasses__()[0] + except AttributeError: + sub_class = cls.__class__.__name__ + return sub_class.lower() + + +def model_fn(cls, fn_name): + return '%s_%s' % (get_class_name(cls), fn_name) + + +def validate_method(allowed): + method = cherrypy.request.method.upper() + if method not in allowed: + raise cherrypy.HTTPError(405) + return method + + +def mime_in_header(header, mime): + if not header in cherrypy.request.headers: + accepts = 'application/json' + else: + accepts = cherrypy.request.headers[header] + + if accepts.find(';') != -1: + accepts, _ = accepts.split(';', 1) + + if mime in accepts.split(','): + return True + + return False + + +def parse_request(): + if 'Content-Length' not in cherrypy.request.headers: + return {} + rawbody = cherrypy.request.body.read() + + if mime_in_header('Content-Type', 'application/json'): + try: + return json.loads(rawbody) + except ValueError: + raise cherrypy.HTTPError(400, "Unable to parse JSON request") + else: + raise cherrypy.HTTPError(415, "This API only supports" + " 'application/json'") + + +def internal_redirect(url): + raise cherrypy.InternalRedirect(url.encode("utf-8")) + + +def validate_params(params, instance, action): + root = cherrypy.request.app.root + + if hasattr(root, 'api_schema'): + api_schema = root.api_schema + else: + return + + operation = model_fn(instance, action) + validator = Draft3Validator(api_schema) + request = {operation: params} + + try: + validator.validate(request) + except ValidationError: + raise InvalidParameter('; '.join( + e.message for e in validator.iter_errors(request))) diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index ba8b53d..aec1579 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -21,88 +21,21 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import cherrypy -import json import urllib2
from functools import wraps -from jsonschema import Draft3Validator, ValidationError
import kimchi.template from kimchi import auth +from kimchi.control.utils import get_class_name, internal_redirect, model_fn +from kimchi.control.utils import parse_request, validate_method, validate_params from kimchi.exception import InvalidOperation, InvalidParameter, MissingParameter from kimchi.exception import NotFoundError, OperationFailed from kimchi.model import ISO_POOL_NAME
-def get_class_name(cls): - try: - sub_class = cls.__subclasses__()[0] - except AttributeError: - sub_class = cls.__class__.__name__ - return sub_class.lower() - - -def model_fn(cls, fn_name): - return '%s_%s' % (get_class_name(cls), fn_name) - - -def validate_method(allowed): - method = cherrypy.request.method.upper() - if method not in allowed: - raise cherrypy.HTTPError(405) - return method - - -def mime_in_header(header, mime): - if not header in cherrypy.request.headers: - accepts = 'application/json' - else: - accepts = cherrypy.request.headers[header] - - if accepts.find(';') != -1: - accepts, _ = accepts.split(';', 1) - - if mime in accepts.split(','): - return True - - return False - - -def parse_request(): - if 'Content-Length' not in cherrypy.request.headers: - return {} - rawbody = cherrypy.request.body.read() - - if mime_in_header('Content-Type', 'application/json'): - try: - return json.loads(rawbody) - except ValueError: - raise cherrypy.HTTPError(400, "Unable to parse JSON request") - else: - raise cherrypy.HTTPError(415, "This API only supports" - " 'application/json'") -def internal_redirect(url): - raise cherrypy.InternalRedirect(url.encode("utf-8")) - - -def validate_params(params, instance, action): - root = cherrypy.request.app.root - if hasattr(root, 'api_schema'): - api_schema = root.api_schema - else: - return - operation = model_fn(instance, action) - validator = Draft3Validator(api_schema) - request = {operation: params} - try: - validator.validate(request) - except ValidationError: - raise InvalidParameter('; '.join( - e.message for e in validator.iter_errors(request))) - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine)
-- Sheldon Feng(???)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 12/24/2013 01:06 AM, Sheldon wrote:
you also update spec file and makefile.
or you can add another new patch to update spec files and makefiles
Yeap! I will add it on V2.
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera<alinefm@br.ibm.com>
The following functions: get_class_name, internal_redirect, model_fn, parse_request, validate_method, validate_params are used in Resource and Collection classes. Move them to a new module control/utils.py - which will have all common functions to API resources
Signed-off-by: Aline Manera<alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/__init__.py | 21 ++++++++ src/kimchi/control/utils.py | 103 ++++++++++++++++++++++++++++++++++++++++ should also update contrib/kimchi.spec.fedora.in contrib/kimchi.spec.suse.in
src/kimchi/controller.py | 71 +-------------------------- 4 files changed, 127 insertions(+), 69 deletions(-) create mode 100644 src/kimchi/control/__init__.py create mode 100644 src/kimchi/control/utils.py
diff --git a/Makefile.am b/Makefile.am index 0fd92c8..65b39b8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,6 +45,7 @@ PEP8_WHITELIST = \ src/kimchi/disks.py \ src/kimchi/root.py \ src/kimchi/server.py \ + src/kimchi/control/utils.py \ plugins/__init__.py \ plugins/sample/__init__.py \ plugins/sample/model.py \ should also update src/kimchi/Makefile.am
diff --git a/src/kimchi/Makefile.am b/src/kimchi/Makefile.am index 47a3bd2..78af50f 100644 --- a/src/kimchi/Makefile.am +++ b/src/kimchi/Makefile.am @@ -20,6 +20,8 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+SUBDIRS = control + kimchi_PYTHON = \ asynctask.py \ auth.py \
should also add a new Makefile.am, src/kimchi/control/Makefile.am
diff --git a/src/kimchi/control/Makefile.am b/src/kimchi/control/Makefile.am new file mode 100644 index 0000000..220811f --- /dev/null +++ b/src/kimchi/control/Makefile.am @@ -0,0 +1,27 @@ +# +# Kimchi +# +# Copyright IBM Corp, 2013 +# +# Authors: +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +kimchicontroldir = $(pythondir)/kimchi/control + +dist_kimchicontrol_PYTHON = \ + __init__.py \ + utils.py
diff --git a/src/kimchi/control/__init__.py b/src/kimchi/control/__init__.py new file mode 100644 index 0000000..55cd7bd --- /dev/null +++ b/src/kimchi/control/__init__.py @@ -0,0 +1,21 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Aline Manera<alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/src/kimchi/control/utils.py b/src/kimchi/control/utils.py new file mode 100644 index 0000000..311b5a6 --- /dev/null +++ b/src/kimchi/control/utils.py @@ -0,0 +1,103 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke<agl@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +import cherrypy +import json + + +from jsonschema import Draft3Validator, ValidationError + + +from kimchi.exception import InvalidParameter + + +def get_class_name(cls): + try: + sub_class = cls.__subclasses__()[0] + except AttributeError: + sub_class = cls.__class__.__name__ + return sub_class.lower() + + +def model_fn(cls, fn_name): + return '%s_%s' % (get_class_name(cls), fn_name) + + +def validate_method(allowed): + method = cherrypy.request.method.upper() + if method not in allowed: + raise cherrypy.HTTPError(405) + return method + + +def mime_in_header(header, mime): + if not header in cherrypy.request.headers: + accepts = 'application/json' + else: + accepts = cherrypy.request.headers[header] + + if accepts.find(';') != -1: + accepts, _ = accepts.split(';', 1) + + if mime in accepts.split(','): + return True + + return False + + +def parse_request(): + if 'Content-Length' not in cherrypy.request.headers: + return {} + rawbody = cherrypy.request.body.read() + + if mime_in_header('Content-Type', 'application/json'): + try: + return json.loads(rawbody) + except ValueError: + raise cherrypy.HTTPError(400, "Unable to parse JSON request") + else: + raise cherrypy.HTTPError(415, "This API only supports" + " 'application/json'") + + +def internal_redirect(url): + raise cherrypy.InternalRedirect(url.encode("utf-8")) + + +def validate_params(params, instance, action): + root = cherrypy.request.app.root + + if hasattr(root, 'api_schema'): + api_schema = root.api_schema + else: + return + + operation = model_fn(instance, action) + validator = Draft3Validator(api_schema) + request = {operation: params} + + try: + validator.validate(request) + except ValidationError: + raise InvalidParameter('; '.join( + e.message for e in validator.iter_errors(request))) diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index ba8b53d..aec1579 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -21,88 +21,21 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import cherrypy -import json import urllib2
from functools import wraps -from jsonschema import Draft3Validator, ValidationError
import kimchi.template from kimchi import auth +from kimchi.control.utils import get_class_name, internal_redirect, model_fn +from kimchi.control.utils import parse_request, validate_method, validate_params from kimchi.exception import InvalidOperation, InvalidParameter, MissingParameter from kimchi.exception import NotFoundError, OperationFailed from kimchi.model import ISO_POOL_NAME
-def get_class_name(cls): - try: - sub_class = cls.__subclasses__()[0] - except AttributeError: - sub_class = cls.__class__.__name__ - return sub_class.lower() - - -def model_fn(cls, fn_name): - return '%s_%s' % (get_class_name(cls), fn_name) - - -def validate_method(allowed): - method = cherrypy.request.method.upper() - if method not in allowed: - raise cherrypy.HTTPError(405) - return method - - -def mime_in_header(header, mime): - if not header in cherrypy.request.headers: - accepts = 'application/json' - else: - accepts = cherrypy.request.headers[header] - - if accepts.find(';') != -1: - accepts, _ = accepts.split(';', 1) - - if mime in accepts.split(','): - return True - - return False - - -def parse_request(): - if 'Content-Length' not in cherrypy.request.headers: - return {} - rawbody = cherrypy.request.body.read() - - if mime_in_header('Content-Type', 'application/json'): - try: - return json.loads(rawbody) - except ValueError: - raise cherrypy.HTTPError(400, "Unable to parse JSON request") - else: - raise cherrypy.HTTPError(415, "This API only supports" - " 'application/json'") -def internal_redirect(url): - raise cherrypy.InternalRedirect(url.encode("utf-8")) - - -def validate_params(params, instance, action): - root = cherrypy.request.app.root - if hasattr(root, 'api_schema'): - api_schema = root.api_schema - else: - return - operation = model_fn(instance, action) - validator = Draft3Validator(api_schema) - request = {operation: params} - try: - validator.validate(request) - except ValidationError: - raise InvalidParameter('; '.join( - e.message for e in validator.iter_errors(request))) - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine)
-- Sheldon Feng(???)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
The following functions: get_class_name, internal_redirect, model_fn, parse_request, validate_method, validate_params are used in Resource and Collection classes. Move them to a new module control/utils.py - which will have all common functions to API resources
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/__init__.py | 21 ++++++++ src/kimchi/control/utils.py | 103 ++++++++++++++++++++++++++++++++++++++++ src/kimchi/controller.py | 71 +-------------------------- 4 files changed, 127 insertions(+), 69 deletions(-) create mode 100644 src/kimchi/control/__init__.py create mode 100644 src/kimchi/control/utils.py You also need update Makefile.am under src/kimchi and add Makefile.am in src/kimchi/control.
Package spec file needs update too.
diff --git a/Makefile.am b/Makefile.am index 0fd92c8..65b39b8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,6 +45,7 @@ PEP8_WHITELIST = \ src/kimchi/disks.py \ src/kimchi/root.py \ src/kimchi/server.py \ + src/kimchi/control/utils.py \
all new added python files under src/kimchi/control are pep8 clean, so you can just use a wildcard: src/kimchi/control/*.py \
plugins/__init__.py \ plugins/sample/__init__.py \ plugins/sample/model.py \ diff --git a/src/kimchi/control/__init__.py b/src/kimchi/control/__init__.py new file mode 100644 index 0000000..55cd7bd --- /dev/null +++ b/src/kimchi/control/__init__.py @@ -0,0 +1,21 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/src/kimchi/control/utils.py b/src/kimchi/control/utils.py new file mode 100644 index 0000000..311b5a6 --- /dev/null +++ b/src/kimchi/control/utils.py @@ -0,0 +1,103 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +import cherrypy +import json + + +from jsonschema import Draft3Validator, ValidationError + + +from kimchi.exception import InvalidParameter + + +def get_class_name(cls): + try: + sub_class = cls.__subclasses__()[0] + except AttributeError: + sub_class = cls.__class__.__name__ + return sub_class.lower() + + +def model_fn(cls, fn_name): + return '%s_%s' % (get_class_name(cls), fn_name) + + +def validate_method(allowed): + method = cherrypy.request.method.upper() + if method not in allowed: + raise cherrypy.HTTPError(405) + return method + + +def mime_in_header(header, mime): + if not header in cherrypy.request.headers: + accepts = 'application/json' + else: + accepts = cherrypy.request.headers[header] + + if accepts.find(';') != -1: + accepts, _ = accepts.split(';', 1) + + if mime in accepts.split(','): + return True + + return False + + +def parse_request(): + if 'Content-Length' not in cherrypy.request.headers: + return {} + rawbody = cherrypy.request.body.read() + + if mime_in_header('Content-Type', 'application/json'): + try: + return json.loads(rawbody) + except ValueError: + raise cherrypy.HTTPError(400, "Unable to parse JSON request") + else: + raise cherrypy.HTTPError(415, "This API only supports" + " 'application/json'") + + +def internal_redirect(url): + raise cherrypy.InternalRedirect(url.encode("utf-8")) + + +def validate_params(params, instance, action): + root = cherrypy.request.app.root + + if hasattr(root, 'api_schema'): + api_schema = root.api_schema + else: + return + + operation = model_fn(instance, action) + validator = Draft3Validator(api_schema) + request = {operation: params} + + try: + validator.validate(request) + except ValidationError: + raise InvalidParameter('; '.join( + e.message for e in validator.iter_errors(request))) diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index ba8b53d..aec1579 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -21,88 +21,21 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import cherrypy -import json import urllib2
from functools import wraps -from jsonschema import Draft3Validator, ValidationError
import kimchi.template from kimchi import auth +from kimchi.control.utils import get_class_name, internal_redirect, model_fn +from kimchi.control.utils import parse_request, validate_method, validate_params from kimchi.exception import InvalidOperation, InvalidParameter, MissingParameter from kimchi.exception import NotFoundError, OperationFailed from kimchi.model import ISO_POOL_NAME
-def get_class_name(cls): - try: - sub_class = cls.__subclasses__()[0] - except AttributeError: - sub_class = cls.__class__.__name__ - return sub_class.lower() - - -def model_fn(cls, fn_name): - return '%s_%s' % (get_class_name(cls), fn_name) - - -def validate_method(allowed): - method = cherrypy.request.method.upper() - if method not in allowed: - raise cherrypy.HTTPError(405) - return method - - -def mime_in_header(header, mime): - if not header in cherrypy.request.headers: - accepts = 'application/json' - else: - accepts = cherrypy.request.headers[header] - - if accepts.find(';') != -1: - accepts, _ = accepts.split(';', 1) - - if mime in accepts.split(','): - return True - - return False - - -def parse_request(): - if 'Content-Length' not in cherrypy.request.headers: - return {} - rawbody = cherrypy.request.body.read() - - if mime_in_header('Content-Type', 'application/json'): - try: - return json.loads(rawbody) - except ValueError: - raise cherrypy.HTTPError(400, "Unable to parse JSON request") - else: - raise cherrypy.HTTPError(415, "This API only supports" - " 'application/json'") -def internal_redirect(url): - raise cherrypy.InternalRedirect(url.encode("utf-8")) - - -def validate_params(params, instance, action): - root = cherrypy.request.app.root - if hasattr(root, 'api_schema'): - api_schema = root.api_schema - else: - return - operation = model_fn(instance, action) - validator = Draft3Validator(api_schema) - request = {operation: params} - try: - validator.validate(request) - except ValidationError: - raise InvalidParameter('; '.join( - e.message for e in validator.iter_errors(request))) - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine)

于 2013/12/24 2:41, Aline Manera 写道:
From: Aline Manera <alinefm@br.ibm.com>
The following functions: get_class_name, internal_redirect, model_fn, parse_request, validate_method, validate_params are used in Resource and Collection classes. Move them to a new module control/utils.py - which will have all common functions to API resources
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/__init__.py | 21 ++++++++ src/kimchi/control/utils.py | 103 ++++++++++++++++++++++++++++++++++++++++ src/kimchi/controller.py | 71 +-------------------------- 4 files changed, 127 insertions(+), 69 deletions(-) create mode 100644 src/kimchi/control/__init__.py create mode 100644 src/kimchi/control/utils.py
diff --git a/Makefile.am b/Makefile.am index 0fd92c8..65b39b8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,6 +45,7 @@ PEP8_WHITELIST = \ src/kimchi/disks.py \ src/kimchi/root.py \ src/kimchi/server.py \ + src/kimchi/control/utils.py \ plugins/__init__.py \ plugins/sample/__init__.py \ plugins/sample/model.py \ diff --git a/src/kimchi/control/__init__.py b/src/kimchi/control/__init__.py new file mode 100644 index 0000000..55cd7bd --- /dev/null +++ b/src/kimchi/control/__init__.py @@ -0,0 +1,21 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/src/kimchi/control/utils.py b/src/kimchi/control/utils.py new file mode 100644 index 0000000..311b5a6 --- /dev/null +++ b/src/kimchi/control/utils.py @@ -0,0 +1,103 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +import cherrypy +import json + + +from jsonschema import Draft3Validator, ValidationError + + +from kimchi.exception import InvalidParameter + + +def get_class_name(cls): + try: + sub_class = cls.__subclasses__()[0] + except AttributeError: + sub_class = cls.__class__.__name__ + return sub_class.lower() + + +def model_fn(cls, fn_name): + return '%s_%s' % (get_class_name(cls), fn_name) + + +def validate_method(allowed): + method = cherrypy.request.method.upper() + if method not in allowed: + raise cherrypy.HTTPError(405) + return method + + +def mime_in_header(header, mime): + if not header in cherrypy.request.headers: + accepts = 'application/json' + else: + accepts = cherrypy.request.headers[header] + + if accepts.find(';') != -1: + accepts, _ = accepts.split(';', 1) + + if mime in accepts.split(','): + return True + + return False + + +def parse_request(): + if 'Content-Length' not in cherrypy.request.headers: + return {} + rawbody = cherrypy.request.body.read() + + if mime_in_header('Content-Type', 'application/json'): + try: + return json.loads(rawbody) + except ValueError: + raise cherrypy.HTTPError(400, "Unable to parse JSON request") + else: + raise cherrypy.HTTPError(415, "This API only supports" + " 'application/json'") + + +def internal_redirect(url): + raise cherrypy.InternalRedirect(url.encode("utf-8")) + + +def validate_params(params, instance, action): + root = cherrypy.request.app.root + + if hasattr(root, 'api_schema'): + api_schema = root.api_schema + else: + return + + operation = model_fn(instance, action) + validator = Draft3Validator(api_schema) + request = {operation: params} + + try: + validator.validate(request) + except ValidationError: + raise InvalidParameter('; '.join( + e.message for e in validator.iter_errors(request)))
I am not sure if we should expose validate_params() to utils.py. Because it only be referenced by Class Resource and Collection, it is better to keep it in the same file as Class Resource and Collection.
diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index ba8b53d..aec1579 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -21,88 +21,21 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import cherrypy -import json import urllib2
from functools import wraps -from jsonschema import Draft3Validator, ValidationError
import kimchi.template from kimchi import auth +from kimchi.control.utils import get_class_name, internal_redirect, model_fn +from kimchi.control.utils import parse_request, validate_method, validate_params from kimchi.exception import InvalidOperation, InvalidParameter, MissingParameter from kimchi.exception import NotFoundError, OperationFailed from kimchi.model import ISO_POOL_NAME
-def get_class_name(cls): - try: - sub_class = cls.__subclasses__()[0] - except AttributeError: - sub_class = cls.__class__.__name__ - return sub_class.lower() - - -def model_fn(cls, fn_name): - return '%s_%s' % (get_class_name(cls), fn_name) - - -def validate_method(allowed): - method = cherrypy.request.method.upper() - if method not in allowed: - raise cherrypy.HTTPError(405) - return method - - -def mime_in_header(header, mime): - if not header in cherrypy.request.headers: - accepts = 'application/json' - else: - accepts = cherrypy.request.headers[header] - - if accepts.find(';') != -1: - accepts, _ = accepts.split(';', 1) - - if mime in accepts.split(','): - return True - - return False - - -def parse_request(): - if 'Content-Length' not in cherrypy.request.headers: - return {} - rawbody = cherrypy.request.body.read() - - if mime_in_header('Content-Type', 'application/json'): - try: - return json.loads(rawbody) - except ValueError: - raise cherrypy.HTTPError(400, "Unable to parse JSON request") - else: - raise cherrypy.HTTPError(415, "This API only supports" - " 'application/json'") -def internal_redirect(url): - raise cherrypy.InternalRedirect(url.encode("utf-8")) - - -def validate_params(params, instance, action): - root = cherrypy.request.app.root - if hasattr(root, 'api_schema'): - api_schema = root.api_schema - else: - return - operation = model_fn(instance, action) - validator = Draft3Validator(api_schema) - request = {operation: params} - try: - validator.validate(request) - except ValidationError: - raise InvalidParameter('; '.join( - e.message for e in validator.iter_errors(request))) - - class Resource(object): """ A Resource represents a single entity in the API (such as a Virtual Machine)

From: Aline Manera <alinefm@br.ibm.com> controller.py file must contain all related to Kimchi API resources Common cherrypy functions must be in root.py Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/controller.py | 20 -------------------- src/kimchi/root.py | 25 +++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index aec1579..5bcffff 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -28,7 +28,6 @@ from functools import wraps import kimchi.template -from kimchi import auth from kimchi.control.utils import get_class_name, internal_redirect, model_fn from kimchi.control.utils import parse_request, validate_method, validate_params from kimchi.exception import InvalidOperation, InvalidParameter, MissingParameter @@ -649,25 +648,6 @@ class Partition(Resource): def data(self): return self.info -@cherrypy.expose -def login(*args): - params = parse_request() - try: - userid = params['userid'] - password = params['password'] - except KeyError, key: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % key) - try: - auth.login(userid, password) - except OperationFailed: - raise cherrypy.HTTPError(401) - return '{}' - -@cherrypy.expose -def logout(): - auth.logout() - return '{}' - class Plugins(Collection): def __init__(self, model): super(Plugins, self).__init__(model) diff --git a/src/kimchi/root.py b/src/kimchi/root.py index d1fe818..6310781 100644 --- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -25,9 +25,11 @@ import cherrypy import json +from kimchi import auth from kimchi import controller from kimchi import template from kimchi.config import get_api_schema_file +from kimchi.control.utils import parse_request class Root(controller.Resource): @@ -52,8 +54,6 @@ class Root(controller.Resource): self.tasks = controller.Tasks(model) self.config = controller.Config(model) self.host = controller.Host(model) - self.login = controller.login - self.logout = controller.logout self.debugreports = controller.DebugReports(model) self.plugins = controller.Plugins(model) self.api_schema = json.load(open(get_api_schema_file())) @@ -87,3 +87,24 @@ class Root(controller.Resource): if page.endswith('.html'): return template.render('tabs/' + page, None) raise cherrypy.HTTPError(404) + + @cherrypy.expose + def login(*args): + params = parse_request() + try: + userid = params['userid'] + password = params['password'] + except KeyError, key: + raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % key) + + try: + auth.login(userid, password) + except OperationFailed: + raise cherrypy.HTTPError(401) + + return '{}' + + @cherrypy.expose + def logout(): + auth.logout() + return '{}' -- 1.7.10.4

Reviewed-By: Ramon Medeiros <ramonn@br.ibm.com>

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

The only suggestion in this patch is to make errors as constants. Maybe, we can create a library or a file that we can import the errors!

On 12/23/2013 05:53 PM, Ramon Medeiros wrote:
The only suggestion in this patch is to make errors as constants. Maybe, we can create a library or a file that we can import the errors!
All backend errors will be refactored during Improve error feedback <https://github.com/kimchi-project/kimchi/wiki/refactor-exception> task <https://github.com/kimchi-project/kimchi/wiki/refactor-exception>

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

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

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

On 12/26/2013 04:47 AM, Shu Ming wrote:
As the other python files named after the class name contained, like vms.py vs class VMs, templates.py vs class Templates. We should name this file as "collection.py" instead of "base.py".
In fact the file names refer to the kimchi uri: in vms.py will contain all related to /vms
2013/12/24 2:41, Aline Manera:
From: Aline Manera <alinefm@br.ibm.com>
Resource(), Collection() and AsyncCollection classes are base for all Kimchi resources. Move them to a separated file as they should not be changed with high frequency
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/base.py | 290 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 src/kimchi/control/base.py
diff --git a/Makefile.am b/Makefile.am index 65b39b8..997d4cc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,6 +45,7 @@ PEP8_WHITELIST = \ src/kimchi/disks.py \ src/kimchi/root.py \ src/kimchi/server.py \ + src/kimchi/control/base.py \ src/kimchi/control/utils.py \ plugins/__init__.py \ plugins/sample/__init__.py \ diff --git a/src/kimchi/control/base.py b/src/kimchi/control/base.py new file mode 100644 index 0000000..22452fc --- /dev/null +++ b/src/kimchi/control/base.py @@ -0,0 +1,290 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linix.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import cherrypy +import urllib2 + + +import kimchi.template +from kimchi.control.utils import get_class_name, internal_redirect, model_fn +from kimchi.control.utils import parse_request, validate_method +from kimchi.control.utils import validate_params +from kimchi.exception import InvalidOperation, InvalidParameter +from kimchi.exception import MissingParameter, NotFoundError, OperationFailed + + +class Resource(object): + """ + A Resource represents a single entity in the API (such as a Virtual + Machine) + + To create new Resource types, subclass this and change the following things + in the child class: + + - If the Resource requires more than one identifier set self.model_args as + appropriate. This should only be necessary if this Resource is logically + nested. For example: A Storage Volume belongs to a Storage Pool so the + Storage Volume would set model args to (pool_ident, volume_ident). + + - Implement the base operations of 'lookup' and 'delete' in the model(s). + + - Set the 'data' property to a JSON-serializable representation of the + Resource. + """ + def __init__(self, model, ident=None): + self.model = model + self.ident = ident + self.model_args = (ident,) + self.update_params = [] + + def generate_action_handler(self, instance, action_name, action_args=None): + def wrapper(*args, **kwargs): + validate_method(('POST')) + try: + model_args = list(instance.model_args) + if action_args is not None: + model_args.extend(parse_request()[key] + for key in action_args) + fn = getattr(instance.model, model_fn(instance, action_name)) + fn(*model_args) + uri_params = tuple(instance.model_args) + raise internal_redirect(instance.uri_fmt % uri_params) + except MissingParameter, param: + error = "Missing parameter: '%s'" % param + raise cherrypy.HTTPError(400, error) + except InvalidParameter, param: + error = "Invalid parameter: '%s'" % param + raise cherrypy.HTTPError(400, error) + except InvalidOperation, msg: + raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) + except OperationFailed, msg: + raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) + except NotFoundError, msg: + raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) + + wrapper.__name__ = action_name + wrapper.exposed = True + return wrapper + + def lookup(self): + try: + lookup = getattr(self.model, model_fn(self, 'lookup')) + self.info = lookup(*self.model_args) + except AttributeError: + self.info = {} + + def delete(self): + try: + fn = getattr(self.model, model_fn(self, 'delete')) + fn(*self.model_args) + cherrypy.response.status = 204 + except AttributeError: + error = "Delete is not allowed for %s" % get_class_name(self) + raise cherrypy.HTTPError(405, error) + except OperationFailed, msg: + raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) + except InvalidOperation, msg: + raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) + + @cherrypy.expose + def index(self): + method = validate_method(('GET', 'DELETE', 'PUT')) + if method == 'GET': + try: + return self.get() + except NotFoundError, msg: + raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) + except InvalidOperation, msg: + raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) + except OperationFailed, msg: + raise cherrypy.HTTPError(406, "Operation failed: '%s'" % msg) + elif method == 'DELETE': + try: + return self.delete() + except NotFoundError, msg: + raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) + elif method == 'PUT': + try: + return self.update() + except InvalidParameter, msg: + raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % msg) + except InvalidOperation, msg: + raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) + except NotFoundError, msg: + raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) + + def update(self): + try: + update = getattr(self.model, model_fn(self, 'update')) + except AttributeError: + error = "%s does not implement update method" + raise cherrypy.HTTPError(405, error % get_class_name(self)) + + params = parse_request() + validate_params(params, self, 'update') + + if self.update_params is not None: + invalids = [v for v in params.keys() if + v not in self.update_params] + if invalids: + error = "%s are not allowed to be updated" % invalids + raise cherrypy.HTTPError(405, error) + + ident = update(self.ident, params) + if ident != self.ident: + uri_params = list(self.model_args[:-1]) + uri_params += [urllib2.quote(ident.encode('utf8'))] + raise cherrypy.HTTPRedirect(self.uri_fmt % tuple(uri_params), 303) + + return self.get() + + def get(self): + self.lookup() + return kimchi.template.render(get_class_name(self), self.data) + + @property + def data(self): + """ + Override this in inherited classes to provide the Resource + representation as a python dictionary. + """ + return {} + + +class Collection(object): + """ + A Collection is a container for Resource objects. To create a new + Collection type, subclass this and make the following changes to the child + class: + + - Set self.resource to the type of Resource that this Collection contains + + - Set self.resource_args. This can remain an empty list if the Resources + can be initialized with only one identifier. Otherwise, include + additional values as needed (eg. to identify a parent resource). + + - Set self.model_args. Similar to above, this is needed only if the model + needs additional information to identify this Collection. + + - Implement the base operations of 'create' and 'get_list' in the model. + """ + def __init__(self, model): + self.model = model + self.resource = Resource + self.resource_args = [] + self.model_args = [] + + def create(self, *args): + try: + create = getattr(self.model, model_fn(self, 'create')) + except AttributeError: + error = 'Create is not allowed for %s' % get_class_name(self) + raise cherrypy.HTTPError(405, error) + + params = parse_request() + validate_params(params, self, 'create') + args = self.model_args + [params] + name = create(*args) + cherrypy.response.status = 201 + args = self.resource_args + [name] + res = self.resource(self.model, *args) + + return res.get() + + def _get_resources(self): + try: + get_list = getattr(self.model, model_fn(self, 'get_list')) + idents = get_list(*self.model_args) + res_list = [] + for ident in idents: + # internal text, get_list changes ident to unicode for sorted + args = self.resource_args + [ident] + res = self.resource(self.model, *args) + res.lookup() + res_list.append(res) + return res_list + except AttributeError: + return [] + + def _cp_dispatch(self, vpath): + if vpath: + ident = vpath.pop(0) + # incoming text, from URL, is not unicode, need decode + args = self.resource_args + [ident.decode("utf-8")] + return self.resource(self.model, *args) + + def get(self): + resources = self._get_resources() + data = [] + for res in resources: + data.append(res.data) + return kimchi.template.render(get_class_name(self), data) + + @cherrypy.expose + def index(self, *args): + method = validate_method(('GET', 'POST')) + if method == 'GET': + try: + return self.get() + except InvalidOperation, param: + error = "Invalid operation: '%s'" % param + raise cherrypy.HTTPError(400, error) + except NotFoundError, param: + raise cherrypy.HTTPError(404, "Not found: '%s'" % param) + + elif method == 'POST': + try: + return self.create(*args) + except MissingParameter, param: + error = "Missing parameter: '%s'" % param + raise cherrypy.HTTPError(400, error) + except InvalidParameter, param: + error = "Invalid parameter: '%s'" % param + raise cherrypy.HTTPError(400, error) + except OperationFailed, param: + raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % param) + except InvalidOperation, param: + error = "Invalid operation: '%s'" % param + raise cherrypy.HTTPError(400, error) + except NotFoundError, param: + raise cherrypy.HTTPError(404, "Not found: '%s'" % param) + + +class AsyncCollection(Collection): + """ + A Collection to create it's resource by asynchronous task + """ + def __init__(self, model): + super(AsyncCollection, self).__init__(model) + + def create(self, *args): + try: + create = getattr(self.model, model_fn(self, 'create')) + except AttributeError: + raise cherrypy.HTTPError(405, + 'Create is not allowed for %s' % get_class_name(self)) + params = parse_request() + args = self.model_args + [params] + task = create(*args) + cherrypy.response.status = 202 + return kimchi.template.render("Task", task)

From: Aline Manera <alinefm@br.ibm.com> VMs(Collection), VM(Resource) and VMScreenshot(Resource) were moved to a new - control/vms.py That way we can easily know where vm resource is implemented. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/vms.py | 64 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/kimchi/control/vms.py diff --git a/Makefile.am b/Makefile.am index 997d4cc..3fda86f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/utils.py \ + src/kimchi/control/vms.py \ plugins/__init__.py \ plugins/sample/__init__.py \ plugins/sample/model.py \ diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py new file mode 100644 index 0000000..da960cd --- /dev/null +++ b/src/kimchi/control/vms.py @@ -0,0 +1,64 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import internal_redirect + + +class VMs(Collection): + def __init__(self, model): + super(VMs, self).__init__(model) + self.resource = VM + + +class VM(Resource): + def __init__(self, model, ident): + super(VM, self).__init__(model, ident) + self.update_params = ["name"] + self.screenshot = VMScreenShot(model, ident) + self.uri_fmt = '/vms/%s' + self.start = self.generate_action_handler(self, 'start') + self.stop = self.generate_action_handler(self, 'stop') + self.connect = self.generate_action_handler(self, 'connect') + + @property + def data(self): + return {'name': self.ident, + 'uuid': self.info['uuid'], + 'stats': self.info['stats'], + 'memory': self.info['memory'], + 'cpus': self.info['cpus'], + 'state': self.info['state'], + 'screenshot': self.info['screenshot'], + 'icon': self.info['icon'], + 'graphics': {'type': self.info['graphics']['type'], + 'port': self.info['graphics']['port']}} + + +class VMScreenShot(Resource): + def __init__(self, model, ident): + super(VMScreenShot, self).__init__(model, ident) + + def get(self): + self.lookup() + raise internal_redirect(self.info) -- 1.7.10.4

Reviewed-By: Ramon Medeiros <ramonn@br.ibm.com>

On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
VMs(Collection), VM(Resource) and VMScreenshot(Resource) were moved to a new - control/vms.py That way we can easily know where vm resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/vms.py | 64 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/kimchi/control/vms.py
diff --git a/Makefile.am b/Makefile.am index 997d4cc..3fda86f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/utils.py \ + src/kimchi/control/vms.py \ plugins/__init__.py \ plugins/sample/__init__.py \ plugins/sample/model.py \ diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py new file mode 100644 index 0000000..da960cd --- /dev/null +++ b/src/kimchi/control/vms.py @@ -0,0 +1,64 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import internal_redirect + + +class VMs(Collection): + def __init__(self, model): + super(VMs, self).__init__(model) + self.resource = VM + + +class VM(Resource): + def __init__(self, model, ident): + super(VM, self).__init__(model, ident) + self.update_params = ["name"] + self.screenshot = VMScreenShot(model, ident) + self.uri_fmt = '/vms/%s' + self.start = self.generate_action_handler(self, 'start') + self.stop = self.generate_action_handler(self, 'stop') + self.connect = self.generate_action_handler(self, 'connect') + + @property + def data(self): + return {'name': self.ident, + 'uuid': self.info['uuid'], + 'stats': self.info['stats'], + 'memory': self.info['memory'], + 'cpus': self.info['cpus'], + 'state': self.info['state'], + 'screenshot': self.info['screenshot'], + 'icon': self.info['icon'], + 'graphics': {'type': self.info['graphics']['type'], + 'port': self.info['graphics']['port']}} + + +class VMScreenShot(Resource): + def __init__(self, model, ident): + super(VMScreenShot, self).__init__(model, ident) + + def get(self): + self.lookup() + raise internal_redirect(self.info) I don't understand why the original code was remained there.

On 12/24/2013 04:26 AM, Mark Wu wrote:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
VMs(Collection), VM(Resource) and VMScreenshot(Resource) were moved to a new - control/vms.py That way we can easily know where vm resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/vms.py | 64 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/kimchi/control/vms.py
diff --git a/Makefile.am b/Makefile.am index 997d4cc..3fda86f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/utils.py \ + src/kimchi/control/vms.py \ plugins/__init__.py \ plugins/sample/__init__.py \ plugins/sample/model.py \ diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py new file mode 100644 index 0000000..da960cd --- /dev/null +++ b/src/kimchi/control/vms.py @@ -0,0 +1,64 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import internal_redirect + + +class VMs(Collection): + def __init__(self, model): + super(VMs, self).__init__(model) + self.resource = VM + + +class VM(Resource): + def __init__(self, model, ident): + super(VM, self).__init__(model, ident) + self.update_params = ["name"] + self.screenshot = VMScreenShot(model, ident) + self.uri_fmt = '/vms/%s' + self.start = self.generate_action_handler(self, 'start') + self.stop = self.generate_action_handler(self, 'stop') + self.connect = self.generate_action_handler(self, 'connect') + + @property + def data(self): + return {'name': self.ident, + 'uuid': self.info['uuid'], + 'stats': self.info['stats'], + 'memory': self.info['memory'], + 'cpus': self.info['cpus'], + 'state': self.info['state'], + 'screenshot': self.info['screenshot'], + 'icon': self.info['icon'], + 'graphics': {'type': self.info['graphics']['type'], + 'port': self.info['graphics']['port']}} + + +class VMScreenShot(Resource): + def __init__(self, model, ident): + super(VMScreenShot, self).__init__(model, ident) + + def get(self): + self.lookup() + raise internal_redirect(self.info) I don't understand why the original code was remained there.
I removed the controller.py file in the last commit. After all changes are done.

On 12/24/2013 06:50 PM, Aline Manera wrote:
On 12/24/2013 04:26 AM, Mark Wu wrote:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
VMs(Collection), VM(Resource) and VMScreenshot(Resource) were moved to a new - control/vms.py That way we can easily know where vm resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/vms.py | 64 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/kimchi/control/vms.py
diff --git a/Makefile.am b/Makefile.am index 997d4cc..3fda86f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/utils.py \ + src/kimchi/control/vms.py \ plugins/__init__.py \ plugins/sample/__init__.py \ plugins/sample/model.py \ diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py new file mode 100644 index 0000000..da960cd --- /dev/null +++ b/src/kimchi/control/vms.py @@ -0,0 +1,64 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import internal_redirect + + +class VMs(Collection): + def __init__(self, model): + super(VMs, self).__init__(model) + self.resource = VM + + +class VM(Resource): + def __init__(self, model, ident): + super(VM, self).__init__(model, ident) + self.update_params = ["name"] + self.screenshot = VMScreenShot(model, ident) + self.uri_fmt = '/vms/%s' + self.start = self.generate_action_handler(self, 'start') + self.stop = self.generate_action_handler(self, 'stop') + self.connect = self.generate_action_handler(self, 'connect') + + @property + def data(self): + return {'name': self.ident, + 'uuid': self.info['uuid'], + 'stats': self.info['stats'], + 'memory': self.info['memory'], + 'cpus': self.info['cpus'], + 'state': self.info['state'], + 'screenshot': self.info['screenshot'], + 'icon': self.info['icon'], + 'graphics': {'type': self.info['graphics']['type'], + 'port': self.info['graphics']['port']}} + + +class VMScreenShot(Resource): + def __init__(self, model, ident): + super(VMScreenShot, self).__init__(model, ident) + + def get(self): + self.lookup() + raise internal_redirect(self.info) I don't understand why the original code was remained there.
I removed the controller.py file in the last commit. After all changes are done. I don't think we should do it in this way. we need keep very commit is clean. It's confusing to include duplicate code in two files.

On 12/25/2013 11:29 PM, Mark Wu wrote:
On 12/24/2013 06:50 PM, Aline Manera wrote:
On 12/24/2013 04:26 AM, Mark Wu wrote:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
VMs(Collection), VM(Resource) and VMScreenshot(Resource) were moved to a new - control/vms.py That way we can easily know where vm resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/vms.py | 64 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/kimchi/control/vms.py
diff --git a/Makefile.am b/Makefile.am index 997d4cc..3fda86f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/utils.py \ + src/kimchi/control/vms.py \ plugins/__init__.py \ plugins/sample/__init__.py \ plugins/sample/model.py \ diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py new file mode 100644 index 0000000..da960cd --- /dev/null +++ b/src/kimchi/control/vms.py @@ -0,0 +1,64 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import internal_redirect + + +class VMs(Collection): + def __init__(self, model): + super(VMs, self).__init__(model) + self.resource = VM + + +class VM(Resource): + def __init__(self, model, ident): + super(VM, self).__init__(model, ident) + self.update_params = ["name"] + self.screenshot = VMScreenShot(model, ident) + self.uri_fmt = '/vms/%s' + self.start = self.generate_action_handler(self, 'start') + self.stop = self.generate_action_handler(self, 'stop') + self.connect = self.generate_action_handler(self, 'connect') + + @property + def data(self): + return {'name': self.ident, + 'uuid': self.info['uuid'], + 'stats': self.info['stats'], + 'memory': self.info['memory'], + 'cpus': self.info['cpus'], + 'state': self.info['state'], + 'screenshot': self.info['screenshot'], + 'icon': self.info['icon'], + 'graphics': {'type': self.info['graphics']['type'], + 'port': self.info['graphics']['port']}} + + +class VMScreenShot(Resource): + def __init__(self, model, ident): + super(VMScreenShot, self).__init__(model, ident) + + def get(self): + self.lookup() + raise internal_redirect(self.info) I don't understand why the original code was remained there.
I removed the controller.py file in the last commit. After all changes are done. I don't think we should do it in this way. we need keep very commit is clean. It's confusing to include duplicate code in two files.
Ok. I can change it on V2

From: Aline Manera <alinefm@br.ibm.com> Templates(Collection) and Template(Resource) were moved to a new - control/templates.py That way we can easily know where template resource is implemented. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/templates.py | 51 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/kimchi/control/templates.py diff --git a/Makefile.am b/Makefile.am index 3fda86f..f510e11 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,7 @@ PEP8_WHITELIST = \ src/kimchi/root.py \ src/kimchi/server.py \ src/kimchi/control/base.py \ + src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ plugins/__init__.py \ diff --git a/src/kimchi/control/templates.py b/src/kimchi/control/templates.py new file mode 100644 index 0000000..a0de25c --- /dev/null +++ b/src/kimchi/control/templates.py @@ -0,0 +1,51 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Templates(Collection): + def __init__(self, model): + super(Templates, self).__init__(model) + self.resource = Template + + +class Template(Resource): + def __init__(self, model, ident): + super(Template, self).__init__(model, ident) + self.update_params = ["name", "folder", "icon", "os_distro", + "storagepool", "os_version", "cpus", + "memory", "cdrom", "disks"] + self.uri_fmt = "/templates/%s" + + @property + def data(self): + return {'name': self.ident, + 'icon': self.info['icon'], + 'os_distro': self.info['os_distro'], + 'os_version': self.info['os_version'], + 'cpus': self.info['cpus'], + 'memory': self.info['memory'], + 'cdrom': self.info['cdrom'], + 'disks': self.info['disks'], + 'storagepool': self.info['storagepool'], + 'folder': self.info.get('folder', [])} -- 1.7.10.4

Reviewed-By: Ramon Medeiros <ramonn@br.ibm.com>

From: Aline Manera <alinefm@br.ibm.com> DebugReports(Collection), DebugReport(Resource) and DebugReportContent(Resource) were moved to a new - control/debugreports.py That way we can easily know where debug report resource is implemented. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/debugreports.py | 52 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/kimchi/control/debugreports.py diff --git a/Makefile.am b/Makefile.am index f510e11..a16cd2a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,7 @@ PEP8_WHITELIST = \ src/kimchi/root.py \ src/kimchi/server.py \ src/kimchi/control/base.py \ + src/kimchi/control/debugreports.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/debugreports.py b/src/kimchi/control/debugreports.py new file mode 100644 index 0000000..e36aa75 --- /dev/null +++ b/src/kimchi/control/debugreports.py @@ -0,0 +1,52 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import AsyncCollection, Resource +from kimchi.control.utils import internal_redirect + + +class DebugReports(AsyncCollection): + def __init__(self, model): + super(DebugReports, self).__init__(model) + self.resource = DebugReport + + +class DebugReport(Resource): + def __init__(self, model, ident): + super(DebugReport, self).__init__(model, ident) + self.content = DebugReportContent(model, ident) + + @property + def data(self): + return {'name': self.ident, + 'file': self.info['file'], + 'time': self.info['ctime']} + + +class DebugReportContent(Resource): + def __init__(self, model, ident): + super(DebugReportContent, self).__init__(model, ident) + + def get(self): + self.lookup() + raise internal_redirect(self.info['file']) -- 1.7.10.4

Reviewed-By: Ramon Medeiros <ramonn@br.ibm.com>

On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
DebugReports(Collection), DebugReport(Resource) and DebugReportContent(Resource) were moved to a new - control/debugreports.py That way we can easily know where debug report resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/debugreports.py | 52 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/kimchi/control/debugreports.py
diff --git a/Makefile.am b/Makefile.am index f510e11..a16cd2a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,7 @@ PEP8_WHITELIST = \ src/kimchi/root.py \ src/kimchi/server.py \ src/kimchi/control/base.py \ + src/kimchi/control/debugreports.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/debugreports.py b/src/kimchi/control/debugreports.py new file mode 100644 index 0000000..e36aa75 --- /dev/null +++ b/src/kimchi/control/debugreports.py @@ -0,0 +1,52 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> $ git show bb59b46c # Authors: # Shu Ming <shuming@linux.vnet.ibm.com> # Aline Manera <alinefm@linux.vnet.ibm.com>
+# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import AsyncCollection, Resource +from kimchi.control.utils import internal_redirect + + +class DebugReports(AsyncCollection): + def __init__(self, model): + super(DebugReports, self).__init__(model) + self.resource = DebugReport + + +class DebugReport(Resource): + def __init__(self, model, ident): + super(DebugReport, self).__init__(model, ident) + self.content = DebugReportContent(model, ident) + + @property + def data(self): + return {'name': self.ident, + 'file': self.info['file'], + 'time': self.info['ctime']} + + +class DebugReportContent(Resource): + def __init__(self, model, ident): + super(DebugReportContent, self).__init__(model, ident) + + def get(self): + self.lookup() + raise internal_redirect(self.info['file'])
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

From: Aline Manera <alinefm@br.ibm.com> StoragePools(Collection), StoragePool(Resource) and IsoPool(Resource) were moved to a new - control/storagepools.py That way we can easily know where storage pool resource is implemented. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/storagepools.py | 125 ++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/kimchi/control/storagepools.py diff --git a/Makefile.am b/Makefile.am index a16cd2a..f67af3d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ + src/kimchi/control/storagepools.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py new file mode 100644 index 0000000..f4fffd7 --- /dev/null +++ b/src/kimchi/control/storagepools.py @@ -0,0 +1,125 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import cherrypy + + +from kimchi.control.base import Collection, Resource +from kimchi.control.storagevolumes import IsoVolumes, StorageVolumes +from kimchi.control.utils import get_class_name, model_fn, parse_request +from kimchi.model import ISO_POOL_NAME + + +class StoragePools(Collection): + def __init__(self, model): + super(StoragePools, self).__init__(model) + self.resource = StoragePool + isos = IsoPool(model) + isos.exposed = True + setattr(self, ISO_POOL_NAME, isos) + + def create(self, *args): + try: + create = getattr(self.model, model_fn(self, 'create')) + except AttributeError: + raise cherrypy.HTTPError(405, + 'Create is not allowed for %s' % get_class_name(self)) + + params = parse_request() + args = self.model_args + [params] + name = create(*args) + args = self.resource_args + [name] + res = self.resource(self.model, *args) + resp = res.get() + + if 'task_id' in res.data: + cherrypy.response.status = 202 + else: + cherrypy.response.status = 201 + + return resp + + def _get_resources(self): + try: + res_list = super(StoragePools, self)._get_resources() + # Append reserved pools + isos = getattr(self, ISO_POOL_NAME) + isos.lookup() + res_list.append(isos) + except AttributeError: + pass + + return res_list + + +class StoragePool(Resource): + def __init__(self, model, ident): + super(StoragePool, self).__init__(model, ident) + self.update_params = ["autostart"] + self.uri_fmt = "/storagepools/%s" + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate') + + @property + def data(self): + res = {'name': self.ident, + 'state': self.info['state'], + 'capacity': self.info['capacity'], + 'allocated': self.info['allocated'], + 'available': self.info['available'], + 'path': self.info['path'], + 'source': self.info['source'], + 'type': self.info['type'], + 'nr_volumes': self.info['nr_volumes'], + 'autostart': self.info['autostart']} + + val = self.info.get('task_id') + if val: + res['task_id'] = val + + return res + + def _cp_dispatch(self, vpath): + if vpath: + subcollection = vpath.pop(0) + if subcollection == 'storagevolumes': + # incoming text, from URL, is not unicode, need decode + return StorageVolumes(self.model, self.ident.decode("utf-8")) + + +class IsoPool(Resource): + def __init__(self, model): + super(IsoPool, self).__init__(model, ISO_POOL_NAME) + + @property + def data(self): + return {'name': self.ident, + 'state': self.info['state'], + 'type': self.info['type']} + + def _cp_dispatch(self, vpath): + if vpath: + subcollection = vpath.pop(0) + if subcollection == 'storagevolumes': + # incoming text, from URL, is not unicode, need decode + return IsoVolumes(self.model, self.ident.decode("utf-8")) -- 1.7.10.4

On 12/23/2013 04:41 PM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
StoragePools(Collection), StoragePool(Resource) and IsoPool(Resource) were moved to a new - control/storagepools.py That way we can easily know where storage pool resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/storagepools.py | 125 ++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/kimchi/control/storagepools.py
diff --git a/Makefile.am b/Makefile.am index a16cd2a..f67af3d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ + src/kimchi/control/storagepools.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py new file mode 100644 index 0000000..f4fffd7 --- /dev/null +++ b/src/kimchi/control/storagepools.py @@ -0,0 +1,125 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import cherrypy + + +from kimchi.control.base import Collection, Resource +from kimchi.control.storagevolumes import IsoVolumes, StorageVolumes +from kimchi.control.utils import get_class_name, model_fn, parse_request +from kimchi.model import ISO_POOL_NAME + + +class StoragePools(Collection): + def __init__(self, model): + super(StoragePools, self).__init__(model) + self.resource = StoragePool + isos = IsoPool(model) + isos.exposed = True + setattr(self, ISO_POOL_NAME, isos) + + def create(self, *args): + try: + create = getattr(self.model, model_fn(self, 'create')) + except AttributeError: + raise cherrypy.HTTPError(405, + 'Create is not allowed for %s' % get_class_name(self)) + + params = parse_request() + args = self.model_args + [params] + name = create(*args) + args = self.resource_args + [name] + res = self.resource(self.model, *args) + resp = res.get() + + if 'task_id' in res.data: + cherrypy.response.status = 202 + else: + cherrypy.response.status = 201 + + return resp + + def _get_resources(self): + try: + res_list = super(StoragePools, self)._get_resources() + # Append reserved pools + isos = getattr(self, ISO_POOL_NAME) + isos.lookup() + res_list.append(isos) + except AttributeError: + pass + + return res_list + + +class StoragePool(Resource): + def __init__(self, model, ident): + super(StoragePool, self).__init__(model, ident) + self.update_params = ["autostart"] + self.uri_fmt = "/storagepools/%s" make /storagepools/%s as a constant. + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate') + + @property + def data(self): + res = {'name': self.ident, + 'state': self.info['state'], + 'capacity': self.info['capacity'], + 'allocated': self.info['allocated'], + 'available': self.info['available'], + 'path': self.info['path'], + 'source': self.info['source'], + 'type': self.info['type'], + 'nr_volumes': self.info['nr_volumes'], + 'autostart': self.info['autostart']} + + val = self.info.get('task_id') + if val: + res['task_id'] = val + + return res + + def _cp_dispatch(self, vpath): + if vpath: + subcollection = vpath.pop(0) + if subcollection == 'storagevolumes': + # incoming text, from URL, is not unicode, need decode + return StorageVolumes(self.model, self.ident.decode("utf-8")) + + +class IsoPool(Resource): + def __init__(self, model): + super(IsoPool, self).__init__(model, ISO_POOL_NAME) + + @property + def data(self): + return {'name': self.ident, + 'state': self.info['state'], + 'type': self.info['type']} + + def _cp_dispatch(self, vpath): + if vpath: + subcollection = vpath.pop(0) + if subcollection == 'storagevolumes': + # incoming text, from URL, is not unicode, need decode + return IsoVolumes(self.model, self.ident.decode("utf-8"))
-- Ramon Nunes Medeiros Software Engineer - Linux Technology Center Brazil IBM Systems & Technology Group Phone : +55 19 2132 7878 ramonn@br.ibm.com

On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
StoragePools(Collection), StoragePool(Resource) and IsoPool(Resource) were moved to a new - control/storagepools.py That way we can easily know where storage pool resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/storagepools.py | 125 ++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/kimchi/control/storagepools.py
diff --git a/Makefile.am b/Makefile.am index a16cd2a..f67af3d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ + src/kimchi/control/storagepools.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py new file mode 100644 index 0000000..f4fffd7 --- /dev/null +++ b/src/kimchi/control/storagepools.py @@ -0,0 +1,125 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import cherrypy + + +from kimchi.control.base import Collection, Resource +from kimchi.control.storagevolumes import IsoVolumes, StorageVolumes +from kimchi.control.utils import get_class_name, model_fn, parse_request +from kimchi.model import ISO_POOL_NAME + + +class StoragePools(Collection): + def __init__(self, model): + super(StoragePools, self).__init__(model) + self.resource = StoragePool + isos = IsoPool(model) + isos.exposed = True + setattr(self, ISO_POOL_NAME, isos) + + def create(self, *args): + try: + create = getattr(self.model, model_fn(self, 'create')) + except AttributeError: + raise cherrypy.HTTPError(405, + 'Create is not allowed for %s' % get_class_name(self)) + + params = parse_request() + args = self.model_args + [params] + name = create(*args) + args = self.resource_args + [name] + res = self.resource(self.model, *args) + resp = res.get() + + if 'task_id' in res.data: + cherrypy.response.status = 202 + else: + cherrypy.response.status = 201 + + return resp + + def _get_resources(self): + try: + res_list = super(StoragePools, self)._get_resources() + # Append reserved pools + isos = getattr(self, ISO_POOL_NAME) + isos.lookup() + res_list.append(isos) + except AttributeError: + pass + + return res_list + + +class StoragePool(Resource): + def __init__(self, model, ident): + super(StoragePool, self).__init__(model, ident) + self.update_params = ["autostart"] + self.uri_fmt = "/storagepools/%s" + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate') + + @property + def data(self): + res = {'name': self.ident, + 'state': self.info['state'], + 'capacity': self.info['capacity'], + 'allocated': self.info['allocated'], + 'available': self.info['available'], + 'path': self.info['path'], + 'source': self.info['source'], + 'type': self.info['type'], + 'nr_volumes': self.info['nr_volumes'], + 'autostart': self.info['autostart']} + + val = self.info.get('task_id') + if val: + res['task_id'] = val + + return res + + def _cp_dispatch(self, vpath): + if vpath: + subcollection = vpath.pop(0) + if subcollection == 'storagevolumes': + # incoming text, from URL, is not unicode, need decode + return StorageVolumes(self.model, self.ident.decode("utf-8")) + + +class IsoPool(Resource): + def __init__(self, model): + super(IsoPool, self).__init__(model, ISO_POOL_NAME) + + @property + def data(self): + return {'name': self.ident, + 'state': self.info['state'], + 'type': self.info['type']} + + def _cp_dispatch(self, vpath): + if vpath: + subcollection = vpath.pop(0) + if subcollection == 'storagevolumes': + # incoming text, from URL, is not unicode, need decode + return IsoVolumes(self.model, self.ident.decode("utf-8")) PEP8 error: src/kimchi/control/base.py:285:17: E128 continuation line under-indented for visual indent

On 12/24/2013 04:18 AM, Mark Wu wrote:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
StoragePools(Collection), StoragePool(Resource) and IsoPool(Resource) were moved to a new - control/storagepools.py That way we can easily know where storage pool resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/storagepools.py | 125 ++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/kimchi/control/storagepools.py
diff --git a/Makefile.am b/Makefile.am index a16cd2a..f67af3d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ + src/kimchi/control/storagepools.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py new file mode 100644 index 0000000..f4fffd7 --- /dev/null +++ b/src/kimchi/control/storagepools.py @@ -0,0 +1,125 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import cherrypy + + +from kimchi.control.base import Collection, Resource +from kimchi.control.storagevolumes import IsoVolumes, StorageVolumes +from kimchi.control.utils import get_class_name, model_fn, parse_request +from kimchi.model import ISO_POOL_NAME + + +class StoragePools(Collection): + def __init__(self, model): + super(StoragePools, self).__init__(model) + self.resource = StoragePool + isos = IsoPool(model) + isos.exposed = True + setattr(self, ISO_POOL_NAME, isos) + + def create(self, *args): + try: + create = getattr(self.model, model_fn(self, 'create')) + except AttributeError: + raise cherrypy.HTTPError(405, + 'Create is not allowed for %s' % get_class_name(self)) + + params = parse_request() + args = self.model_args + [params] + name = create(*args) + args = self.resource_args + [name] + res = self.resource(self.model, *args) + resp = res.get() + + if 'task_id' in res.data: + cherrypy.response.status = 202 + else: + cherrypy.response.status = 201 + + return resp + + def _get_resources(self): + try: + res_list = super(StoragePools, self)._get_resources() + # Append reserved pools + isos = getattr(self, ISO_POOL_NAME) + isos.lookup() + res_list.append(isos) + except AttributeError: + pass + + return res_list + + +class StoragePool(Resource): + def __init__(self, model, ident): + super(StoragePool, self).__init__(model, ident) + self.update_params = ["autostart"] + self.uri_fmt = "/storagepools/%s" + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate') + + @property + def data(self): + res = {'name': self.ident, + 'state': self.info['state'], + 'capacity': self.info['capacity'], + 'allocated': self.info['allocated'], + 'available': self.info['available'], + 'path': self.info['path'], + 'source': self.info['source'], + 'type': self.info['type'], + 'nr_volumes': self.info['nr_volumes'], + 'autostart': self.info['autostart']} + + val = self.info.get('task_id') + if val: + res['task_id'] = val + + return res + + def _cp_dispatch(self, vpath): + if vpath: + subcollection = vpath.pop(0) + if subcollection == 'storagevolumes': + # incoming text, from URL, is not unicode, need decode + return StorageVolumes(self.model, self.ident.decode("utf-8")) + + +class IsoPool(Resource): + def __init__(self, model): + super(IsoPool, self).__init__(model, ISO_POOL_NAME) + + @property + def data(self): + return {'name': self.ident, + 'state': self.info['state'], + 'type': self.info['type']} + + def _cp_dispatch(self, vpath): + if vpath: + subcollection = vpath.pop(0) + if subcollection == 'storagevolumes': + # incoming text, from URL, is not unicode, need decode + return IsoVolumes(self.model, self.ident.decode("utf-8")) PEP8 error: src/kimchi/control/base.py:285:17: E128 continuation line under-indented for visual indent
Same as I commented previously. I didn't get this error. alinefm@alinefm:~/kimchi$ sudo make check-local /usr/bin/pep8 --version 1.2 /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

From: Aline Manera <alinefm@br.ibm.com>
StoragePools(Collection), StoragePool(Resource) and IsoPool(Resource) were moved to a new - control/storagepools.py That way we can easily know where storage pool resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/storagepools.py | 125 ++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/kimchi/control/storagepools.py
diff --git a/Makefile.am b/Makefile.am index a16cd2a..f67af3d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ + src/kimchi/control/storagepools.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py new file mode 100644 index 0000000..f4fffd7 --- /dev/null +++ b/src/kimchi/control/storagepools.py @@ -0,0 +1,125 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> $ git show 44ff8295 $ git show 451c006c
On 12/24/2013 02:41 AM, Aline Manera wrote: the authors are: Bing Bu Cao <mars@linux.vnet.ibm.com> Adam Litke <agl@linux.vnet.ibm.com> Royce Lv <lvroyce@linux.vnet.ibm.com>
+# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import cherrypy + + +from kimchi.control.base import Collection, Resource +from kimchi.control.storagevolumes import IsoVolumes, StorageVolumes +from kimchi.control.utils import get_class_name, model_fn, parse_request +from kimchi.model import ISO_POOL_NAME + + +class StoragePools(Collection): + def __init__(self, model): + super(StoragePools, self).__init__(model) + self.resource = StoragePool + isos = IsoPool(model) + isos.exposed = True + setattr(self, ISO_POOL_NAME, isos) + + def create(self, *args): + try: + create = getattr(self.model, model_fn(self, 'create')) + except AttributeError: + raise cherrypy.HTTPError(405, + 'Create is not allowed for %s' % get_class_name(self)) + + params = parse_request() + args = self.model_args + [params] + name = create(*args) + args = self.resource_args + [name] + res = self.resource(self.model, *args) + resp = res.get() + + if 'task_id' in res.data: + cherrypy.response.status = 202 + else: + cherrypy.response.status = 201 + + return resp + + def _get_resources(self): + try: + res_list = super(StoragePools, self)._get_resources() + # Append reserved pools + isos = getattr(self, ISO_POOL_NAME) + isos.lookup() + res_list.append(isos) + except AttributeError: + pass + + return res_list + + +class StoragePool(Resource): + def __init__(self, model, ident): + super(StoragePool, self).__init__(model, ident) + self.update_params = ["autostart"] + self.uri_fmt = "/storagepools/%s" + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate') + + @property + def data(self): + res = {'name': self.ident, + 'state': self.info['state'], + 'capacity': self.info['capacity'], + 'allocated': self.info['allocated'], + 'available': self.info['available'], + 'path': self.info['path'], + 'source': self.info['source'], + 'type': self.info['type'], + 'nr_volumes': self.info['nr_volumes'], + 'autostart': self.info['autostart']} + + val = self.info.get('task_id') + if val: + res['task_id'] = val + + return res + + def _cp_dispatch(self, vpath): + if vpath: + subcollection = vpath.pop(0) + if subcollection == 'storagevolumes': + # incoming text, from URL, is not unicode, need decode + return StorageVolumes(self.model, self.ident.decode("utf-8")) + + +class IsoPool(Resource): + def __init__(self, model): + super(IsoPool, self).__init__(model, ISO_POOL_NAME) + + @property + def data(self): + return {'name': self.ident, + 'state': self.info['state'], + 'type': self.info['type']} + + def _cp_dispatch(self, vpath): + if vpath: + subcollection = vpath.pop(0) + if subcollection == 'storagevolumes': + # incoming text, from URL, is not unicode, need decode + return IsoVolumes(self.model, self.ident.decode("utf-8"))
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

From: Aline Manera <alinefm@br.ibm.com> StorageVolumes(Collection), StorageVolume(Resource) and IsoVolumes(Collection) were moved to a new - control/storagevolumes.py That way we can easily know where storage volume resource is implemented. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/storagevolumes.py | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/kimchi/control/storagevolumes.py diff --git a/Makefile.am b/Makefile.am index f67af3d..c9a1faf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ src/kimchi/control/storagepools.py \ + src/kimchi/control/storagevolumes.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py new file mode 100644 index 0000000..14f78a3 --- /dev/null +++ b/src/kimchi/control/storagevolumes.py @@ -0,0 +1,79 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import kimchi.template +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn + + +class StorageVolumes(Collection): + def __init__(self, model, pool): + super(StorageVolumes, self).__init__(model) + self.resource = StorageVolume + self.pool = pool + self.resource_args = [self.pool, ] + self.model_args = [self.pool, ] + + +class StorageVolume(Resource): + def __init__(self, model, pool, ident): + super(StorageVolume, self).__init__(model, ident) + self.pool = pool + self.ident = ident + self.info = {} + self.model_args = [self.pool, self.ident] + self.uri_fmt = '/storagepools/%s/storagevolumes/%s' + self.resize = self.generate_action_handler(self, 'resize', ['size']) + self.wipe = self.generate_action_handler(self, 'wipe') + + @property + def data(self): + res = {'name': self.ident, + 'type': self.info['type'], + 'capacity': self.info['capacity'], + 'allocation': self.info['allocation'], + 'path': self.info['path'], + 'format': self.info['format']} + + for key in ('os_version', 'os_distro', 'bootable'): + val = self.info.get(key) + if val: + res[key] = val + + return res + + +class IsoVolumes(Collection): + def __init__(self, model, pool): + super(IsoVolumes, self).__init__(model) + self.pool = pool + + def get(self): + res_list = [] + try: + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args) + except AttributeError: + pass + + return kimchi.template.render(get_class_name(self), res_list) -- 1.7.10.4

On 12/23/2013 04:41 PM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
StorageVolumes(Collection), StorageVolume(Resource) and IsoVolumes(Collection) were moved to a new - control/storagevolumes.py That way we can easily know where storage volume resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/storagevolumes.py | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/kimchi/control/storagevolumes.py
diff --git a/Makefile.am b/Makefile.am index f67af3d..c9a1faf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ src/kimchi/control/storagepools.py \ + src/kimchi/control/storagevolumes.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py new file mode 100644 index 0000000..14f78a3 --- /dev/null +++ b/src/kimchi/control/storagevolumes.py @@ -0,0 +1,79 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import kimchi.template +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn + + +class StorageVolumes(Collection): + def __init__(self, model, pool): + super(StorageVolumes, self).__init__(model) + self.resource = StorageVolume + self.pool = pool + self.resource_args = [self.pool, ] + self.model_args = [self.pool, ] + + +class StorageVolume(Resource): + def __init__(self, model, pool, ident): + super(StorageVolume, self).__init__(model, ident) + self.pool = pool + self.ident = ident + self.info = {} + self.model_args = [self.pool, self.ident] + self.uri_fmt = '/storagepools/%s/storagevolumes/%s' constant! + self.resize = self.generate_action_handler(self, 'resize', ['size']) + self.wipe = self.generate_action_handler(self, 'wipe') + + @property + def data(self): + res = {'name': self.ident, + 'type': self.info['type'], + 'capacity': self.info['capacity'], + 'allocation': self.info['allocation'], + 'path': self.info['path'], + 'format': self.info['format']} + + for key in ('os_version', 'os_distro', 'bootable'): + val = self.info.get(key) + if val: + res[key] = val + + return res + + +class IsoVolumes(Collection): + def __init__(self, model, pool): + super(IsoVolumes, self).__init__(model) + self.pool = pool + + def get(self): + res_list = [] + try: + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args) + except AttributeError: + pass + + return kimchi.template.render(get_class_name(self), res_list)
-- Ramon Nunes Medeiros Software Engineer - Linux Technology Center Brazil IBM Systems & Technology Group Phone : +55 19 2132 7878 ramonn@br.ibm.com

On 12/23/2013 04:41 PM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
StorageVolumes(Collection), StorageVolume(Resource) and IsoVolumes(Collection) were moved to a new - control/storagevolumes.py That way we can easily know where storage volume resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/storagevolumes.py | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/kimchi/control/storagevolumes.py
diff --git a/Makefile.am b/Makefile.am index f67af3d..c9a1faf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ src/kimchi/control/storagepools.py \ + src/kimchi/control/storagevolumes.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py new file mode 100644 index 0000000..14f78a3 --- /dev/null +++ b/src/kimchi/control/storagevolumes.py @@ -0,0 +1,79 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> $ git show 44ff8295 $ git show 451c006c
On 12/24/2013 04:02 AM, Ramon Medeiros wrote: the authors are: Bing Bu Cao <mars@linux.vnet.ibm.com> Adam Litke <agl@linux.vnet.ibm.com> Royce Lv <lvroyce@linux.vnet.ibm.com>
+# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import kimchi.template +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn + + +class StorageVolumes(Collection): + def __init__(self, model, pool): + super(StorageVolumes, self).__init__(model) + self.resource = StorageVolume + self.pool = pool + self.resource_args = [self.pool, ] + self.model_args = [self.pool, ] + + +class StorageVolume(Resource): + def __init__(self, model, pool, ident): + super(StorageVolume, self).__init__(model, ident) + self.pool = pool + self.ident = ident + self.info = {} + self.model_args = [self.pool, self.ident] + self.uri_fmt = '/storagepools/%s/storagevolumes/%s' constant! + self.resize = self.generate_action_handler(self, 'resize', ['size']) + self.wipe = self.generate_action_handler(self, 'wipe') + + @property + def data(self): + res = {'name': self.ident, + 'type': self.info['type'], + 'capacity': self.info['capacity'], + 'allocation': self.info['allocation'], + 'path': self.info['path'], + 'format': self.info['format']} + + for key in ('os_version', 'os_distro', 'bootable'): + val = self.info.get(key) + if val: + res[key] = val + + return res + + +class IsoVolumes(Collection): + def __init__(self, model, pool): + super(IsoVolumes, self).__init__(model) + self.pool = pool + + def get(self): + res_list = [] + try: + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args) + except AttributeError: + pass + + return kimchi.template.render(get_class_name(self), res_list)
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

From: Aline Manera <alinefm@br.ibm.com>
StorageVolumes(Collection), StorageVolume(Resource) and IsoVolumes(Collection) were moved to a new - control/storagevolumes.py That way we can easily know where storage volume resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/storagevolumes.py | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/kimchi/control/storagevolumes.py
diff --git a/Makefile.am b/Makefile.am index f67af3d..c9a1faf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ src/kimchi/control/storagepools.py \ + src/kimchi/control/storagevolumes.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py new file mode 100644 index 0000000..14f78a3 --- /dev/null +++ b/src/kimchi/control/storagevolumes.py @@ -0,0 +1,79 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> $ git show 44ff8295 $ git show 451c006c
On 12/24/2013 02:41 AM, Aline Manera wrote: the authors are: Bing Bu Cao <mars@linux.vnet.ibm.com> Adam Litke <agl@linux.vnet.ibm.com> Royce Lv <lvroyce@linux.vnet.ibm.com>
+# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import kimchi.template +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn + + +class StorageVolumes(Collection): + def __init__(self, model, pool): + super(StorageVolumes, self).__init__(model) + self.resource = StorageVolume + self.pool = pool + self.resource_args = [self.pool, ] + self.model_args = [self.pool, ] + + +class StorageVolume(Resource): + def __init__(self, model, pool, ident): + super(StorageVolume, self).__init__(model, ident) + self.pool = pool + self.ident = ident + self.info = {} + self.model_args = [self.pool, self.ident] + self.uri_fmt = '/storagepools/%s/storagevolumes/%s' + self.resize = self.generate_action_handler(self, 'resize', ['size']) + self.wipe = self.generate_action_handler(self, 'wipe') + + @property + def data(self): + res = {'name': self.ident, + 'type': self.info['type'], + 'capacity': self.info['capacity'], + 'allocation': self.info['allocation'], + 'path': self.info['path'], + 'format': self.info['format']} + + for key in ('os_version', 'os_distro', 'bootable'): + val = self.info.get(key) + if val: + res[key] = val + + return res + + +class IsoVolumes(Collection): + def __init__(self, model, pool): + super(IsoVolumes, self).__init__(model) + self.pool = pool + + def get(self): + res_list = [] + try: + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args) + except AttributeError: + pass + + return kimchi.template.render(get_class_name(self), res_list)
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 12/24/2013 09:00 AM, Sheldon wrote:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
StorageVolumes(Collection), StorageVolume(Resource) and IsoVolumes(Collection) were moved to a new - control/storagevolumes.py That way we can easily know where storage volume resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/storagevolumes.py | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/kimchi/control/storagevolumes.py
diff --git a/Makefile.am b/Makefile.am index f67af3d..c9a1faf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ src/kimchi/control/storagepools.py \ + src/kimchi/control/storagevolumes.py \ src/kimchi/control/templates.py \ src/kimchi/control/utils.py \ src/kimchi/control/vms.py \ diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py new file mode 100644 index 0000000..14f78a3 --- /dev/null +++ b/src/kimchi/control/storagevolumes.py @@ -0,0 +1,79 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com>
$ git show 44ff8295 $ git show 451c006c the authors are: Bing Bu Cao <mars@linux.vnet.ibm.com> Adam Litke <agl@linux.vnet.ibm.com> Royce Lv <lvroyce@linux.vnet.ibm.com>
ACK
+# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import kimchi.template +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn + + +class StorageVolumes(Collection): + def __init__(self, model, pool): + super(StorageVolumes, self).__init__(model) + self.resource = StorageVolume + self.pool = pool + self.resource_args = [self.pool, ] + self.model_args = [self.pool, ] + + +class StorageVolume(Resource): + def __init__(self, model, pool, ident): + super(StorageVolume, self).__init__(model, ident) + self.pool = pool + self.ident = ident + self.info = {} + self.model_args = [self.pool, self.ident] + self.uri_fmt = '/storagepools/%s/storagevolumes/%s' + self.resize = self.generate_action_handler(self, 'resize', ['size']) + self.wipe = self.generate_action_handler(self, 'wipe') + + @property + def data(self): + res = {'name': self.ident, + 'type': self.info['type'], + 'capacity': self.info['capacity'], + 'allocation': self.info['allocation'], + 'path': self.info['path'], + 'format': self.info['format']} + + for key in ('os_version', 'os_distro', 'bootable'): + val = self.info.get(key) + if val: + res[key] = val + + return res + + +class IsoVolumes(Collection): + def __init__(self, model, pool): + super(IsoVolumes, self).__init__(model) + self.pool = pool + + def get(self): + res_list = [] + try: + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args) + except AttributeError: + pass + + return kimchi.template.render(get_class_name(self), res_list)

From: Aline Manera <alinefm@br.ibm.com> Interfaces(Collection) and Interface(Resource) were moved to a new - control/interfaces.py That way we can easily know where interface resource is implemented. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/interfaces.py | 44 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/kimchi/control/interfaces.py diff --git a/Makefile.am b/Makefile.am index c9a1faf..76953ea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ + src/kimchi/control/interfaces.py \ src/kimchi/control/storagepools.py \ src/kimchi/control/storagevolumes.py \ src/kimchi/control/templates.py \ diff --git a/src/kimchi/control/interfaces.py b/src/kimchi/control/interfaces.py new file mode 100644 index 0000000..33afea2 --- /dev/null +++ b/src/kimchi/control/interfaces.py @@ -0,0 +1,44 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Interfaces(Collection): + def __init__(self, model): + super(Interfaces, self).__init__(model) + self.resource = Interface + + +class Interface(Resource): + def __init__(self, model, ident): + super(Interface, self).__init__(model, ident) + self.uri_fmt = "/interfaces/%s" + + @property + def data(self): + return {'name': self.ident, + 'type': self.info['type'], + 'ipaddr': self.info['ipaddr'], + 'netmask': self.info['netmask'], + 'status': self.info['status']} -- 1.7.10.4

On 12/23/2013 04:41 PM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Interfaces(Collection) and Interface(Resource) were moved to a new - control/interfaces.py That way we can easily know where interface resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/interfaces.py | 44 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/kimchi/control/interfaces.py
diff --git a/Makefile.am b/Makefile.am index c9a1faf..76953ea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ PEP8_WHITELIST = \ src/kimchi/server.py \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ + src/kimchi/control/interfaces.py \ src/kimchi/control/storagepools.py \ src/kimchi/control/storagevolumes.py \ src/kimchi/control/templates.py \ diff --git a/src/kimchi/control/interfaces.py b/src/kimchi/control/interfaces.py new file mode 100644 index 0000000..33afea2 --- /dev/null +++ b/src/kimchi/control/interfaces.py @@ -0,0 +1,44 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Interfaces(Collection): + def __init__(self, model): + super(Interfaces, self).__init__(model) + self.resource = Interface + + +class Interface(Resource): + def __init__(self, model, ident): + super(Interface, self).__init__(model, ident) + self.uri_fmt = "/interfaces/%s" make a constant! + + @property + def data(self): + return {'name': self.ident, + 'type': self.info['type'], + 'ipaddr': self.info['ipaddr'], + 'netmask': self.info['netmask'], + 'status': self.info['status']}
-- Ramon Nunes Medeiros Software Engineer - Linux Technology Center Brazil IBM Systems & Technology Group Phone : +55 19 2132 7878 ramonn@br.ibm.com

From: Aline Manera <alinefm@br.ibm.com> Networks(Collection) and Network(Resource) were moved to a new - control/networks.py That way we can easily know where network resource is implemented. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/networks.py | 48 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/kimchi/control/networks.py diff --git a/Makefile.am b/Makefile.am index 76953ea..988702e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ src/kimchi/control/interfaces.py \ + src/kimchi/control/networks.py \ src/kimchi/control/storagepools.py \ src/kimchi/control/storagevolumes.py \ src/kimchi/control/templates.py \ diff --git a/src/kimchi/control/networks.py b/src/kimchi/control/networks.py new file mode 100644 index 0000000..f7df5bd --- /dev/null +++ b/src/kimchi/control/networks.py @@ -0,0 +1,48 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Networks(Collection): + def __init__(self, model): + super(Networks, self).__init__(model) + self.resource = Network + + +class Network(Resource): + def __init__(self, model, ident): + super(Network, self).__init__(model, ident) + self.uri_fmt = "/networks/%s" + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate') + + @property + def data(self): + return {'name': self.ident, + 'autostart': self.info['autostart'], + 'connection': self.info['connection'], + 'interface': self.info['interface'], + 'subnet': self.info['subnet'], + 'dhcp': self.info['dhcp'], + 'state': self.info['state']} -- 1.7.10.4

On 12/23/2013 04:41 PM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Networks(Collection) and Network(Resource) were moved to a new - control/networks.py That way we can easily know where network resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/networks.py | 48 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/kimchi/control/networks.py
diff --git a/Makefile.am b/Makefile.am index 76953ea..988702e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ src/kimchi/control/interfaces.py \ + src/kimchi/control/networks.py \ src/kimchi/control/storagepools.py \ src/kimchi/control/storagevolumes.py \ src/kimchi/control/templates.py \ diff --git a/src/kimchi/control/networks.py b/src/kimchi/control/networks.py new file mode 100644 index 0000000..f7df5bd --- /dev/null +++ b/src/kimchi/control/networks.py @@ -0,0 +1,48 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Networks(Collection): + def __init__(self, model): + super(Networks, self).__init__(model) + self.resource = Network + + +class Network(Resource): + def __init__(self, model, ident): + super(Network, self).__init__(model, ident) + self.uri_fmt = "/networks/%s" constant + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate') + + @property + def data(self): + return {'name': self.ident, + 'autostart': self.info['autostart'], + 'connection': self.info['connection'], + 'interface': self.info['interface'], + 'subnet': self.info['subnet'], + 'dhcp': self.info['dhcp'], + 'state': self.info['state']}
-- Ramon Nunes Medeiros Software Engineer - Linux Technology Center Brazil IBM Systems & Technology Group Phone : +55 19 2132 7878 ramonn@br.ibm.com

On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Networks(Collection) and Network(Resource) were moved to a new - control/networks.py That way we can easily know where network resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/networks.py | 48 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/kimchi/control/networks.py
diff --git a/Makefile.am b/Makefile.am index 76953ea..988702e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ src/kimchi/control/interfaces.py \ + src/kimchi/control/networks.py \ src/kimchi/control/storagepools.py \ src/kimchi/control/storagevolumes.py \ src/kimchi/control/templates.py \ diff --git a/src/kimchi/control/networks.py b/src/kimchi/control/networks.py new file mode 100644 index 0000000..f7df5bd --- /dev/null +++ b/src/kimchi/control/networks.py @@ -0,0 +1,48 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> $ git show 26ee2089 author is: ShaoHe Feng <shaohef@linux.vnet.ibm.com> not: Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Networks(Collection): + def __init__(self, model): + super(Networks, self).__init__(model) + self.resource = Network + + +class Network(Resource): + def __init__(self, model, ident): + super(Network, self).__init__(model, ident) + self.uri_fmt = "/networks/%s" + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate') + + @property + def data(self): + return {'name': self.ident, + 'autostart': self.info['autostart'], + 'connection': self.info['connection'], + 'interface': self.info['interface'], + 'subnet': self.info['subnet'], + 'dhcp': self.info['dhcp'], + 'state': self.info['state']}
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 12/24/2013 08:55 AM, Sheldon wrote:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Networks(Collection) and Network(Resource) were moved to a new - control/networks.py That way we can easily know where network resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/networks.py | 48 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/kimchi/control/networks.py
diff --git a/Makefile.am b/Makefile.am index 76953ea..988702e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ src/kimchi/control/base.py \ src/kimchi/control/debugreports.py \ src/kimchi/control/interfaces.py \ + src/kimchi/control/networks.py \ src/kimchi/control/storagepools.py \ src/kimchi/control/storagevolumes.py \ src/kimchi/control/templates.py \ diff --git a/src/kimchi/control/networks.py b/src/kimchi/control/networks.py new file mode 100644 index 0000000..f7df5bd --- /dev/null +++ b/src/kimchi/control/networks.py @@ -0,0 +1,48 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com>
$ git show 26ee2089 author is: ShaoHe Feng <shaohef@linux.vnet.ibm.com> not: Adam Litke <agl@linux.vnet.ibm.com>
ACK
+# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Networks(Collection): + def __init__(self, model): + super(Networks, self).__init__(model) + self.resource = Network + + +class Network(Resource): + def __init__(self, model, ident): + super(Network, self).__init__(model, ident) + self.uri_fmt = "/networks/%s" + self.activate = self.generate_action_handler(self, 'activate') + self.deactivate = self.generate_action_handler(self, 'deactivate') + + @property + def data(self): + return {'name': self.ident, + 'autostart': self.info['autostart'], + 'connection': self.info['connection'], + 'interface': self.info['interface'], + 'subnet': self.info['subnet'], + 'dhcp': self.info['dhcp'], + 'state': self.info['state']}

From: Aline Manera <alinefm@br.ibm.com> Config(Resource), Capabilities(Resource), Distros(Collection) and Distro(Resource) were moved to a new - control/config.py That way we can easily know where config resource is implemented. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/config.py | 65 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/kimchi/control/config.py diff --git a/Makefile.am b/Makefile.am index 988702e..5ae93cd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,7 @@ PEP8_WHITELIST = \ 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/interfaces.py \ src/kimchi/control/networks.py \ diff --git a/src/kimchi/control/config.py b/src/kimchi/control/config.py new file mode 100644 index 0000000..df4d89b --- /dev/null +++ b/src/kimchi/control/config.py @@ -0,0 +1,65 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Config(Resource): + def __init__(self, model, id=None): + super(Config, self).__init__(model, id) + self.capabilities = Capabilities(self.model) + self.capabilities.exposed = True + self.distros = Distros(model) + self.distros.exposed = True + + @property + def data(self): + return {'http_port': cherrypy.server.socket_port} + + +class Capabilities(Resource): + def __init__(self, model, id=None): + super(Capabilities, self).__init__(model, id) + + @property + def data(self): + caps = ['libvirt_stream_protocols', 'qemu_stream', + 'screenshot', 'system_report_tool'] + ret = dict([(x, None) for x in caps]) + ret.update(self.model.get_capabilities()) + return ret + + +class Distros(Collection): + def __init__(self, model): + super(Distros, self).__init__(model) + self.resource = Distro + + +class Distro(Resource): + def __init__(self, model, ident): + super(Distro, self).__init__(model, ident) + + @property + def data(self): + return self.info -- 1.7.10.4

Reviewed-By: Ramon Medeiros <ramonn@br.ibm.com>

From: Aline Manera <alinefm@br.ibm.com> Host(Resource), HostStats(Resource), Partitions(Collection) and Partition(Resource) were moved to a new - control/host.py That way we can easily know where host resource is implemented. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/host.py | 61 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/kimchi/control/host.py diff --git a/Makefile.am b/Makefile.am index 5ae93cd..e942045 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ 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/storagepools.py \ diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py new file mode 100644 index 0000000..30e566e --- /dev/null +++ b/src/kimchi/control/host.py @@ -0,0 +1,61 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Host(Resource): + def __init__(self, model, id=None): + super(Host, self).__init__(model, id) + self.uri_fmt = '/host/%s' + self.reboot = self.generate_action_handler(self, 'reboot') + self.shutdown = self.generate_action_handler(self, 'shutdown') + self.stats = HostStats(self.model) + self.stats.exposed = True + self.partitions = Partitions(self.model) + self.partitions.exposed = True + + @property + def data(self): + return self.info + + +class HostStats(Resource): + @property + def data(self): + return self.info + + +class Partitions(Collection): + def __init__(self, model): + super(Partitions, self).__init__(model) + self.resource = Partition + + +class Partition(Resource): + def __init__(self, model, id): + super(Partition, self).__init__(model, id) + + @property + def data(self): + return self.info -- 1.7.10.4

On 12/23/2013 04:41 PM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Host(Resource), HostStats(Resource), Partitions(Collection) and Partition(Resource) were moved to a new - control/host.py That way we can easily know where host resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/host.py | 61 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/kimchi/control/host.py
diff --git a/Makefile.am b/Makefile.am index 5ae93cd..e942045 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ 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/storagepools.py \ diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py new file mode 100644 index 0000000..30e566e --- /dev/null +++ b/src/kimchi/control/host.py @@ -0,0 +1,61 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Host(Resource): + def __init__(self, model, id=None): + super(Host, self).__init__(model, id) + self.uri_fmt = '/host/%s' constant! + self.reboot = self.generate_action_handler(self, 'reboot') + self.shutdown = self.generate_action_handler(self, 'shutdown') + self.stats = HostStats(self.model) + self.stats.exposed = True + self.partitions = Partitions(self.model) + self.partitions.exposed = True + + @property + def data(self): + return self.info + + +class HostStats(Resource): + @property + def data(self): + return self.info + + +class Partitions(Collection): + def __init__(self, model): + super(Partitions, self).__init__(model) + self.resource = Partition + + +class Partition(Resource): + def __init__(self, model, id): + super(Partition, self).__init__(model, id) + + @property + def data(self): + return self.info
-- Ramon Nunes Medeiros Software Engineer - Linux Technology Center Brazil IBM Systems & Technology Group Phone : +55 19 2132 7878 ramonn@br.ibm.com

On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Host(Resource), HostStats(Resource), Partitions(Collection) and Partition(Resource) were moved to a new - control/host.py That way we can easily know where host resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/host.py | 61 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/kimchi/control/host.py
diff --git a/Makefile.am b/Makefile.am index 5ae93cd..e942045 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ PEP8_WHITELIST = \ 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/storagepools.py \ diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py new file mode 100644 index 0000000..30e566e --- /dev/null +++ b/src/kimchi/control/host.py @@ -0,0 +1,61 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> $ git show 1f558d70 $ git show 59a09764 The author are: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
+# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Host(Resource): + def __init__(self, model, id=None): + super(Host, self).__init__(model, id) + self.uri_fmt = '/host/%s' + self.reboot = self.generate_action_handler(self, 'reboot') + self.shutdown = self.generate_action_handler(self, 'shutdown') + self.stats = HostStats(self.model) + self.stats.exposed = True + self.partitions = Partitions(self.model) + self.partitions.exposed = True + + @property + def data(self): + return self.info + + +class HostStats(Resource): + @property + def data(self): + return self.info + + +class Partitions(Collection): + def __init__(self, model): + super(Partitions, self).__init__(model) + self.resource = Partition + + +class Partition(Resource): + def __init__(self, model, id): + super(Partition, self).__init__(model, id) + + @property + def data(self): + return self.info
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

From: Aline Manera <alinefm@br.ibm.com> Plugins(Collection) was moved to a new - control/plugins.py That way we can easily know where plugins resource is implemented. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/plugins.py | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/kimchi/control/plugins.py diff --git a/Makefile.am b/Makefile.am index e942045..7278a9b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,6 +51,7 @@ PEP8_WHITELIST = \ 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/templates.py \ diff --git a/src/kimchi/control/plugins.py b/src/kimchi/control/plugins.py new file mode 100644 index 0000000..c134935 --- /dev/null +++ b/src/kimchi/control/plugins.py @@ -0,0 +1,44 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import kimchi.template +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn + + +class Plugins(Collection): + def __init__(self, model): + super(Plugins, self).__init__(model) + + @property + def data(self): + return self.info + + def get(self): + res_list = [] + try: + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args) + except AttributeError: + pass + return kimchi.template.render(get_class_name(self), res_list) -- 1.7.10.4

Reviewed-By: Ramon Medeiros <ramonn@br.ibm.com>

From: Aline Manera <alinefm@br.ibm.com>
Plugins(Collection) was moved to a new - control/plugins.py That way we can easily know where plugins resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/plugins.py | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/kimchi/control/plugins.py
diff --git a/Makefile.am b/Makefile.am index e942045..7278a9b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,6 +51,7 @@ PEP8_WHITELIST = \ 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/templates.py \ diff --git a/src/kimchi/control/plugins.py b/src/kimchi/control/plugins.py new file mode 100644 index 0000000..c134935 --- /dev/null +++ b/src/kimchi/control/plugins.py @@ -0,0 +1,44 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# $ git show ebf366fd
On 12/24/2013 02:41 AM, Aline Manera wrote: the author is: zhoumeina <zhoumein@linux.vnet.ibm.com> ShaoHe Feng <shaohef@linux.vnet.ibm.com> not: Adam Litke <agl@linux.vnet.ibm.com>
+# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import kimchi.template +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn + + +class Plugins(Collection): + def __init__(self, model): + super(Plugins, self).__init__(model) + + @property + def data(self): + return self.info + + def get(self): + res_list = [] + try: + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args) + except AttributeError: + pass + return kimchi.template.render(get_class_name(self), res_list)
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 12/24/2013 08:47 AM, Sheldon wrote:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Plugins(Collection) was moved to a new - control/plugins.py That way we can easily know where plugins resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/plugins.py | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/kimchi/control/plugins.py
diff --git a/Makefile.am b/Makefile.am index e942045..7278a9b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,6 +51,7 @@ PEP8_WHITELIST = \ 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/templates.py \ diff --git a/src/kimchi/control/plugins.py b/src/kimchi/control/plugins.py new file mode 100644 index 0000000..c134935 --- /dev/null +++ b/src/kimchi/control/plugins.py @@ -0,0 +1,44 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +#
$ git show ebf366fd the author is: zhoumeina <zhoumein@linux.vnet.ibm.com> ShaoHe Feng <shaohef@linux.vnet.ibm.com> not:
Adam Litke <agl@linux.vnet.ibm.com>
I've just copied the authors from controller.py I will update that on V2.
+# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import kimchi.template +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn + + +class Plugins(Collection): + def __init__(self, model): + super(Plugins, self).__init__(model) + + @property + def data(self): + return self.info + + def get(self): + res_list = [] + try: + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args) + except AttributeError: + pass + return kimchi.template.render(get_class_name(self), res_list)

From: Aline Manera <alinefm@br.ibm.com> Tasks(Collection) and Task(Resource) were moved to a new - control/tasks.py That way we can easily know where task resource is implemented. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/tasks.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/kimchi/control/tasks.py diff --git a/Makefile.am b/Makefile.am index 7278a9b..d9daa63 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,6 +54,7 @@ PEP8_WHITELIST = \ 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 \ diff --git a/src/kimchi/control/tasks.py b/src/kimchi/control/tasks.py new file mode 100644 index 0000000..43f232e --- /dev/null +++ b/src/kimchi/control/tasks.py @@ -0,0 +1,41 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Tasks(Collection): + def __init__(self, model): + super(Tasks, self).__init__(model) + self.resource = Task + + +class Task(Resource): + def __init__(self, model, id): + super(Task, self).__init__(model, id) + + @property + def data(self): + return {'id': self.ident, + 'status': self.info['status'], + 'message': self.info['message']} -- 1.7.10.4

Reviewed-By: Ramon Medeiros <ramonn@br.ibm.com>

From: Aline Manera <alinefm@br.ibm.com>
Tasks(Collection) and Task(Resource) were moved to a new - control/tasks.py That way we can easily know where task resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/tasks.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/kimchi/control/tasks.py
diff --git a/Makefile.am b/Makefile.am index 7278a9b..d9daa63 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,6 +54,7 @@ PEP8_WHITELIST = \ 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 \ diff --git a/src/kimchi/control/tasks.py b/src/kimchi/control/tasks.py new file mode 100644 index 0000000..43f232e --- /dev/null +++ b/src/kimchi/control/tasks.py @@ -0,0 +1,41 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: $ git show 0968b5c9
On 12/24/2013 02:41 AM, Aline Manera wrote: the author is: Shu Ming <shuming@linux.vnet.ibm.com> Not Adam Litke <agl@linux.vnet.ibm.com>
+# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Tasks(Collection): + def __init__(self, model): + super(Tasks, self).__init__(model) + self.resource = Task + + +class Task(Resource): + def __init__(self, model, id): + super(Task, self).__init__(model, id) + + @property + def data(self): + return {'id': self.ident, + 'status': self.info['status'], + 'message': self.info['message']}
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 12/24/2013 08:43 AM, Sheldon wrote:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Tasks(Collection) and Task(Resource) were moved to a new - control/tasks.py That way we can easily know where task resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- Makefile.am | 1 + src/kimchi/control/tasks.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/kimchi/control/tasks.py
diff --git a/Makefile.am b/Makefile.am index 7278a9b..d9daa63 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,6 +54,7 @@ PEP8_WHITELIST = \ 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 \ diff --git a/src/kimchi/control/tasks.py b/src/kimchi/control/tasks.py new file mode 100644 index 0000000..43f232e --- /dev/null +++ b/src/kimchi/control/tasks.py @@ -0,0 +1,41 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors:
$ git show 0968b5c9 the author is: Shu Ming <shuming@linux.vnet.ibm.com> Not Adam Litke <agl@linux.vnet.ibm.com>
ACK
+# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Tasks(Collection): + def __init__(self, model): + super(Tasks, self).__init__(model) + self.resource = Task + + +class Task(Resource): + def __init__(self, model, id): + super(Task, self).__init__(model, id) + + @property + def data(self): + return {'id': self.ident, + 'status': self.info['status'], + 'message': self.info['message']}

2013/12/24 18:43, Sheldon:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Tasks(Collection) and Task(Resource) were moved to a new - control/tasks.py That way we can easily know where task resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com>
When I post the first patch of task series, there was such a file. But Adam.L required to remove it in the code review and place the code into controller.py. Can I know why we should split this again?
--- Makefile.am | 1 + src/kimchi/control/tasks.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/kimchi/control/tasks.py
diff --git a/Makefile.am b/Makefile.am index 7278a9b..d9daa63 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,6 +54,7 @@ PEP8_WHITELIST = \ 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 \ diff --git a/src/kimchi/control/tasks.py b/src/kimchi/control/tasks.py new file mode 100644 index 0000000..43f232e --- /dev/null +++ b/src/kimchi/control/tasks.py @@ -0,0 +1,41 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: $ git show 0968b5c9 the author is: Shu Ming <shuming@linux.vnet.ibm.com> Not Adam Litke <agl@linux.vnet.ibm.com>
Yes. That is me to do the initial work.
+# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Tasks(Collection): + def __init__(self, model): + super(Tasks, self).__init__(model) + self.resource = Task + + +class Task(Resource): + def __init__(self, model, id): + super(Task, self).__init__(model, id) + + @property + def data(self): + return {'id': self.ident, + 'status': self.info['status'], + 'message': self.info['message']}

On 12/26/2013 12:00 AM, Shu Ming wrote:
2013/12/24 18:43, Sheldon:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Tasks(Collection) and Task(Resource) were moved to a new - control/tasks.py That way we can easily know where task resource is implemented.
Signed-off-by: Aline Manera <alinefm@br.ibm.com> When I post the first patch of task series, there was such a file. But Adam.L required to remove it in the code review and place the code into controller.py. Can I know why we should split this again?
controller.py tends to increase to infinite as more resources are added to kimchi. Small modules are easier to maintain and most of them are already stable, ie, don't need to be changed with high frequency as controller.py was
--- Makefile.am | 1 + src/kimchi/control/tasks.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/kimchi/control/tasks.py
diff --git a/Makefile.am b/Makefile.am index 7278a9b..d9daa63 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,6 +54,7 @@ PEP8_WHITELIST = \ 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 \ diff --git a/src/kimchi/control/tasks.py b/src/kimchi/control/tasks.py new file mode 100644 index 0000000..43f232e --- /dev/null +++ b/src/kimchi/control/tasks.py @@ -0,0 +1,41 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: $ git show 0968b5c9 the author is: Shu Ming <shuming@linux.vnet.ibm.com> Not Adam Litke <agl@linux.vnet.ibm.com> Yes. That is me to do the initial work.
+# Adam Litke <agl@linux.vnet.ibm.com> +# Aline Manera <alinefm@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource + + +class Tasks(Collection): + def __init__(self, model): + super(Tasks, self).__init__(model) + self.resource = Task + + +class Task(Resource): + def __init__(self, model, id): + super(Task, self).__init__(model, id) + + @property + def data(self): + return {'id': self.ident, + 'status': self.info['status'], + 'message': self.info['message']}

From: Aline Manera <alinefm@br.ibm.com> controller.py was splitted into small modules to make maintenance easier That way each resource has its own module under /control Also delete former controller.py Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/controller.py | 667 ---------------------------------------------- src/kimchi/root.py | 40 +-- 2 files changed, 25 insertions(+), 682 deletions(-) delete mode 100644 src/kimchi/controller.py diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py deleted file mode 100644 index 5bcffff..0000000 --- a/src/kimchi/controller.py +++ /dev/null @@ -1,667 +0,0 @@ -# -# Project Kimchi -# -# Copyright IBM, Corp. 2013 -# -# Authors: -# Adam Litke <agl@linux.vnet.ibm.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -import cherrypy -import urllib2 - - -from functools import wraps - - -import kimchi.template -from kimchi.control.utils import get_class_name, internal_redirect, model_fn -from kimchi.control.utils import parse_request, validate_method, validate_params -from kimchi.exception import InvalidOperation, InvalidParameter, MissingParameter -from kimchi.exception import NotFoundError, OperationFailed -from kimchi.model import ISO_POOL_NAME - - -class Resource(object): - """ - A Resource represents a single entity in the API (such as a Virtual Machine) - - To create new Resource types, subclass this and change the following things - in the child class: - - - If the Resource requires more than one identifier set self.model_args as - appropriate. This should only be necessary if this Resource is logically - nested. For example: A Storage Volume belongs to a Storage Pool so the - Storage Volume would set model args to (pool_ident, volume_ident). - - - Implement the base operations of 'lookup' and 'delete' in the model(s). - - - Set the 'data' property to a JSON-serializable representation of the - Resource. - """ - def __init__(self, model, ident=None): - self.model = model - self.ident = ident - self.model_args = (ident,) - self.update_params = [] - - def generate_action_handler(self, instance, action_name, action_args=None): - def wrapper(*args, **kwargs): - validate_method(('POST')) - try: - model_args = list(instance.model_args) - if action_args is not None: - model_args.extend(parse_request()[key] for key in action_args) - fn = getattr(instance.model, model_fn(instance, action_name)) - fn(*model_args) - raise internal_redirect(instance.uri_fmt % - tuple(instance.model_args)) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - wrapper.__name__ = action_name - wrapper.exposed = True - return wrapper - - def lookup(self): - try: - lookup = getattr(self.model, model_fn(self, 'lookup')) - self.info = lookup(*self.model_args) - except AttributeError: - self.info = {} - - def delete(self): - try: - fn = getattr(self.model, model_fn(self, 'delete')) - fn(*self.model_args) - cherrypy.response.status = 204 - except AttributeError: - raise cherrypy.HTTPError(405, 'Delete is not allowed for %s' % get_class_name(self)) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - - @cherrypy.expose - def index(self): - method = validate_method(('GET', 'DELETE', 'PUT')) - if method == 'GET': - try: - return self.get() - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(406, "Operation failed: '%s'" % msg) - elif method == 'DELETE': - try: - return self.delete() - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - elif method == 'PUT': - try: - return self.update() - except InvalidParameter, msg: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % msg) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - def update(self): - try: - update = getattr(self.model, model_fn(self, 'update')) - except AttributeError: - raise cherrypy.HTTPError(405, "%s does not implement update " - "method" % get_class_name(self)) - params = parse_request() - validate_params(params, self, 'update') - if self.update_params != None: - invalids = [v for v in params.keys() if - v not in self.update_params] - if invalids: - raise cherrypy.HTTPError(405, "%s are not allowed to be updated" % - invalids) - ident = update(self.ident, params) - if ident != self.ident: - raise cherrypy.HTTPRedirect(self.uri_fmt % - tuple(list(self.model_args[:-1]) + [urllib2.quote(ident.encode('utf8'))]), - 303) - return self.get() - - - def get(self): - self.lookup() - return kimchi.template.render(get_class_name(self), self.data) - - @property - def data(self): - """ - Override this in inherited classes to provide the Resource - representation as a python dictionary. - """ - return {} - - -class Collection(object): - """ - A Collection is a container for Resource objects. To create a new - Collection type, subclass this and make the following changes to the child - class: - - - Set self.resource to the type of Resource that this Collection contains - - - Set self.resource_args. This can remain an empty list if the Resources - can be initialized with only one identifier. Otherwise, include - additional values as needed (eg. to identify a parent resource). - - - Set self.model_args. Similar to above, this is needed only if the model - needs additional information to identify this Collection. - - - Implement the base operations of 'create' and 'get_list' in the model. - """ - def __init__(self, model): - self.model = model - self.resource = Resource - self.resource_args = [] - self.model_args = [] - - def create(self, *args): - try: - create = getattr(self.model, model_fn(self, 'create')) - except AttributeError: - raise cherrypy.HTTPError(405, - 'Create is not allowed for %s' % get_class_name(self)) - params = parse_request() - validate_params(params, self, 'create') - args = self.model_args + [params] - name = create(*args) - cherrypy.response.status = 201 - args = self.resource_args + [name] - res = self.resource(self.model, *args) - return res.get() - - def _get_resources(self): - try: - get_list = getattr(self.model, model_fn(self, 'get_list')) - idents = get_list(*self.model_args) - res_list = [] - for ident in idents: - # internal text, get_list changes ident to unicode for sorted - args = self.resource_args + [ident] - res = self.resource(self.model, *args) - res.lookup() - res_list.append(res) - return res_list - except AttributeError: - return [] - - def _cp_dispatch(self, vpath): - if vpath: - ident = vpath.pop(0) - # incoming text, from URL, is not unicode, need decode - args = self.resource_args + [ident.decode("utf-8")] - return self.resource(self.model, *args) - - def get(self): - resources = self._get_resources() - data = [] - for res in resources: - data.append(res.data) - return kimchi.template.render(get_class_name(self), data) - - @cherrypy.expose - def index(self, *args): - method = validate_method(('GET', 'POST')) - if method == 'GET': - try: - return self.get() - except InvalidOperation, param: - raise cherrypy.HTTPError(400, - "Invalid operation: '%s'" % param) - except NotFoundError, param: - raise cherrypy.HTTPError(404, "Not found: '%s'" % param) - elif method == 'POST': - try: - return self.create(*args) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except OperationFailed, param: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % param) - except InvalidOperation, param: - raise cherrypy.HTTPError(400, - "Invalid operation: '%s'" % param) - except NotFoundError, param: - raise cherrypy.HTTPError(404, "Not found: '%s'" % param) - - -class AsyncCollection(Collection): - """ - A Collection to create it's resource by asynchronous task - """ - def __init__(self, model): - super(AsyncCollection, self).__init__(model) - - def create(self, *args): - try: - create = getattr(self.model, model_fn(self, 'create')) - except AttributeError: - raise cherrypy.HTTPError(405, - 'Create is not allowed for %s' % get_class_name(self)) - params = parse_request() - args = self.model_args + [params] - task = create(*args) - cherrypy.response.status = 202 - return kimchi.template.render("Task", task) - - -class DebugReportContent(Resource): - def __init__(self, model, ident): - super(DebugReportContent, self).__init__(model, ident) - - def get(self): - self.lookup() - raise internal_redirect(self.info['file']) - - -class VMs(Collection): - def __init__(self, model): - super(VMs, self).__init__(model) - self.resource = VM - - -class VM(Resource): - def __init__(self, model, ident): - super(VM, self).__init__(model, ident) - self.update_params = ["name"] - self.screenshot = VMScreenShot(model, ident) - self.uri_fmt = '/vms/%s' - self.start = self.generate_action_handler(self, 'start') - self.stop = self.generate_action_handler(self, 'stop') - self.connect = self.generate_action_handler(self, 'connect') - - @property - def data(self): - return {'name': self.ident, - 'uuid': self.info['uuid'], - 'stats': self.info['stats'], - 'memory': self.info['memory'], - 'cpus': self.info['cpus'], - 'state': self.info['state'], - 'screenshot': self.info['screenshot'], - 'icon': self.info['icon'], - 'graphics': {'type': self.info['graphics']['type'], - 'port': self.info['graphics']['port']}} - - -class VMScreenShot(Resource): - def __init__(self, model, ident): - super(VMScreenShot, self).__init__(model, ident) - - def get(self): - self.lookup() - raise internal_redirect(self.info) - -class Templates(Collection): - def __init__(self, model): - super(Templates, self).__init__(model) - self.resource = Template - - -class Template(Resource): - def __init__(self, model, ident): - super(Template, self).__init__(model, ident) - self.update_params = ["name", "folder", "icon", "os_distro", - "storagepool", "os_version", "cpus", - "memory", "cdrom", "disks"] - self.uri_fmt = "/templates/%s" - - @property - def data(self): - return {'name': self.ident, - 'icon': self.info['icon'], - 'os_distro': self.info['os_distro'], - 'os_version': self.info['os_version'], - 'cpus': self.info['cpus'], - 'memory': self.info['memory'], - 'cdrom': self.info['cdrom'], - 'disks': self.info['disks'], - 'storagepool': self.info['storagepool'], - 'folder': self.info.get('folder', [])} - - -class Interfaces(Collection): - def __init__(self, model): - super(Interfaces, self).__init__(model) - self.resource = Interface - - -class Interface(Resource): - def __init__(self, model, ident): - super(Interface, self).__init__(model, ident) - self.uri_fmt = "/interfaces/%s" - - @property - def data(self): - return {'name': self.ident, - 'type': self.info['type'], - 'ipaddr': self.info['ipaddr'], - 'netmask': self.info['netmask'], - 'status': self.info['status']} - - -class Networks(Collection): - def __init__(self, model): - super(Networks, self).__init__(model) - self.resource = Network - - -class Network(Resource): - def __init__(self, model, ident): - super(Network, self).__init__(model, ident) - self.uri_fmt = "/networks/%s" - self.activate = self.generate_action_handler(self, 'activate') - self.deactivate = self.generate_action_handler(self, 'deactivate') - - @property - def data(self): - return {'name': self.ident, - 'autostart': self.info['autostart'], - 'connection': self.info['connection'], - 'interface': self.info['interface'], - 'subnet': self.info['subnet'], - 'dhcp': self.info['dhcp'], - 'state': self.info['state']} - - -class StorageVolume(Resource): - def __init__(self, model, pool, ident): - super(StorageVolume, self).__init__(model, ident) - self.pool = pool - self.ident = ident - self.info = {} - self.model_args = [self.pool, self.ident] - self.uri_fmt = '/storagepools/%s/storagevolumes/%s' - self.resize = self.generate_action_handler(self, 'resize', ['size']) - self.wipe = self.generate_action_handler(self, 'wipe') - - @property - def data(self): - res = {'name': self.ident, - 'type': self.info['type'], - 'capacity': self.info['capacity'], - 'allocation': self.info['allocation'], - 'path': self.info['path'], - 'format': self.info['format']} - for key in ('os_version', 'os_distro', 'bootable'): - val = self.info.get(key) - if val: - res[key] = val - return res - - -class IsoVolumes(Collection): - def __init__(self, model, pool): - super(IsoVolumes, self).__init__(model) - self.pool = pool - - def get(self): - res_list = [] - try: - get_list = getattr(self.model, model_fn(self, 'get_list')) - res_list = get_list(*self.model_args) - except AttributeError: - pass - return kimchi.template.render(get_class_name(self), res_list) - - -class StorageVolumes(Collection): - def __init__(self, model, pool): - super(StorageVolumes, self).__init__(model) - self.resource = StorageVolume - self.pool = pool - self.resource_args = [self.pool, ] - self.model_args = [self.pool, ] - - -class StoragePool(Resource): - def __init__(self, model, ident): - super(StoragePool, self).__init__(model, ident) - self.update_params = ["autostart"] - self.uri_fmt = "/storagepools/%s" - self.activate = self.generate_action_handler(self, 'activate') - self.deactivate = self.generate_action_handler(self, 'deactivate') - - @property - def data(self): - res = {'name': self.ident, - 'state': self.info['state'], - 'capacity': self.info['capacity'], - 'allocated': self.info['allocated'], - 'available': self.info['available'], - 'path': self.info['path'], - 'source': self.info['source'], - 'type': self.info['type'], - 'nr_volumes': self.info['nr_volumes'], - 'autostart': self.info['autostart']} - val = self.info.get('task_id') - if val: - res['task_id'] = val - return res - - - def _cp_dispatch(self, vpath): - if vpath: - subcollection = vpath.pop(0) - if subcollection == 'storagevolumes': - # incoming text, from URL, is not unicode, need decode - return StorageVolumes(self.model, self.ident.decode("utf-8")) - - -class IsoPool(Resource): - def __init__(self, model): - super(IsoPool, self).__init__(model, ISO_POOL_NAME) - - @property - def data(self): - return {'name': self.ident, - 'state': self.info['state'], - 'type': self.info['type']} - - def _cp_dispatch(self, vpath): - if vpath: - subcollection = vpath.pop(0) - if subcollection == 'storagevolumes': - # incoming text, from URL, is not unicode, need decode - return IsoVolumes(self.model, self.ident.decode("utf-8")) - - -class StoragePools(Collection): - def __init__(self, model): - super(StoragePools, self).__init__(model) - self.resource = StoragePool - isos = IsoPool(model) - isos.exposed = True - setattr(self, ISO_POOL_NAME, isos) - - def create(self, *args): - try: - create = getattr(self.model, model_fn(self, 'create')) - except AttributeError: - raise cherrypy.HTTPError(405, - 'Create is not allowed for %s' % get_class_name(self)) - params = parse_request() - args = self.model_args + [params] - name = create(*args) - args = self.resource_args + [name] - res = self.resource(self.model, *args) - resp = res.get() - if 'task_id' in res.data: - cherrypy.response.status = 202 - else: - cherrypy.response.status = 201 - return resp - - def _get_resources(self): - try: - res_list = super(StoragePools, self)._get_resources() - # Append reserved pools - isos = getattr(self, ISO_POOL_NAME) - isos.lookup() - res_list.append(isos) - except AttributeError: - pass - return res_list - -class Task(Resource): - def __init__(self, model, id): - super(Task, self).__init__(model, id) - - @property - def data(self): - return {'id': self.ident, - 'status': self.info['status'], - 'message': self.info['message']} - - -class Tasks(Collection): - def __init__(self, model): - super(Tasks, self).__init__(model) - self.resource = Task - - -class DebugReports(AsyncCollection): - def __init__(self, model): - super(DebugReports, self).__init__(model) - self.resource = DebugReport - - -class DebugReport(Resource): - def __init__(self, model, ident): - super(DebugReport, self).__init__(model, ident) - self.ident = ident - self.content = DebugReportContent(model, ident) - - @property - def data(self): - return {'name': self.ident, - 'file': self.info['file'], - 'time': self.info['ctime']} - - -class Config(Resource): - def __init__(self, model, id=None): - super(Config, self).__init__(model, id) - self.capabilities = Capabilities(self.model) - self.capabilities.exposed = True - self.distros = Distros(model) - self.distros.exposed = True - - @property - def data(self): - return {'http_port': cherrypy.server.socket_port} - -class Capabilities(Resource): - def __init__(self, model, id=None): - super(Capabilities, self).__init__(model, id) - self.model = model - - @property - def data(self): - caps = ['libvirt_stream_protocols', 'qemu_stream', - 'screenshot', 'system_report_tool'] - ret = dict([(x, None) for x in caps]) - ret.update(self.model.get_capabilities()) - return ret - - -class Distro(Resource): - def __init__(self, model, ident): - super(Distro, self).__init__(model, ident) - - @property - def data(self): - return self.info - - -class Distros(Collection): - def __init__(self, model): - super(Distros, self).__init__(model) - self.resource = Distro - - -class Host(Resource): - def __init__(self, model, id=None): - super(Host, self).__init__(model, id) - self.stats = HostStats(self.model) - self.stats.exposed = True - self.uri_fmt = '/host/%s' - self.reboot = self.generate_action_handler(self, 'reboot') - self.shutdown = self.generate_action_handler(self, 'shutdown') - self.partitions = Partitions(self.model) - self.partitions.exposed = True - - @property - def data(self): - return self.info - -class HostStats(Resource): - @property - def data(self): - return self.info - -class Partitions(Collection): - def __init__(self, model): - super(Partitions, self).__init__(model) - self.resource = Partition - - -class Partition(Resource): - def __init__(self, model,id): - super(Partition, self).__init__(model,id) - - @property - def data(self): - return self.info - -class Plugins(Collection): - def __init__(self, model): - super(Plugins, self).__init__(model) - self.model = model - - @property - def data(self): - return self.info - - def get(self): - res_list = [] - try: - get_list = getattr(self.model, model_fn(self, 'get_list')) - res_list = get_list(*self.model_args) - except AttributeError: - pass - return kimchi.template.render(get_class_name(self), res_list) diff --git a/src/kimchi/root.py b/src/kimchi/root.py index 6310781..0261352 100644 --- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -26,13 +26,23 @@ import json from kimchi import auth -from kimchi import controller from kimchi import template from kimchi.config import get_api_schema_file from kimchi.control.utils import parse_request - - -class Root(controller.Resource): +from kimchi.control.base import Resource +from kimchi.control.config import Config +from kimchi.control.debugreports import DebugReports +from kimchi.control.host import Host +from kimchi.control.interfaces import Interfaces +from kimchi.control.networks import Networks +from kimchi.control.plugins import Plugins +from kimchi.control.storagepools import StoragePools +from kimchi.control.tasks import Tasks +from kimchi.control.templates import Templates +from kimchi.control.vms import VMs + + +class Root(Resource): def __init__(self, model, dev_env): self._handled_error = ['error_page.400', 'error_page.404', 'error_page.405', 'error_page.406', @@ -45,17 +55,17 @@ class Root(controller.Resource): self._cp_config = dict([(key, self.error_development_handler) for key in self._handled_error]) - controller.Resource.__init__(self, model) - self.vms = controller.VMs(model) - self.templates = controller.Templates(model) - self.storagepools = controller.StoragePools(model) - self.interfaces = controller.Interfaces(model) - self.networks = controller.Networks(model) - self.tasks = controller.Tasks(model) - self.config = controller.Config(model) - self.host = controller.Host(model) - self.debugreports = controller.DebugReports(model) - self.plugins = controller.Plugins(model) + Resource.__init__(self, model) + self.vms = VMs(model) + self.templates = Templates(model) + self.storagepools = StoragePools(model) + self.interfaces = Interfaces(model) + self.networks = Networks(model) + self.tasks = Tasks(model) + self.config = Config(model) + self.host = Host(model) + self.debugreports = DebugReports(model) + self.plugins = Plugins(model) self.api_schema = json.load(open(get_api_schema_file())) def error_production_handler(status, message, traceback, version): -- 1.7.10.4

Reviewed-By: Ramon Medeiros <ramonn@br.ibm.com>

On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
controller.py was splitted into small modules to make maintenance easier That way each resource has its own module under /control Also delete former controller.py
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/controller.py | 667 ---------------------------------------------- src/kimchi/root.py | 40 +-- 2 files changed, 25 insertions(+), 682 deletions(-) you are delete a files $ git config --global diff.renames ture
delete mode 100644 src/kimchi/controller.py
diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py deleted file mode 100644 index 5bcffff..0000000 --- a/src/kimchi/controller.py +++ /dev/null @@ -1,667 +0,0 @@ -# -# Project Kimchi -# -# Copyright IBM, Corp. 2013 -# -# Authors: -# Adam Litke <agl@linux.vnet.ibm.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -import cherrypy -import urllib2 - - -from functools import wraps - - -import kimchi.template -from kimchi.control.utils import get_class_name, internal_redirect, model_fn -from kimchi.control.utils import parse_request, validate_method, validate_params -from kimchi.exception import InvalidOperation, InvalidParameter, MissingParameter -from kimchi.exception import NotFoundError, OperationFailed -from kimchi.model import ISO_POOL_NAME - - -class Resource(object): - """ - A Resource represents a single entity in the API (such as a Virtual Machine) - - To create new Resource types, subclass this and change the following things - in the child class: - - - If the Resource requires more than one identifier set self.model_args as - appropriate. This should only be necessary if this Resource is logically - nested. For example: A Storage Volume belongs to a Storage Pool so the - Storage Volume would set model args to (pool_ident, volume_ident). - - - Implement the base operations of 'lookup' and 'delete' in the model(s). - - - Set the 'data' property to a JSON-serializable representation of the - Resource. - """ - def __init__(self, model, ident=None): - self.model = model - self.ident = ident - self.model_args = (ident,) - self.update_params = [] - - def generate_action_handler(self, instance, action_name, action_args=None): - def wrapper(*args, **kwargs): - validate_method(('POST')) - try: - model_args = list(instance.model_args) - if action_args is not None: - model_args.extend(parse_request()[key] for key in action_args) - fn = getattr(instance.model, model_fn(instance, action_name)) - fn(*model_args) - raise internal_redirect(instance.uri_fmt % - tuple(instance.model_args)) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - wrapper.__name__ = action_name - wrapper.exposed = True - return wrapper - - def lookup(self): - try: - lookup = getattr(self.model, model_fn(self, 'lookup')) - self.info = lookup(*self.model_args) - except AttributeError: - self.info = {} - - def delete(self): - try: - fn = getattr(self.model, model_fn(self, 'delete')) - fn(*self.model_args) - cherrypy.response.status = 204 - except AttributeError: - raise cherrypy.HTTPError(405, 'Delete is not allowed for %s' % get_class_name(self)) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - - @cherrypy.expose - def index(self): - method = validate_method(('GET', 'DELETE', 'PUT')) - if method == 'GET': - try: - return self.get() - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(406, "Operation failed: '%s'" % msg) - elif method == 'DELETE': - try: - return self.delete() - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - elif method == 'PUT': - try: - return self.update() - except InvalidParameter, msg: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % msg) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - def update(self): - try: - update = getattr(self.model, model_fn(self, 'update')) - except AttributeError: - raise cherrypy.HTTPError(405, "%s does not implement update " - "method" % get_class_name(self)) - params = parse_request() - validate_params(params, self, 'update') - if self.update_params != None: - invalids = [v for v in params.keys() if - v not in self.update_params] - if invalids: - raise cherrypy.HTTPError(405, "%s are not allowed to be updated" % - invalids) - ident = update(self.ident, params) - if ident != self.ident: - raise cherrypy.HTTPRedirect(self.uri_fmt % - tuple(list(self.model_args[:-1]) + [urllib2.quote(ident.encode('utf8'))]), - 303) - return self.get() - - - def get(self): - self.lookup() - return kimchi.template.render(get_class_name(self), self.data) - - @property - def data(self): - """ - Override this in inherited classes to provide the Resource - representation as a python dictionary. - """ - return {} - - -class Collection(object): - """ - A Collection is a container for Resource objects. To create a new - Collection type, subclass this and make the following changes to the child - class: - - - Set self.resource to the type of Resource that this Collection contains - - - Set self.resource_args. This can remain an empty list if the Resources - can be initialized with only one identifier. Otherwise, include - additional values as needed (eg. to identify a parent resource). - - - Set self.model_args. Similar to above, this is needed only if the model - needs additional information to identify this Collection. - - - Implement the base operations of 'create' and 'get_list' in the model. - """ - def __init__(self, model): - self.model = model - self.resource = Resource - self.resource_args = [] - self.model_args = [] - - def create(self, *args): - try: - create = getattr(self.model, model_fn(self, 'create')) - except AttributeError: - raise cherrypy.HTTPError(405, - 'Create is not allowed for %s' % get_class_name(self)) - params = parse_request() - validate_params(params, self, 'create') - args = self.model_args + [params] - name = create(*args) - cherrypy.response.status = 201 - args = self.resource_args + [name] - res = self.resource(self.model, *args) - return res.get() - - def _get_resources(self): - try: - get_list = getattr(self.model, model_fn(self, 'get_list')) - idents = get_list(*self.model_args) - res_list = [] - for ident in idents: - # internal text, get_list changes ident to unicode for sorted - args = self.resource_args + [ident] - res = self.resource(self.model, *args) - res.lookup() - res_list.append(res) - return res_list - except AttributeError: - return [] - - def _cp_dispatch(self, vpath): - if vpath: - ident = vpath.pop(0) - # incoming text, from URL, is not unicode, need decode - args = self.resource_args + [ident.decode("utf-8")] - return self.resource(self.model, *args) - - def get(self): - resources = self._get_resources() - data = [] - for res in resources: - data.append(res.data) - return kimchi.template.render(get_class_name(self), data) - - @cherrypy.expose - def index(self, *args): - method = validate_method(('GET', 'POST')) - if method == 'GET': - try: - return self.get() - except InvalidOperation, param: - raise cherrypy.HTTPError(400, - "Invalid operation: '%s'" % param) - except NotFoundError, param: - raise cherrypy.HTTPError(404, "Not found: '%s'" % param) - elif method == 'POST': - try: - return self.create(*args) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except OperationFailed, param: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % param) - except InvalidOperation, param: - raise cherrypy.HTTPError(400, - "Invalid operation: '%s'" % param) - except NotFoundError, param: - raise cherrypy.HTTPError(404, "Not found: '%s'" % param) - - -class AsyncCollection(Collection): - """ - A Collection to create it's resource by asynchronous task - """ - def __init__(self, model): - super(AsyncCollection, self).__init__(model) - - def create(self, *args): - try: - create = getattr(self.model, model_fn(self, 'create')) - except AttributeError: - raise cherrypy.HTTPError(405, - 'Create is not allowed for %s' % get_class_name(self)) - params = parse_request() - args = self.model_args + [params] - task = create(*args) - cherrypy.response.status = 202 - return kimchi.template.render("Task", task) - - -class DebugReportContent(Resource): - def __init__(self, model, ident): - super(DebugReportContent, self).__init__(model, ident) - - def get(self): - self.lookup() - raise internal_redirect(self.info['file']) - - -class VMs(Collection): - def __init__(self, model): - super(VMs, self).__init__(model) - self.resource = VM - - -class VM(Resource): - def __init__(self, model, ident): - super(VM, self).__init__(model, ident) - self.update_params = ["name"] - self.screenshot = VMScreenShot(model, ident) - self.uri_fmt = '/vms/%s' - self.start = self.generate_action_handler(self, 'start') - self.stop = self.generate_action_handler(self, 'stop') - self.connect = self.generate_action_handler(self, 'connect') - - @property - def data(self): - return {'name': self.ident, - 'uuid': self.info['uuid'], - 'stats': self.info['stats'], - 'memory': self.info['memory'], - 'cpus': self.info['cpus'], - 'state': self.info['state'], - 'screenshot': self.info['screenshot'], - 'icon': self.info['icon'], - 'graphics': {'type': self.info['graphics']['type'], - 'port': self.info['graphics']['port']}} - - -class VMScreenShot(Resource): - def __init__(self, model, ident): - super(VMScreenShot, self).__init__(model, ident) - - def get(self): - self.lookup() - raise internal_redirect(self.info) - -class Templates(Collection): - def __init__(self, model): - super(Templates, self).__init__(model) - self.resource = Template - - -class Template(Resource): - def __init__(self, model, ident): - super(Template, self).__init__(model, ident) - self.update_params = ["name", "folder", "icon", "os_distro", - "storagepool", "os_version", "cpus", - "memory", "cdrom", "disks"] - self.uri_fmt = "/templates/%s" - - @property - def data(self): - return {'name': self.ident, - 'icon': self.info['icon'], - 'os_distro': self.info['os_distro'], - 'os_version': self.info['os_version'], - 'cpus': self.info['cpus'], - 'memory': self.info['memory'], - 'cdrom': self.info['cdrom'], - 'disks': self.info['disks'], - 'storagepool': self.info['storagepool'], - 'folder': self.info.get('folder', [])} - - -class Interfaces(Collection): - def __init__(self, model): - super(Interfaces, self).__init__(model) - self.resource = Interface - - -class Interface(Resource): - def __init__(self, model, ident): - super(Interface, self).__init__(model, ident) - self.uri_fmt = "/interfaces/%s" - - @property - def data(self): - return {'name': self.ident, - 'type': self.info['type'], - 'ipaddr': self.info['ipaddr'], - 'netmask': self.info['netmask'], - 'status': self.info['status']} - - -class Networks(Collection): - def __init__(self, model): - super(Networks, self).__init__(model) - self.resource = Network - - -class Network(Resource): - def __init__(self, model, ident): - super(Network, self).__init__(model, ident) - self.uri_fmt = "/networks/%s" - self.activate = self.generate_action_handler(self, 'activate') - self.deactivate = self.generate_action_handler(self, 'deactivate') - - @property - def data(self): - return {'name': self.ident, - 'autostart': self.info['autostart'], - 'connection': self.info['connection'], - 'interface': self.info['interface'], - 'subnet': self.info['subnet'], - 'dhcp': self.info['dhcp'], - 'state': self.info['state']} - - -class StorageVolume(Resource): - def __init__(self, model, pool, ident): - super(StorageVolume, self).__init__(model, ident) - self.pool = pool - self.ident = ident - self.info = {} - self.model_args = [self.pool, self.ident] - self.uri_fmt = '/storagepools/%s/storagevolumes/%s' - self.resize = self.generate_action_handler(self, 'resize', ['size']) - self.wipe = self.generate_action_handler(self, 'wipe') - - @property - def data(self): - res = {'name': self.ident, - 'type': self.info['type'], - 'capacity': self.info['capacity'], - 'allocation': self.info['allocation'], - 'path': self.info['path'], - 'format': self.info['format']} - for key in ('os_version', 'os_distro', 'bootable'): - val = self.info.get(key) - if val: - res[key] = val - return res - - -class IsoVolumes(Collection): - def __init__(self, model, pool): - super(IsoVolumes, self).__init__(model) - self.pool = pool - - def get(self): - res_list = [] - try: - get_list = getattr(self.model, model_fn(self, 'get_list')) - res_list = get_list(*self.model_args) - except AttributeError: - pass - return kimchi.template.render(get_class_name(self), res_list) - - -class StorageVolumes(Collection): - def __init__(self, model, pool): - super(StorageVolumes, self).__init__(model) - self.resource = StorageVolume - self.pool = pool - self.resource_args = [self.pool, ] - self.model_args = [self.pool, ] - - -class StoragePool(Resource): - def __init__(self, model, ident): - super(StoragePool, self).__init__(model, ident) - self.update_params = ["autostart"] - self.uri_fmt = "/storagepools/%s" - self.activate = self.generate_action_handler(self, 'activate') - self.deactivate = self.generate_action_handler(self, 'deactivate') - - @property - def data(self): - res = {'name': self.ident, - 'state': self.info['state'], - 'capacity': self.info['capacity'], - 'allocated': self.info['allocated'], - 'available': self.info['available'], - 'path': self.info['path'], - 'source': self.info['source'], - 'type': self.info['type'], - 'nr_volumes': self.info['nr_volumes'], - 'autostart': self.info['autostart']} - val = self.info.get('task_id') - if val: - res['task_id'] = val - return res - - - def _cp_dispatch(self, vpath): - if vpath: - subcollection = vpath.pop(0) - if subcollection == 'storagevolumes': - # incoming text, from URL, is not unicode, need decode - return StorageVolumes(self.model, self.ident.decode("utf-8")) - - -class IsoPool(Resource): - def __init__(self, model): - super(IsoPool, self).__init__(model, ISO_POOL_NAME) - - @property - def data(self): - return {'name': self.ident, - 'state': self.info['state'], - 'type': self.info['type']} - - def _cp_dispatch(self, vpath): - if vpath: - subcollection = vpath.pop(0) - if subcollection == 'storagevolumes': - # incoming text, from URL, is not unicode, need decode - return IsoVolumes(self.model, self.ident.decode("utf-8")) - - -class StoragePools(Collection): - def __init__(self, model): - super(StoragePools, self).__init__(model) - self.resource = StoragePool - isos = IsoPool(model) - isos.exposed = True - setattr(self, ISO_POOL_NAME, isos) - - def create(self, *args): - try: - create = getattr(self.model, model_fn(self, 'create')) - except AttributeError: - raise cherrypy.HTTPError(405, - 'Create is not allowed for %s' % get_class_name(self)) - params = parse_request() - args = self.model_args + [params] - name = create(*args) - args = self.resource_args + [name] - res = self.resource(self.model, *args) - resp = res.get() - if 'task_id' in res.data: - cherrypy.response.status = 202 - else: - cherrypy.response.status = 201 - return resp - - def _get_resources(self): - try: - res_list = super(StoragePools, self)._get_resources() - # Append reserved pools - isos = getattr(self, ISO_POOL_NAME) - isos.lookup() - res_list.append(isos) - except AttributeError: - pass - return res_list - -class Task(Resource): - def __init__(self, model, id): - super(Task, self).__init__(model, id) - - @property - def data(self): - return {'id': self.ident, - 'status': self.info['status'], - 'message': self.info['message']} - - -class Tasks(Collection): - def __init__(self, model): - super(Tasks, self).__init__(model) - self.resource = Task - - -class DebugReports(AsyncCollection): - def __init__(self, model): - super(DebugReports, self).__init__(model) - self.resource = DebugReport - - -class DebugReport(Resource): - def __init__(self, model, ident): - super(DebugReport, self).__init__(model, ident) - self.ident = ident - self.content = DebugReportContent(model, ident) - - @property - def data(self): - return {'name': self.ident, - 'file': self.info['file'], - 'time': self.info['ctime']} - - -class Config(Resource): - def __init__(self, model, id=None): - super(Config, self).__init__(model, id) - self.capabilities = Capabilities(self.model) - self.capabilities.exposed = True - self.distros = Distros(model) - self.distros.exposed = True - - @property - def data(self): - return {'http_port': cherrypy.server.socket_port} - -class Capabilities(Resource): - def __init__(self, model, id=None): - super(Capabilities, self).__init__(model, id) - self.model = model - - @property - def data(self): - caps = ['libvirt_stream_protocols', 'qemu_stream', - 'screenshot', 'system_report_tool'] - ret = dict([(x, None) for x in caps]) - ret.update(self.model.get_capabilities()) - return ret - - -class Distro(Resource): - def __init__(self, model, ident): - super(Distro, self).__init__(model, ident) - - @property - def data(self): - return self.info - - -class Distros(Collection): - def __init__(self, model): - super(Distros, self).__init__(model) - self.resource = Distro - - -class Host(Resource): - def __init__(self, model, id=None): - super(Host, self).__init__(model, id) - self.stats = HostStats(self.model) - self.stats.exposed = True - self.uri_fmt = '/host/%s' - self.reboot = self.generate_action_handler(self, 'reboot') - self.shutdown = self.generate_action_handler(self, 'shutdown') - self.partitions = Partitions(self.model) - self.partitions.exposed = True - - @property - def data(self): - return self.info - -class HostStats(Resource): - @property - def data(self): - return self.info - -class Partitions(Collection): - def __init__(self, model): - super(Partitions, self).__init__(model) - self.resource = Partition - - -class Partition(Resource): - def __init__(self, model,id): - super(Partition, self).__init__(model,id) - - @property - def data(self): - return self.info - -class Plugins(Collection): - def __init__(self, model): - super(Plugins, self).__init__(model) - self.model = model - - @property - def data(self): - return self.info - - def get(self): - res_list = [] - try: - get_list = getattr(self.model, model_fn(self, 'get_list')) - res_list = get_list(*self.model_args) - except AttributeError: - pass - return kimchi.template.render(get_class_name(self), res_list) diff --git a/src/kimchi/root.py b/src/kimchi/root.py index 6310781..0261352 100644 --- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -26,13 +26,23 @@ import json
from kimchi import auth -from kimchi import controller from kimchi import template from kimchi.config import get_api_schema_file from kimchi.control.utils import parse_request - - -class Root(controller.Resource): +from kimchi.control.base import Resource +from kimchi.control.config import Config +from kimchi.control.debugreports import DebugReports +from kimchi.control.host import Host +from kimchi.control.interfaces import Interfaces +from kimchi.control.networks import Networks +from kimchi.control.plugins import Plugins +from kimchi.control.storagepools import StoragePools +from kimchi.control.tasks import Tasks +from kimchi.control.templates import Templates +from kimchi.control.vms import VMs + + +class Root(Resource): def __init__(self, model, dev_env): self._handled_error = ['error_page.400', 'error_page.404', 'error_page.405', 'error_page.406', @@ -45,17 +55,17 @@ class Root(controller.Resource): self._cp_config = dict([(key, self.error_development_handler) for key in self._handled_error])
- controller.Resource.__init__(self, model) - self.vms = controller.VMs(model) - self.templates = controller.Templates(model) - self.storagepools = controller.StoragePools(model) - self.interfaces = controller.Interfaces(model) - self.networks = controller.Networks(model) - self.tasks = controller.Tasks(model) - self.config = controller.Config(model) - self.host = controller.Host(model) - self.debugreports = controller.DebugReports(model) - self.plugins = controller.Plugins(model) + Resource.__init__(self, model)
consistence with other kimchi python class define It is more better: + super(Resource, self).__init__(model)
+ self.vms = VMs(model) + self.templates = Templates(model) + self.storagepools = StoragePools(model) + self.interfaces = Interfaces(model) + self.networks = Networks(model) + self.tasks = Tasks(model) + self.config = Config(model) + self.host = Host(model) + self.debugreports = DebugReports(model) + self.plugins = Plugins(model) self.api_schema = json.load(open(get_api_schema_file()))
def error_production_handler(status, message, traceback, version):
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 12/24/2013 06:37 PM, Sheldon wrote:
On 12/24/2013 02:41 AM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
controller.py was splitted into small modules to make maintenance easier That way each resource has its own module under /control Also delete former controller.py
Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/controller.py | 667 ---------------------------------------------- src/kimchi/root.py | 40 +-- 2 files changed, 25 insertions(+), 682 deletions(-) you are delete a files $ git config --global diff.renames ture ignore this. diff.renames takes no effect on deletefile.
just see the next comment.
delete mode 100644 src/kimchi/controller.py
diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py deleted file mode 100644 index 5bcffff..0000000 --- a/src/kimchi/controller.py +++ /dev/null @@ -1,667 +0,0 @@ -# -# Project Kimchi -# -# Copyright IBM, Corp. 2013 -# -# Authors: -# Adam Litke <agl@linux.vnet.ibm.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -import cherrypy -import urllib2 - - -from functools import wraps - - -import kimchi.template -from kimchi.control.utils import get_class_name, internal_redirect, model_fn -from kimchi.control.utils import parse_request, validate_method, validate_params -from kimchi.exception import InvalidOperation, InvalidParameter, MissingParameter -from kimchi.exception import NotFoundError, OperationFailed -from kimchi.model import ISO_POOL_NAME - - -class Resource(object): - """ - A Resource represents a single entity in the API (such as a Virtual Machine) - - To create new Resource types, subclass this and change the following things - in the child class: - - - If the Resource requires more than one identifier set self.model_args as - appropriate. This should only be necessary if this Resource is logically - nested. For example: A Storage Volume belongs to a Storage Pool so the - Storage Volume would set model args to (pool_ident, volume_ident). - - - Implement the base operations of 'lookup' and 'delete' in the model(s). - - - Set the 'data' property to a JSON-serializable representation of the - Resource. - """ - def __init__(self, model, ident=None): - self.model = model - self.ident = ident - self.model_args = (ident,) - self.update_params = [] - - def generate_action_handler(self, instance, action_name, action_args=None): - def wrapper(*args, **kwargs): - validate_method(('POST')) - try: - model_args = list(instance.model_args) - if action_args is not None: - model_args.extend(parse_request()[key] for key in action_args) - fn = getattr(instance.model, model_fn(instance, action_name)) - fn(*model_args) - raise internal_redirect(instance.uri_fmt % - tuple(instance.model_args)) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - wrapper.__name__ = action_name - wrapper.exposed = True - return wrapper - - def lookup(self): - try: - lookup = getattr(self.model, model_fn(self, 'lookup')) - self.info = lookup(*self.model_args) - except AttributeError: - self.info = {} - - def delete(self): - try: - fn = getattr(self.model, model_fn(self, 'delete')) - fn(*self.model_args) - cherrypy.response.status = 204 - except AttributeError: - raise cherrypy.HTTPError(405, 'Delete is not allowed for %s' % get_class_name(self)) - except OperationFailed, msg: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % msg) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - - @cherrypy.expose - def index(self): - method = validate_method(('GET', 'DELETE', 'PUT')) - if method == 'GET': - try: - return self.get() - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except OperationFailed, msg: - raise cherrypy.HTTPError(406, "Operation failed: '%s'" % msg) - elif method == 'DELETE': - try: - return self.delete() - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - elif method == 'PUT': - try: - return self.update() - except InvalidParameter, msg: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % msg) - except InvalidOperation, msg: - raise cherrypy.HTTPError(400, "Invalid operation: '%s'" % msg) - except NotFoundError, msg: - raise cherrypy.HTTPError(404, "Not found: '%s'" % msg) - - def update(self): - try: - update = getattr(self.model, model_fn(self, 'update')) - except AttributeError: - raise cherrypy.HTTPError(405, "%s does not implement update " - "method" % get_class_name(self)) - params = parse_request() - validate_params(params, self, 'update') - if self.update_params != None: - invalids = [v for v in params.keys() if - v not in self.update_params] - if invalids: - raise cherrypy.HTTPError(405, "%s are not allowed to be updated" % - invalids) - ident = update(self.ident, params) - if ident != self.ident: - raise cherrypy.HTTPRedirect(self.uri_fmt % - tuple(list(self.model_args[:-1]) + [urllib2.quote(ident.encode('utf8'))]), - 303) - return self.get() - - - def get(self): - self.lookup() - return kimchi.template.render(get_class_name(self), self.data) - - @property - def data(self): - """ - Override this in inherited classes to provide the Resource - representation as a python dictionary. - """ - return {} - - -class Collection(object): - """ - A Collection is a container for Resource objects. To create a new - Collection type, subclass this and make the following changes to the child - class: - - - Set self.resource to the type of Resource that this Collection contains - - - Set self.resource_args. This can remain an empty list if the Resources - can be initialized with only one identifier. Otherwise, include - additional values as needed (eg. to identify a parent resource). - - - Set self.model_args. Similar to above, this is needed only if the model - needs additional information to identify this Collection. - - - Implement the base operations of 'create' and 'get_list' in the model. - """ - def __init__(self, model): - self.model = model - self.resource = Resource - self.resource_args = [] - self.model_args = [] - - def create(self, *args): - try: - create = getattr(self.model, model_fn(self, 'create')) - except AttributeError: - raise cherrypy.HTTPError(405, - 'Create is not allowed for %s' % get_class_name(self)) - params = parse_request() - validate_params(params, self, 'create') - args = self.model_args + [params] - name = create(*args) - cherrypy.response.status = 201 - args = self.resource_args + [name] - res = self.resource(self.model, *args) - return res.get() - - def _get_resources(self): - try: - get_list = getattr(self.model, model_fn(self, 'get_list')) - idents = get_list(*self.model_args) - res_list = [] - for ident in idents: - # internal text, get_list changes ident to unicode for sorted - args = self.resource_args + [ident] - res = self.resource(self.model, *args) - res.lookup() - res_list.append(res) - return res_list - except AttributeError: - return [] - - def _cp_dispatch(self, vpath): - if vpath: - ident = vpath.pop(0) - # incoming text, from URL, is not unicode, need decode - args = self.resource_args + [ident.decode("utf-8")] - return self.resource(self.model, *args) - - def get(self): - resources = self._get_resources() - data = [] - for res in resources: - data.append(res.data) - return kimchi.template.render(get_class_name(self), data) - - @cherrypy.expose - def index(self, *args): - method = validate_method(('GET', 'POST')) - if method == 'GET': - try: - return self.get() - except InvalidOperation, param: - raise cherrypy.HTTPError(400, - "Invalid operation: '%s'" % param) - except NotFoundError, param: - raise cherrypy.HTTPError(404, "Not found: '%s'" % param) - elif method == 'POST': - try: - return self.create(*args) - except MissingParameter, param: - raise cherrypy.HTTPError(400, "Missing parameter: '%s'" % param) - except InvalidParameter, param: - raise cherrypy.HTTPError(400, "Invalid parameter: '%s'" % param) - except OperationFailed, param: - raise cherrypy.HTTPError(500, "Operation Failed: '%s'" % param) - except InvalidOperation, param: - raise cherrypy.HTTPError(400, - "Invalid operation: '%s'" % param) - except NotFoundError, param: - raise cherrypy.HTTPError(404, "Not found: '%s'" % param) - - -class AsyncCollection(Collection): - """ - A Collection to create it's resource by asynchronous task - """ - def __init__(self, model): - super(AsyncCollection, self).__init__(model) - - def create(self, *args): - try: - create = getattr(self.model, model_fn(self, 'create')) - except AttributeError: - raise cherrypy.HTTPError(405, - 'Create is not allowed for %s' % get_class_name(self)) - params = parse_request() - args = self.model_args + [params] - task = create(*args) - cherrypy.response.status = 202 - return kimchi.template.render("Task", task) - - -class DebugReportContent(Resource): - def __init__(self, model, ident): - super(DebugReportContent, self).__init__(model, ident) - - def get(self): - self.lookup() - raise internal_redirect(self.info['file']) - - -class VMs(Collection): - def __init__(self, model): - super(VMs, self).__init__(model) - self.resource = VM - - -class VM(Resource): - def __init__(self, model, ident): - super(VM, self).__init__(model, ident) - self.update_params = ["name"] - self.screenshot = VMScreenShot(model, ident) - self.uri_fmt = '/vms/%s' - self.start = self.generate_action_handler(self, 'start') - self.stop = self.generate_action_handler(self, 'stop') - self.connect = self.generate_action_handler(self, 'connect') - - @property - def data(self): - return {'name': self.ident, - 'uuid': self.info['uuid'], - 'stats': self.info['stats'], - 'memory': self.info['memory'], - 'cpus': self.info['cpus'], - 'state': self.info['state'], - 'screenshot': self.info['screenshot'], - 'icon': self.info['icon'], - 'graphics': {'type': self.info['graphics']['type'], - 'port': self.info['graphics']['port']}} - - -class VMScreenShot(Resource): - def __init__(self, model, ident): - super(VMScreenShot, self).__init__(model, ident) - - def get(self): - self.lookup() - raise internal_redirect(self.info) - -class Templates(Collection): - def __init__(self, model): - super(Templates, self).__init__(model) - self.resource = Template - - -class Template(Resource): - def __init__(self, model, ident): - super(Template, self).__init__(model, ident) - self.update_params = ["name", "folder", "icon", "os_distro", - "storagepool", "os_version", "cpus", - "memory", "cdrom", "disks"] - self.uri_fmt = "/templates/%s" - - @property - def data(self): - return {'name': self.ident, - 'icon': self.info['icon'], - 'os_distro': self.info['os_distro'], - 'os_version': self.info['os_version'], - 'cpus': self.info['cpus'], - 'memory': self.info['memory'], - 'cdrom': self.info['cdrom'], - 'disks': self.info['disks'], - 'storagepool': self.info['storagepool'], - 'folder': self.info.get('folder', [])} - - -class Interfaces(Collection): - def __init__(self, model): - super(Interfaces, self).__init__(model) - self.resource = Interface - - -class Interface(Resource): - def __init__(self, model, ident): - super(Interface, self).__init__(model, ident) - self.uri_fmt = "/interfaces/%s" - - @property - def data(self): - return {'name': self.ident, - 'type': self.info['type'], - 'ipaddr': self.info['ipaddr'], - 'netmask': self.info['netmask'], - 'status': self.info['status']} - - -class Networks(Collection): - def __init__(self, model): - super(Networks, self).__init__(model) - self.resource = Network - - -class Network(Resource): - def __init__(self, model, ident): - super(Network, self).__init__(model, ident) - self.uri_fmt = "/networks/%s" - self.activate = self.generate_action_handler(self, 'activate') - self.deactivate = self.generate_action_handler(self, 'deactivate') - - @property - def data(self): - return {'name': self.ident, - 'autostart': self.info['autostart'], - 'connection': self.info['connection'], - 'interface': self.info['interface'], - 'subnet': self.info['subnet'], - 'dhcp': self.info['dhcp'], - 'state': self.info['state']} - - -class StorageVolume(Resource): - def __init__(self, model, pool, ident): - super(StorageVolume, self).__init__(model, ident) - self.pool = pool - self.ident = ident - self.info = {} - self.model_args = [self.pool, self.ident] - self.uri_fmt = '/storagepools/%s/storagevolumes/%s' - self.resize = self.generate_action_handler(self, 'resize', ['size']) - self.wipe = self.generate_action_handler(self, 'wipe') - - @property - def data(self): - res = {'name': self.ident, - 'type': self.info['type'], - 'capacity': self.info['capacity'], - 'allocation': self.info['allocation'], - 'path': self.info['path'], - 'format': self.info['format']} - for key in ('os_version', 'os_distro', 'bootable'): - val = self.info.get(key) - if val: - res[key] = val - return res - - -class IsoVolumes(Collection): - def __init__(self, model, pool): - super(IsoVolumes, self).__init__(model) - self.pool = pool - - def get(self): - res_list = [] - try: - get_list = getattr(self.model, model_fn(self, 'get_list')) - res_list = get_list(*self.model_args) - except AttributeError: - pass - return kimchi.template.render(get_class_name(self), res_list) - - -class StorageVolumes(Collection): - def __init__(self, model, pool): - super(StorageVolumes, self).__init__(model) - self.resource = StorageVolume - self.pool = pool - self.resource_args = [self.pool, ] - self.model_args = [self.pool, ] - - -class StoragePool(Resource): - def __init__(self, model, ident): - super(StoragePool, self).__init__(model, ident) - self.update_params = ["autostart"] - self.uri_fmt = "/storagepools/%s" - self.activate = self.generate_action_handler(self, 'activate') - self.deactivate = self.generate_action_handler(self, 'deactivate') - - @property - def data(self): - res = {'name': self.ident, - 'state': self.info['state'], - 'capacity': self.info['capacity'], - 'allocated': self.info['allocated'], - 'available': self.info['available'], - 'path': self.info['path'], - 'source': self.info['source'], - 'type': self.info['type'], - 'nr_volumes': self.info['nr_volumes'], - 'autostart': self.info['autostart']} - val = self.info.get('task_id') - if val: - res['task_id'] = val - return res - - - def _cp_dispatch(self, vpath): - if vpath: - subcollection = vpath.pop(0) - if subcollection == 'storagevolumes': - # incoming text, from URL, is not unicode, need decode - return StorageVolumes(self.model, self.ident.decode("utf-8")) - - -class IsoPool(Resource): - def __init__(self, model): - super(IsoPool, self).__init__(model, ISO_POOL_NAME) - - @property - def data(self): - return {'name': self.ident, - 'state': self.info['state'], - 'type': self.info['type']} - - def _cp_dispatch(self, vpath): - if vpath: - subcollection = vpath.pop(0) - if subcollection == 'storagevolumes': - # incoming text, from URL, is not unicode, need decode - return IsoVolumes(self.model, self.ident.decode("utf-8")) - - -class StoragePools(Collection): - def __init__(self, model): - super(StoragePools, self).__init__(model) - self.resource = StoragePool - isos = IsoPool(model) - isos.exposed = True - setattr(self, ISO_POOL_NAME, isos) - - def create(self, *args): - try: - create = getattr(self.model, model_fn(self, 'create')) - except AttributeError: - raise cherrypy.HTTPError(405, - 'Create is not allowed for %s' % get_class_name(self)) - params = parse_request() - args = self.model_args + [params] - name = create(*args) - args = self.resource_args + [name] - res = self.resource(self.model, *args) - resp = res.get() - if 'task_id' in res.data: - cherrypy.response.status = 202 - else: - cherrypy.response.status = 201 - return resp - - def _get_resources(self): - try: - res_list = super(StoragePools, self)._get_resources() - # Append reserved pools - isos = getattr(self, ISO_POOL_NAME) - isos.lookup() - res_list.append(isos) - except AttributeError: - pass - return res_list - -class Task(Resource): - def __init__(self, model, id): - super(Task, self).__init__(model, id) - - @property - def data(self): - return {'id': self.ident, - 'status': self.info['status'], - 'message': self.info['message']} - - -class Tasks(Collection): - def __init__(self, model): - super(Tasks, self).__init__(model) - self.resource = Task - - -class DebugReports(AsyncCollection): - def __init__(self, model): - super(DebugReports, self).__init__(model) - self.resource = DebugReport - - -class DebugReport(Resource): - def __init__(self, model, ident): - super(DebugReport, self).__init__(model, ident) - self.ident = ident - self.content = DebugReportContent(model, ident) - - @property - def data(self): - return {'name': self.ident, - 'file': self.info['file'], - 'time': self.info['ctime']} - - -class Config(Resource): - def __init__(self, model, id=None): - super(Config, self).__init__(model, id) - self.capabilities = Capabilities(self.model) - self.capabilities.exposed = True - self.distros = Distros(model) - self.distros.exposed = True - - @property - def data(self): - return {'http_port': cherrypy.server.socket_port} - -class Capabilities(Resource): - def __init__(self, model, id=None): - super(Capabilities, self).__init__(model, id) - self.model = model - - @property - def data(self): - caps = ['libvirt_stream_protocols', 'qemu_stream', - 'screenshot', 'system_report_tool'] - ret = dict([(x, None) for x in caps]) - ret.update(self.model.get_capabilities()) - return ret - - -class Distro(Resource): - def __init__(self, model, ident): - super(Distro, self).__init__(model, ident) - - @property - def data(self): - return self.info - - -class Distros(Collection): - def __init__(self, model): - super(Distros, self).__init__(model) - self.resource = Distro - - -class Host(Resource): - def __init__(self, model, id=None): - super(Host, self).__init__(model, id) - self.stats = HostStats(self.model) - self.stats.exposed = True - self.uri_fmt = '/host/%s' - self.reboot = self.generate_action_handler(self, 'reboot') - self.shutdown = self.generate_action_handler(self, 'shutdown') - self.partitions = Partitions(self.model) - self.partitions.exposed = True - - @property - def data(self): - return self.info - -class HostStats(Resource): - @property - def data(self): - return self.info - -class Partitions(Collection): - def __init__(self, model): - super(Partitions, self).__init__(model) - self.resource = Partition - - -class Partition(Resource): - def __init__(self, model,id): - super(Partition, self).__init__(model,id) - - @property - def data(self): - return self.info - -class Plugins(Collection): - def __init__(self, model): - super(Plugins, self).__init__(model) - self.model = model - - @property - def data(self): - return self.info - - def get(self): - res_list = [] - try: - get_list = getattr(self.model, model_fn(self, 'get_list')) - res_list = get_list(*self.model_args) - except AttributeError: - pass - return kimchi.template.render(get_class_name(self), res_list) diff --git a/src/kimchi/root.py b/src/kimchi/root.py index 6310781..0261352 100644 --- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -26,13 +26,23 @@ import json
from kimchi import auth -from kimchi import controller from kimchi import template from kimchi.config import get_api_schema_file from kimchi.control.utils import parse_request - - -class Root(controller.Resource): +from kimchi.control.base import Resource +from kimchi.control.config import Config +from kimchi.control.debugreports import DebugReports +from kimchi.control.host import Host +from kimchi.control.interfaces import Interfaces +from kimchi.control.networks import Networks +from kimchi.control.plugins import Plugins +from kimchi.control.storagepools import StoragePools +from kimchi.control.tasks import Tasks +from kimchi.control.templates import Templates +from kimchi.control.vms import VMs + + +class Root(Resource): def __init__(self, model, dev_env): self._handled_error = ['error_page.400', 'error_page.404', 'error_page.405', 'error_page.406', @@ -45,17 +55,17 @@ class Root(controller.Resource): self._cp_config = dict([(key, self.error_development_handler) for key in self._handled_error])
- controller.Resource.__init__(self, model) - self.vms = controller.VMs(model) - self.templates = controller.Templates(model) - self.storagepools = controller.StoragePools(model) - self.interfaces = controller.Interfaces(model) - self.networks = controller.Networks(model) - self.tasks = controller.Tasks(model) - self.config = controller.Config(model) - self.host = controller.Host(model) - self.debugreports = controller.DebugReports(model) - self.plugins = controller.Plugins(model) + Resource.__init__(self, model)
consistence with other kimchi python class define It is more better: + super(Resource, self).__init__(model)
+ self.vms = VMs(model) + self.templates = Templates(model) + self.storagepools = StoragePools(model) + self.interfaces = Interfaces(model) + self.networks = Networks(model) + self.tasks = Tasks(model) + self.config = Config(model) + self.host = Host(model) + self.debugreports = DebugReports(model) + self.plugins = Plugins(model) self.api_schema = json.load(open(get_api_schema_file()))
def error_production_handler(status, message, traceback, version):
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 12/24/2013 02:41 AM, Aline Manera wrote:
--- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -26,13 +26,23 @@ import json
from kimchi import auth -from kimchi import controller from kimchi import template from kimchi.config import get_api_schema_file from kimchi.control.utils import parse_request - - -class Root(controller.Resource): +from kimchi.control.base import Resource +from kimchi.control.config import Config +from kimchi.control.debugreports import DebugReports +from kimchi.control.host import Host +from kimchi.control.interfaces import Interfaces +from kimchi.control.networks import Networks +from kimchi.control.plugins import Plugins +from kimchi.control.storagepools import StoragePools +from kimchi.control.tasks import Tasks +from kimchi.control.templates import Templates +from kimchi.control.vms import VMs not sure we can move these code to __all__ or other attribute of control/__init__.py just from kimchi.control.networks import * + + +class Root(Resource): def __init__(self, model, dev_env): self._handled_error = ['error_page.400', 'error_page.404', 'error_page.405', 'error_page.406', @@ -45,17 +55,17 @@ class Root(controller.Resource): self._cp_config = dict([(key, self.error_development_handler) for key in self._handled_error])
- controller.Resource.__init__(self, model) - self.vms = controller.VMs(model) - self.templates = controller.Templates(model) - self.storagepools = controller.StoragePools(model) - self.interfaces = controller.Interfaces(model) - self.networks = controller.Networks(model) - self.tasks = controller.Tasks(model) - self.config = controller.Config(model) - self.host = controller.Host(model) - self.debugreports = controller.DebugReports(model) - self.plugins = controller.Plugins(model) + Resource.__init__(self, model) + self.vms = VMs(model) + self.templates = Templates(model) + self.storagepools = StoragePools(model) + self.interfaces = Interfaces(model) + self.networks = Networks(model) + self.tasks = Tasks(model) + self.config = Config(model) + self.host = Host(model) + self.debugreports = DebugReports(model) + self.plugins = Plugins(model) self.api_schema = json.load(open(get_api_schema_file()))
def error_production_handler(status, message, traceback, version): --
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

2013/12/24 2:41, Aline Manera:
From: Aline Manera <alinefm@br.ibm.com>
The current implemenation put all resources implementation into controller.py file. That way this file tends to increase to infinity as more resources are added to Kimchi.
This patch splits controller module (controller.py) into small modules. So each resource will have its own controller implementation under /control It will make maintenance easier and anyone can easily identify where any resource is implemented. I would argue that most the resources defined are pretty trival and splitting these definitions into different files will add many small files which cause unnecessary maintenance. From another point of view, the real logical is in model.py, do you have plan to split the model.py file?
This patch set does not change any logic - just move classes from controller to a new module.
Aline Manera (16): Move generate_action_handler() function to Resource() class Move common functions for Resource and Collection to control/utils.py Move login() and logout() functions from controller.py to root.py Move basic controller resources to control/base.py Move all resources related to vms to control/vms.py Move all resources related to templates to control/templates.py Move all resources related to debug reports to control/debugreports.py Move all resources related to storage pools to control/storagepools.py Move all resources related to storage volume to control/storagevolumes.py Move all resources related to interfaces to control/interfaces.py Move all resources related to networks to control/networks.py Move all resources related to config to control/config.py Move all resources related to host to control/host.py Move all resources related to plugins to control/plugins.py Move all resources related to tasks to control/tasks.py Use new control modules in root.py
Makefile.am | 13 + src/kimchi/control/__init__.py | 21 + src/kimchi/control/base.py | 290 +++++++++++++ src/kimchi/control/config.py | 65 +++ src/kimchi/control/debugreports.py | 52 +++ src/kimchi/control/host.py | 61 +++ src/kimchi/control/interfaces.py | 44 ++ src/kimchi/control/networks.py | 48 +++ src/kimchi/control/plugins.py | 44 ++ src/kimchi/control/storagepools.py | 125 ++++++ src/kimchi/control/storagevolumes.py | 79 ++++ src/kimchi/control/tasks.py | 41 ++ src/kimchi/control/templates.py | 51 +++ src/kimchi/control/utils.py | 103 +++++ src/kimchi/control/vms.py | 64 +++ src/kimchi/controller.py | 755 ---------------------------------- src/kimchi/root.py | 61 ++- 17 files changed, 1147 insertions(+), 770 deletions(-) create mode 100644 src/kimchi/control/__init__.py create mode 100644 src/kimchi/control/base.py create mode 100644 src/kimchi/control/config.py create mode 100644 src/kimchi/control/debugreports.py create mode 100644 src/kimchi/control/host.py create mode 100644 src/kimchi/control/interfaces.py create mode 100644 src/kimchi/control/networks.py create mode 100644 src/kimchi/control/plugins.py create mode 100644 src/kimchi/control/storagepools.py create mode 100644 src/kimchi/control/storagevolumes.py create mode 100644 src/kimchi/control/tasks.py create mode 100644 src/kimchi/control/templates.py create mode 100644 src/kimchi/control/utils.py create mode 100644 src/kimchi/control/vms.py delete mode 100644 src/kimchi/controller.py

On 12/26/2013 02:55 PM, Shu Ming wrote:
2013/12/24 2:41, Aline Manera:
From: Aline Manera <alinefm@br.ibm.com>
The current implemenation put all resources implementation into controller.py file. That way this file tends to increase to infinity as more resources are added to Kimchi.
This patch splits controller module (controller.py) into small modules. So each resource will have its own controller implementation under /control It will make maintenance easier and anyone can easily identify where any resource is implemented. I would argue that most the resources defined are pretty trival and splitting these definitions into different files will add many small files which cause unnecessary maintenance. From another point of view, the real logical is in model.py, do you have plan to split the model.py file? Maybe we can refactor the controller-model structure, make it more better. such some mechanism to register different collections.
For src/kimchi/control/tasks.py @expose("tasks", "auth") +class Tasks(Collection): + def __init__(self, model): + super(Tasks, self).__init__(model) + self.resource = Task "expose" means Tasks will be added as an exposed attribute "tasks" of root. src/kimchi/control/__init__.py can do the register. Then we will no need to assign Tasks to Root.tasks manually. And these code in root.py can be removed. --- a/src/kimchi/root.py +++ b/src/kimchi/root.py src/kimchi/root.py +from kimchi.control.tasks import Tasks +class Root(Resource): def __init__(self, model, dev_env): + self.tasks = Tasks(model)
This patch set does not change any logic - just move classes from controller to a new module.
Aline Manera (16): Move generate_action_handler() function to Resource() class Move common functions for Resource and Collection to control/utils.py Move login() and logout() functions from controller.py to root.py Move basic controller resources to control/base.py Move all resources related to vms to control/vms.py Move all resources related to templates to control/templates.py Move all resources related to debug reports to control/debugreports.py Move all resources related to storage pools to control/storagepools.py Move all resources related to storage volume to control/storagevolumes.py Move all resources related to interfaces to control/interfaces.py Move all resources related to networks to control/networks.py Move all resources related to config to control/config.py Move all resources related to host to control/host.py Move all resources related to plugins to control/plugins.py Move all resources related to tasks to control/tasks.py Use new control modules in root.py
Makefile.am | 13 + src/kimchi/control/__init__.py | 21 + src/kimchi/control/base.py | 290 +++++++++++++ src/kimchi/control/config.py | 65 +++ src/kimchi/control/debugreports.py | 52 +++ src/kimchi/control/host.py | 61 +++ src/kimchi/control/interfaces.py | 44 ++ src/kimchi/control/networks.py | 48 +++ src/kimchi/control/plugins.py | 44 ++ src/kimchi/control/storagepools.py | 125 ++++++ src/kimchi/control/storagevolumes.py | 79 ++++ src/kimchi/control/tasks.py | 41 ++ src/kimchi/control/templates.py | 51 +++ src/kimchi/control/utils.py | 103 +++++ src/kimchi/control/vms.py | 64 +++ src/kimchi/controller.py | 755 ---------------------------------- src/kimchi/root.py | 61 ++- 17 files changed, 1147 insertions(+), 770 deletions(-) create mode 100644 src/kimchi/control/__init__.py create mode 100644 src/kimchi/control/base.py create mode 100644 src/kimchi/control/config.py create mode 100644 src/kimchi/control/debugreports.py create mode 100644 src/kimchi/control/host.py create mode 100644 src/kimchi/control/interfaces.py create mode 100644 src/kimchi/control/networks.py create mode 100644 src/kimchi/control/plugins.py create mode 100644 src/kimchi/control/storagepools.py create mode 100644 src/kimchi/control/storagevolumes.py create mode 100644 src/kimchi/control/tasks.py create mode 100644 src/kimchi/control/templates.py create mode 100644 src/kimchi/control/utils.py create mode 100644 src/kimchi/control/vms.py delete mode 100644 src/kimchi/controller.py
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
-- Sheldon Feng(???)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 12/26/2013 04:55 AM, Shu Ming wrote:
2013/12/24 2:41, Aline Manera:
From: Aline Manera <alinefm@br.ibm.com>
The current implemenation put all resources implementation into controller.py file. That way this file tends to increase to infinity as more resources are added to Kimchi.
This patch splits controller module (controller.py) into small modules. So each resource will have its own controller implementation under /control It will make maintenance easier and anyone can easily identify where any resource is implemented. I would argue that most the resources defined are pretty trival and splitting these definitions into different files will add many small files which cause unnecessary maintenance. From another point of view, the real logical is in model.py, do you have plan to split the model.py file?
Yeap! model/mockmodel will be the next step of re-factore backend task.
This patch set does not change any logic - just move classes from controller to a new module.
Aline Manera (16): Move generate_action_handler() function to Resource() class Move common functions for Resource and Collection to control/utils.py Move login() and logout() functions from controller.py to root.py Move basic controller resources to control/base.py Move all resources related to vms to control/vms.py Move all resources related to templates to control/templates.py Move all resources related to debug reports to control/debugreports.py Move all resources related to storage pools to control/storagepools.py Move all resources related to storage volume to control/storagevolumes.py Move all resources related to interfaces to control/interfaces.py Move all resources related to networks to control/networks.py Move all resources related to config to control/config.py Move all resources related to host to control/host.py Move all resources related to plugins to control/plugins.py Move all resources related to tasks to control/tasks.py Use new control modules in root.py
Makefile.am | 13 + src/kimchi/control/__init__.py | 21 + src/kimchi/control/base.py | 290 +++++++++++++ src/kimchi/control/config.py | 65 +++ src/kimchi/control/debugreports.py | 52 +++ src/kimchi/control/host.py | 61 +++ src/kimchi/control/interfaces.py | 44 ++ src/kimchi/control/networks.py | 48 +++ src/kimchi/control/plugins.py | 44 ++ src/kimchi/control/storagepools.py | 125 ++++++ src/kimchi/control/storagevolumes.py | 79 ++++ src/kimchi/control/tasks.py | 41 ++ src/kimchi/control/templates.py | 51 +++ src/kimchi/control/utils.py | 103 +++++ src/kimchi/control/vms.py | 64 +++ src/kimchi/controller.py | 755 ---------------------------------- src/kimchi/root.py | 61 ++- 17 files changed, 1147 insertions(+), 770 deletions(-) create mode 100644 src/kimchi/control/__init__.py create mode 100644 src/kimchi/control/base.py create mode 100644 src/kimchi/control/config.py create mode 100644 src/kimchi/control/debugreports.py create mode 100644 src/kimchi/control/host.py create mode 100644 src/kimchi/control/interfaces.py create mode 100644 src/kimchi/control/networks.py create mode 100644 src/kimchi/control/plugins.py create mode 100644 src/kimchi/control/storagepools.py create mode 100644 src/kimchi/control/storagevolumes.py create mode 100644 src/kimchi/control/tasks.py create mode 100644 src/kimchi/control/templates.py create mode 100644 src/kimchi/control/utils.py create mode 100644 src/kimchi/control/vms.py delete mode 100644 src/kimchi/controller.py
participants (6)
-
Aline Manera
-
Mark Wu
-
Ramon Medeiros
-
Sheldon
-
Shu Ming
-
Zhou Zheng Sheng