[PATCH 0/7 V4] Host's software update support

From: Aline Manera <alinefm@br.ibm.com> Hi all, I helped Paulo to apply the suggestions made on reviews and I am resending his patches. Following are the modifications I made. V3 -> V4 - Update API.md - fix type and add the task resource will be returned when doing an update operation - Use cherrypy.expose to expose the update() handler for PackagesUpdate(Collection) - Return a task resource while performing an update operation That way UI can provide good progress messages to the user - Use apt-get command to update Ubuntu system instead of apt binding python to be able to get the output in live time. - Adjust errors messages according refactor exception patches - Expose update tool in Capabilities that way UI can disable update button when no supported tool is found. - Add tests for test_rest.py ------------------------------------------------------------------------------ This patch set provides support to host's software update operations. To test the backend (ONLY mockmodel) execute the following commands: $ cd tests $ sudo ./run_tests.sh test_mockmodel.MockModelTests.test_packages_update To test the REST API, execute the following commands (all them are agnostic of the host's distro): 1) Get list of all packages to be updated in the host: $ curl -H 'Content-type: application/json' -H 'Accept: application/json' http://localhost:8000/host/packagesupdate/ -X GET 2) Update the host system: $ curl -H 'Content-type: application/json' -H 'Accept: application/json' http://localhost:8000/host/packagesupdate/update -X POST -d '' V2 -> V3: * Fixed indent and wrong bullets on docs/API.md * Changed to use Collection and Resource * Removed unnecessary 'exposed' * PEP8 compatibility on mockmodel.py * Removed debug prints from swupdate.py * Fixed indent in swupdate.py * Usage of utils.run_command() to execute Zypper update * Updated swupdate.py and mockmodel.py to make consistent with docs/API.md * Usage of Task element in mockmodel.py * Created test case in test_mockmodel.py V1 -> V2: * rebased to use new model framework * added SLES as Zypper supported distro * PEP8 compatibility * changed import sentences * use of run_command() from utils.py to execute shell commands * usage of Task element to return update status V1: At this point, an agnostic class is providing support to backend and REST API operations. In addition, YUM (for RHEL and Fedora), APT (for Debian and Ubuntu) and ZYPPER (for OpenSuse) specific classes are provided to support the operation os each software update system. Aline Manera (2): host update: Update po files host update: Expose update tool Paulo Vital (5): Host's software update: Update API.md Host's software update: Update REST API Host's software update: Update backend. Host's software update: Update Makefile Host's software update: Update test cases. Makefile.am | 1 + docs/API.md | 35 +++++++ po/en_US.po | 29 +++++- po/kimchi.pot | 29 +++++- po/pt_BR.po | 29 +++++- po/zh_CN.po | 29 +++++- src/kimchi/Makefile.am | 1 + src/kimchi/control/host.py | 29 ++++++ src/kimchi/i18n.py | 5 + src/kimchi/mockmodel.py | 51 +++++++++ src/kimchi/model/config.py | 10 +- src/kimchi/model/host.py | 45 +++++++- src/kimchi/swupdate.py | 247 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_mockmodel.py | 15 +++ tests/test_rest.py | 28 +++++ 15 files changed, 577 insertions(+), 6 deletions(-) create mode 100644 src/kimchi/swupdate.py -- 1.7.10.4

From: Paulo Vital <pvital@linux.vnet.ibm.com> Define get and POST action update API for software update resource. Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> --- docs/API.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/API.md b/docs/API.md index 48a293f..9b4e2fd 100644 --- a/docs/API.md +++ b/docs/API.md @@ -744,3 +744,34 @@ Contains the host sample data. * size: The total size of the partition, in bytes * mountpoint: If the partition is mounted, represents the mountpoint. Otherwise blank. + +### Collection: Host Packages Update + +**URI:** /host/packagesupdate + +Contains the information and action of packages update in the host. + +**Methods:** + +* **GET**: Retrieves a list of all packages to be updated in the host: + +* **POST**: *See Software Update Actions* + +**Actions (POST):** + +* update: Start the update of packages in background and return a Task resource + * task resource. * See Resource: Task * + +### Resource: Host Package Update + +**URI:** /host/packagesupdate/*:name* + +Contains the information for a specific package to be updated. + +**Methods:** + +* **GET**: Retrieves a full description of a package: + * package_name: The name of the package to be updated + * arch: The architecture of the package + * version: The new version of the package + * repository: The repository name from where package will be downloaded -- 1.7.10.4

On 02/13/2014 10:07 PM, Aline Manera wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
Define get and POST action update API for software update resource.
Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> --- docs/API.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
diff --git a/docs/API.md b/docs/API.md index 48a293f..9b4e2fd 100644 --- a/docs/API.md +++ b/docs/API.md @@ -744,3 +744,34 @@ Contains the host sample data. * size: The total size of the partition, in bytes * mountpoint: If the partition is mounted, represents the mountpoint. Otherwise blank. + +### Collection: Host Packages Update + +**URI:** /host/packagesupdate + +Contains the information and action of packages update in the host. + +**Methods:** + +* **GET**: Retrieves a list of all packages to be updated in the host: + +* **POST**: *See Software Update Actions* + +**Actions (POST):** + +* update: Start the update of packages in background and return a Task resource + * task resource. * See Resource: Task * This collection is different from other collections. It support an action on collection.
+ +### Resource: Host Package Update + +**URI:** /host/packagesupdate/*:name* + +Contains the information for a specific package to be updated. + +**Methods:** + +* **GET**: Retrieves a full description of a package: + * package_name: The name of the package to be updated + * arch: The architecture of the package + * version: The new version of the package + * repository: The repository name from where package will be downloaded
-- Thanks and best regards! Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 02/17/2014 03:46 PM, Sheldon wrote:
On 02/13/2014 10:07 PM, Aline Manera wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
Define get and POST action update API for software update resource.
Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> --- docs/API.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
diff --git a/docs/API.md b/docs/API.md index 48a293f..9b4e2fd 100644 --- a/docs/API.md +++ b/docs/API.md @@ -744,3 +744,34 @@ Contains the host sample data. * size: The total size of the partition, in bytes * mountpoint: If the partition is mounted, represents the mountpoint. Otherwise blank. + +### Collection: Host Packages Update + +**URI:** /host/packagesupdate + +Contains the information and action of packages update in the host. + +**Methods:** + +* **GET**: Retrieves a list of all packages to be updated in the host: + +* **POST**: *See Software Update Actions* + +**Actions (POST):** + +* update: Start the update of packages in background and return a Task resource + * task resource. * See Resource: Task * This collection is different from other collections. It support an action on collection.
Yes, sounds somewhat confusing. The URI /host/packagesupdate/update is in resource format and I thought it is a package named "update". POST /host/packagesupdate/update is supposed to create some property to package "update". We've seen other product defining actions to a collection using predefined keywords as: /host/packagesupdate/ALL F.Y.I.
+ +### Resource: Host Package Update + +**URI:** /host/packagesupdate/*:name* + +Contains the information for a specific package to be updated. + +**Methods:** + +* **GET**: Retrieves a full description of a package: + * package_name: The name of the package to be updated + * arch: The architecture of the package + * version: The new version of the package + * repository: The repository name from where package will be downloaded

On 02/17/2014 05:01 AM, Hongliang Wang wrote:
On 02/17/2014 03:46 PM, Sheldon wrote:
On 02/13/2014 10:07 PM, Aline Manera wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
Define get and POST action update API for software update resource.
Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> --- docs/API.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
diff --git a/docs/API.md b/docs/API.md index 48a293f..9b4e2fd 100644 --- a/docs/API.md +++ b/docs/API.md @@ -744,3 +744,34 @@ Contains the host sample data. * size: The total size of the partition, in bytes * mountpoint: If the partition is mounted, represents the mountpoint. Otherwise blank. + +### Collection: Host Packages Update + +**URI:** /host/packagesupdate + +Contains the information and action of packages update in the host. + +**Methods:** + +* **GET**: Retrieves a list of all packages to be updated in the host: + +* **POST**: *See Software Update Actions* + +**Actions (POST):** + +* update: Start the update of packages in background and return a Task resource + * task resource. * See Resource: Task * This collection is different from other collections. It support an action on collection.
Yes, sounds somewhat confusing.
The URI /host/packagesupdate/update is in resource format and I thought it is a package named "update".
POST /host/packagesupdate/update is supposed to create some property to package "update".
We've seen other product defining actions to a collection using predefined keywords as:
/host/packagesupdate/ALL
F.Y.I.
How about have the update action in host resource? Which mean we will update all packages marked to update in host POST /host/update # update host system packages GET /host/packagesupdate # list the packages marked to update GET /host/packagesupdate/<id> # list the package info
+ +### Resource: Host Package Update + +**URI:** /host/packagesupdate/*:name* + +Contains the information for a specific package to be updated. + +**Methods:** + +* **GET**: Retrieves a full description of a package: + * package_name: The name of the package to be updated + * arch: The architecture of the package + * version: The new version of the package + * repository: The repository name from where package will be downloaded
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 02/17/2014 10:23 AM, Aline Manera wrote:
On 02/17/2014 05:01 AM, Hongliang Wang wrote:
On 02/17/2014 03:46 PM, Sheldon wrote:
On 02/13/2014 10:07 PM, Aline Manera wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
Define get and POST action update API for software update resource.
Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> --- docs/API.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
diff --git a/docs/API.md b/docs/API.md index 48a293f..9b4e2fd 100644 --- a/docs/API.md +++ b/docs/API.md @@ -744,3 +744,34 @@ Contains the host sample data. * size: The total size of the partition, in bytes * mountpoint: If the partition is mounted, represents the mountpoint. Otherwise blank. + +### Collection: Host Packages Update + +**URI:** /host/packagesupdate + +Contains the information and action of packages update in the host. + +**Methods:** + +* **GET**: Retrieves a list of all packages to be updated in the host: + +* **POST**: *See Software Update Actions* + +**Actions (POST):** + +* update: Start the update of packages in background and return a Task resource + * task resource. * See Resource: Task * This collection is different from other collections. It support an action on collection.
Yes, sounds somewhat confusing.
The URI /host/packagesupdate/update is in resource format and I thought it is a package named "update".
POST /host/packagesupdate/update is supposed to create some property to package "update".
We've seen other product defining actions to a collection using predefined keywords as:
/host/packagesupdate/ALL
F.Y.I.
How about have the update action in host resource? Which mean we will update all packages marked to update in host
POST /host/update # update host system packages GET /host/packagesupdate # list the packages marked to update GET /host/packagesupdate/<id> # list the package info
I will send a patch for it
+ +### Resource: Host Package Update + +**URI:** /host/packagesupdate/*:name* + +Contains the information for a specific package to be updated. + +**Methods:** + +* **GET**: Retrieves a full description of a package: + * package_name: The name of the package to be updated + * arch: The architecture of the package + * version: The new version of the package + * repository: The repository name from where package will be downloaded
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

From: Paulo Vital <pvital@linux.vnet.ibm.com> Define PackageUpdate resource features according to API.md Activate auth support to new resource Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/control/host.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py index 053c822..b4dd544 100644 --- a/src/kimchi/control/host.py +++ b/src/kimchi/control/host.py @@ -23,8 +23,12 @@ # 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.utils import UrlSubNode +from kimchi.exception import OperationFailed +from kimchi.template import render @UrlSubNode("host", True) @@ -36,6 +40,7 @@ class Host(Resource): self.shutdown = self.generate_action_handler('shutdown') self.stats = HostStats(self.model) self.partitions = Partitions(self.model) + self.packagesupdate = PackagesUpdate(self.model) @property def data(self): @@ -61,3 +66,27 @@ class Partition(Resource): @property def data(self): return self.info + + +class PackagesUpdate(Collection): + def __init__(self, model): + super(PackagesUpdate, self).__init__(model) + self.resource = PackageUpdate + + @cherrypy.expose + def update(self): + try: + task = self.model.packagesupdate_update() + cherrypy.response.status = 202 + return render("Task", task) + except OperationFailed, e: + raise cherrypy.HTTPError(500, e.message) + + +class PackageUpdate(Resource): + def __init__(self, model, id=None): + super(PackageUpdate, self).__init__(model, id) + + @property + def data(self): + return self.info -- 1.7.10.4

From: Paulo Vital <pvital@linux.vnet.ibm.com>
Define PackageUpdate resource features according to API.md Activate auth support to new resource
Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/control/host.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py index 053c822..b4dd544 100644 --- a/src/kimchi/control/host.py +++ b/src/kimchi/control/host.py @@ -23,8 +23,12 @@ # 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.utils import UrlSubNode +from kimchi.exception import OperationFailed +from kimchi.template import render
@UrlSubNode("host", True) @@ -36,6 +40,7 @@ class Host(Resource): self.shutdown = self.generate_action_handler('shutdown') self.stats = HostStats(self.model) self.partitions = Partitions(self.model) + self.packagesupdate = PackagesUpdate(self.model)
@property def data(self): @@ -61,3 +66,27 @@ class Partition(Resource): @property def data(self): return self.info + + +class PackagesUpdate(Collection): + def __init__(self, model): + super(PackagesUpdate, self).__init__(model) + self.resource = PackageUpdate + + @cherrypy.expose Oh, from your document, +**Actions (POST):**
On 02/13/2014 10:07 PM, Aline Manera wrote: + +* update: Start the update of packages in background and return a Task resource + * task resource. * See Resource: Task * This should be a POST method. Now it support all method: DELETE,POST,PUT,GET. you can have a try # curl -u <user> -H 'Content-type: application/json' -H 'Accept: application/json' http://localhost:8000/host/packagesupdate/update -x DELETE
+ def update(self): + try: + task = self.model.packagesupdate_update() + cherrypy.response.status = 202 + return render("Task", task) + except OperationFailed, e: + raise cherrypy.HTTPError(500, e.message) + + +class PackageUpdate(Resource): + def __init__(self, model, id=None): + super(PackageUpdate, self).__init__(model, id) + + @property + def data(self): + return self.info
-- Thanks and best regards! Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

From: Paulo Vital <pvital@linux.vnet.ibm.com> Update model and mockmodel to support backend opertions. Add new file implementing backend operations, with four new classes: 1) SoftwareUpdate (object): Class to represent and operate with OS software update system in Kimchi's perspective. It's agnostic to host's package management system, and can execute all operations necessary: get all packages to update, get information about one package and execute the update. This class will load in runtime the necessary classes to work with the host's package management: YumUpdate for YUM systems based, AptUpdate for APT systems based and ZypperUpdate for Zypper systems based. 2) YumUpdate (object): Class to represent and operate with YUM. Loaded only on those systems that supports YUM, it's responsible to connect and collect information of the packages to be updated. Also it's responsible to execute the update of the system. 3) AptUpdate (object): Class to represent and operate with APT. Loaded only on those systems that supports APT, it's responsible to connect and collect information of the packages to be updated. Also it's responsible to execute the update of the system. 4) ZypperUpdate (object): Class to represent and operate with Zypper. Loaded only on those systems that supports Zypper, it's responsible to connect and collect information of the packages to be updated. Also it's responsible to execute the update of the system. Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- src/kimchi/i18n.py | 5 + src/kimchi/mockmodel.py | 51 ++++++++++ src/kimchi/model/host.py | 45 ++++++++- src/kimchi/swupdate.py | 247 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 src/kimchi/swupdate.py diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 03d1052..d0a269e 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -176,6 +176,11 @@ messages = { "KCHHOST0001E": _("Unable to shutdown host machine as there are running virtual machines"), "KCHHOST0002E": _("Unable to reboot host machine as there are running virtual machines"), + "KCHPKGUPD0001E": _("No packages marked for update"), + "KCHPKGUPD0002E": _("Package %(name)s is not marked to be updated."), + "KCHPKGUPD0003E": _("Error while getting packages marked to be updated. Details: %(err)s"), + "KCHPKGUPD0004E": _("There is no compatible package manager for this system."), + "KCHOBJST0001E": _("Unable to find %(item)s in datastore"), "KCHUTILS0001E": _("Invalid URI %(uri)s"), diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index be0ae4b..73ae970 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -75,6 +75,7 @@ class MockModel(object): self._mock_storagepools = {'default': MockStoragePool('default')} self._mock_networks = {'default': MockNetwork('default')} self._mock_interfaces = self.dummy_interfaces() + self._mock_swupdate = MockSoftwareUpdate() self.next_taskid = 1 self.storagepool_activate('default') @@ -685,6 +686,16 @@ class MockModel(object): 'display_proxy_port': kconfig.get('display', 'display_proxy_port')} + def packagesupdate_get_list(self): + return self._mock_swupdate.getUpdates() + + def packageupdate_lookup(self, pkg_name): + return self._mock_swupdate.getUpdate(pkg_name) + + def packagesupdate_update(self, args=None): + task_id = self.add_task('', self._mock_swupdate.doUpdate, None) + return self.task_lookup(task_id) + class MockVMTemplate(VMTemplate): def __init__(self, args, mockmodel_inst=None): @@ -848,6 +859,46 @@ class MockVMScreenshot(VMScreenshot): image.save(thumbnail) +class MockSoftwareUpdate(object): + def __init__(self): + self._packages = { + 'udevmountd': {'repository': 'openSUSE-13.1-Update', + 'version': '0.81.5-14.1', + 'arch': 'x86_64', + 'package_name': 'udevmountd'}, + 'sysconfig-network': {'repository': 'openSUSE-13.1-Extras', + 'version': '0.81.5-14.1', + 'arch': 'x86_64', + 'package_name': 'sysconfig-network'}, + 'libzypp': {'repository': 'openSUSE-13.1-Update', + 'version': '13.9.0-10.1', + 'arch': 'noarch', + 'package_name': 'libzypp'}} + self._num2update = 3 + + def getUpdates(self): + return self._packages.keys() + + def getUpdate(self, name): + if name not in self._packages.keys(): + raise NotFoundError('KCHPKGUPD0002E', {'name': name}) + return self._packages[name] + + def getNumOfUpdates(self): + return self._num2update + + def doUpdate(self, cb, params): + msgs = [] + for pkg in self._packages.keys(): + msgs.append("Updating package %s" % pkg) + cb('\n'.join(msgs)) + time.sleep(1) + + time.sleep(2) + msgs.append("All packages updated") + cb('\n'.join(msgs), True) + + def get_mock_environment(): model = MockModel() for i in xrange(5): diff --git a/src/kimchi/model/host.py b/src/kimchi/model/host.py index 80f93db..b078b7b 100644 --- a/src/kimchi/model/host.py +++ b/src/kimchi/model/host.py @@ -32,8 +32,10 @@ from kimchi import disks from kimchi import netinfo from kimchi.basemodel import Singleton from kimchi.exception import NotFoundError, OperationFailed +from kimchi.model.tasks import TaskModel from kimchi.model.vms import DOM_STATE_MAP -from kimchi.utils import kimchi_log +from kimchi.swupdate import SoftwareUpdate +from kimchi.utils import add_task, kimchi_log HOST_STATS_INTERVAL = 1 @@ -201,3 +203,44 @@ class PartitionModel(object): raise NotFoundError("KCHPART0001E", {'name': name}) return disks.get_partition_details(name) + + +class PackagesUpdateModel(object): + def __init__(self, **kargs): + + self.objstore = kargs['objstore'] + self.task = TaskModel(**kargs) + + def get_list(self): + return self.host_swupdate.getUpdates() + + def update(self, **kargs): + try: + swupdate = SoftwareUpdate() + except Exception: + raise OperationFailed('KCHPKGUPD0004E') + + try: + pkgs = swupdate.getNumOfUpdates() + except OperationFailed, e: + raise e + + if pkgs == 0: + raise OperationFailed('KCHPKGUPD0001E') + + kimchi_log.debug('Host is going to be updated.') + taskid = add_task('', swupdate.doUpdate, self.objstore, None) + return self.task.lookup(taskid) + + +class PackageUpdateModel(object): + def __init__(self, **kargs): + pass + + def lookup(self, name): + try: + swupdate = SoftwareUpdate() + except Exception: + raise OperationFailed('KCHPKGUPD0004E') + + return swupdate.getUpdate(name) diff --git a/src/kimchi/swupdate.py b/src/kimchi/swupdate.py new file mode 100644 index 0000000..70855dc --- /dev/null +++ b/src/kimchi/swupdate.py @@ -0,0 +1,247 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# Authors: +# Paulo Vital <pvital@linux.vnet.ibm.com> +# Ramon Medeiros <ramonn@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 platform +import subprocess +import time + +from kimchi.basemodel import Singleton +from kimchi.exception import NotFoundError, OperationFailed +from kimchi.utils import kimchi_log, run_command + +YUM_DISTROS = ['fedora', 'red hat enterprise linux', + 'red hat enterprise linux server'] +APT_DISTROS = ['debian', 'ubuntu'] +ZYPPER_DISTROS = ['opensuse ', 'suse linux enterprise server '] + + +class SoftwareUpdate(object): + __metaclass__ = Singleton + + """ + Class to represent and operate with OS software update. + """ + def __init__(self): + # This stores all packages to be updated for Kimchi perspective. It's a + # dictionary of dictionaries, in the format {'package_name': package}, + # where: + # package = {'package_name': <string>, 'version': <string>, + # 'arch': <string>, 'repository': <string> + # } + self._packages = {} + + # This stores the number of packages to update + self._num2update = 0 + + # Get the distro of host machine and creates an object related to + # correct package management system + self._distro = platform.linux_distribution()[0].lower() + if (self._distro in YUM_DISTROS): + kimchi_log.info("Loading YumUpdate features.") + self._pkg_mnger = YumUpdate() + elif (self._distro in APT_DISTROS): + kimchi_log.info("Loading AptUpdate features.") + self._pkg_mnger = AptUpdate() + elif (self._distro in ZYPPER_DISTROS): + kimchi_log.info("Loading ZypperUpdate features.") + self._pkg_mnger = ZypperUpdate() + else: + raise Exception("There is no compatible package manager for " + "this system.") + + def _scanUpdates(self): + """ + Update self._packages with packages to be updated. + """ + self._packages = {} + self._num2update = 0 + + # Call system pkg_mnger to get the packages as list of dictionaries. + for pkg in self._pkg_mnger.getPackagesList(): + + # Check if already exist a package in self._packages + pkg_id = pkg.get('package_name') + if pkg_id in self._packages.keys(): + # package already listed to update. do nothing + continue + + # Update the self._packages and self._num2update + self._packages[pkg_id] = pkg + self._num2update = self._num2update + 1 + + def getUpdates(self): + """ + Return the self._packages. + """ + self._scanUpdates() + return self._packages + + def getUpdate(self, name): + """ + Return a dictionary with all info from a given package name. + """ + if not name in self._packages.keys(): + raise NotFoundError('KCHPKGUPD0002E', {'name': name}) + + return self._packages[name] + + def getNumOfUpdates(self): + """ + Return the number of packages to be updated. + """ + self._scanUpdates() + return self._num2update + + def doUpdate(self, cb, params): + """ + Execute the update + """ + cmd = self._pkg_mnger.update_cmd + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + msgs = [] + while proc.poll() is None: + msgs.append(proc.stdout.read()) + cb('\n'.join(msgs)) + time.sleep(0.5) + + retcode = proc.poll() + if retcode == 0: + return cb('\n'.join(msgs), True) + + msgs.append(proc.stderr.read()) + return cb('\n'.join(msgs), False) + + +class YumUpdate(object): + """ + Class to represent and operate with YUM software update system. + It's loaded only on those systems listed at YUM_DISTROS and loads necessary + modules in runtime. + """ + def __init__(self): + self._pkgs = {} + self._yb = getattr(__import__('yum'), 'YumBase')() + self.update_cmd = ["yum", "-y", "update"] + + def _refreshUpdateList(self): + """ + Update the list of packages to be updated in the system. + """ + self._pkgs = self._yb.doPackageLists('updates') + + def getPackagesList(self): + """ + Return a list of package's dictionaries. Each dictionary contains the + information about a package, in the format: + package = {'package_name': <string>, 'version': <string>, + 'arch': <string>, 'repository': <string>} + """ + self._refreshUpdateList() + pkg_list = [] + for pkg in self._pkgs: + package = {'package_name': pkg.name, + 'version': "%s-%s" % (pkg.version, pkg.release), + 'arch': pkg.arch, 'repository': pkg.ui_from_repo} + pkg_list.append(package) + return pkg_list + + +class AptUpdate(object): + """ + Class to represent and operate with APT software update system. + It's loaded only on those systems listed at APT_DISTROS and loads necessary + modules in runtime. + """ + def __init__(self): + self._pkgs = {} + self._apt_cache = getattr(__import__('apt'), 'Cache')() + self.update_cmd = ['apt-get', 'upgrade', '-y'] + + def _refreshUpdateList(self): + """ + Update the list of packages to be updated in the system. + """ + self._apt_cache.update() + self._apt_cache.upgrade() + self._pkgs = self._apt_cache.get_changes() + + def getPackagesList(self): + """ + Return a list of package's dictionaries. Each dictionary contains the + information about a package, in the format + package = {'package_name': <string>, 'version': <string>, + 'arch': <string>, 'repository': <string>} + """ + self._refreshUpdateList() + pkg_list = [] + for pkg in self._pkgs: + package = {'package_name': pkg.shortname, + 'version': pkg.candidate.version, + 'arch': pkg.architecture(), + 'repository': pkg.candidate.origins[0].label} + pkg_list.append(package) + return pkg_list + + +class ZypperUpdate(object): + """ + Class to represent and operate with Zypper software update system. + It's loaded only on those systems listed at ZYPPER_DISTROS and loads + necessary modules in runtime. + """ + def __init__(self): + self._pkgs = {} + self.update_cmd = ["zypper", "--non-interactive", "update", + "--auto-agree-with-licenses"] + + def _refreshUpdateList(self): + """ + Update the list of packages to be updated in the system. + """ + self._pkgs = {} + cmd = ["zypper", "list-updates"] + (stdout, stderr, returncode) = run_command(cmd) + + if len(stderr) > 0: + raise OperationFailed('KCHPKGUPD0003E', {'err': stderr}) + + for line in stdout.split('\n'): + if line.find('v |') >= 0: + info = line.split(' | ') + package = {'package_name': info[2], 'version': info[4], + 'arch': info[5], 'repository': info[1]} + self._pkgs[info[2]] = package + + def getPackagesList(self): + """ + Return a list of package's dictionaries. Each dictionary contains the + information about a package, in the format + package = {'package_name': <string>, 'version': <string>, + 'arch': <string>, 'repository': <string>} + """ + self._refreshUpdateList() + pkg_list = [] + for pkg in self._pkgs: + pkg_list.append(pkg) + return pkg_list -- 1.7.10.4

From: Paulo Vital <pvital@linux.vnet.ibm.com> Update Makefile to provide software update support Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> Signed-off-by: Ramon Medeiros <ramonn@linux.vnet.ibm.com> --- Makefile.am | 1 + src/kimchi/Makefile.am | 1 + 2 files changed, 2 insertions(+) diff --git a/Makefile.am b/Makefile.am index 266f78f..f89dcf2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,6 +57,7 @@ PEP8_WHITELIST = \ src/kimchi/rollbackcontext.py \ src/kimchi/root.py \ src/kimchi/server.py \ + src/kimchi/swupdate.py \ src/kimchi/utils.py \ tests/test_config.py.in \ tests/test_mockmodel.py \ diff --git a/src/kimchi/Makefile.am b/src/kimchi/Makefile.am index 51a496e..c8823fa 100644 --- a/src/kimchi/Makefile.am +++ b/src/kimchi/Makefile.am @@ -47,6 +47,7 @@ kimchi_PYTHON = \ screenshot.py \ server.py \ sslcert.py \ + swupdate.py \ template.py \ utils.py \ vmtemplate.py \ -- 1.7.10.4

From: Paulo Vital <pvital@linux.vnet.ibm.com> Added unit tests into test_mockmodel.py Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> --- tests/test_mockmodel.py | 15 +++++++++++++++ tests/test_rest.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/tests/test_mockmodel.py b/tests/test_mockmodel.py index 29413a9..b985fe0 100644 --- a/tests/test_mockmodel.py +++ b/tests/test_mockmodel.py @@ -152,3 +152,18 @@ class MockModelTests(unittest.TestCase): self.assertEquals(stats_keys, set(eval(info['stats']).keys())) self.assertEquals('vnc', info['graphics']['type']) self.assertEquals('0.0.0.0', info['graphics']['listen']) + + def test_packages_update(self): + pkgs = model.packagesupdate_get_list() + self.assertEquals(3, len(pkgs)) + + for pkg_name in pkgs: + pkgupdate = model.packageupdate_lookup(pkg_name) + self.assertIn('package_name', pkgupdate.keys()) + self.assertIn('repository', pkgupdate.keys()) + self.assertIn('arch', pkgupdate.keys()) + self.assertIn('version', pkgupdate.keys()) + + task = model.packagesupdate_update() + task_params = [u'id', u'message', u'status', u'target_uri'] + self.assertEquals(sorted(task_params), sorted(task.keys())) diff --git a/tests/test_rest.py b/tests/test_rest.py index b72ff0b..69702a9 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1300,6 +1300,34 @@ class RestTests(unittest.TestCase): self.assertIn('net_recv_rate', stats) self.assertIn('net_sent_rate', stats) + def test_packages_update(self): + resp = self.request('/host/packagesupdate', None, 'GET') + pkgs = json.loads(resp.read()) + self.assertEquals(3, len(pkgs)) + + for p in pkgs: + name = p['package_name'] + resp = self.request('/host/packagesupdate/' + name, None, 'GET') + info = json.loads(resp.read()) + self.assertIn('package_name', info.keys()) + self.assertIn('repository', info.keys()) + self.assertIn('arch', info.keys()) + self.assertIn('version', info.keys()) + + resp = self.request('/host/packagesupdate/update', '{}', 'POST') + task = json.loads(resp.read()) + task_params = [u'id', u'message', u'status', u'target_uri'] + self.assertEquals(sorted(task_params), sorted(task.keys())) + + resp = self.request('/tasks/' + task[u'id'], None, 'GET') + task_info = json.loads(resp.read()) + self.assertEquals(task_info['status'], 'running') + time.sleep(6) + resp = self.request('/tasks/' + task[u'id'], None, 'GET') + task_info = json.loads(resp.read()) + self.assertEquals(task_info['status'], 'finished') + self.assertIn(u'All packages updated', task_info['message']) + def test_get_param(self): req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'}) self.request('/templates', req, 'POST') -- 1.7.10.4

From: Aline Manera <alinefm@br.ibm.com> Update po files as new messages were added to support host software update. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- po/en_US.po | 29 ++++++++++++++++++++++++++++- po/kimchi.pot | 29 ++++++++++++++++++++++++++++- po/pt_BR.po | 29 ++++++++++++++++++++++++++++- po/zh_CN.po | 29 ++++++++++++++++++++++++++++- 4 files changed, 112 insertions(+), 4 deletions(-) diff --git a/po/en_US.po b/po/en_US.po index 5a5327a..2e195e6 100644 --- a/po/en_US.po +++ b/po/en_US.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-13 00:11-0200\n" "PO-Revision-Date: 2013-07-11 17:32-0400\n" "Last-Translator: Crístian Viana <vianac@linux.vnet.ibm.com>\n" "Language-Team: English\n" @@ -909,6 +909,19 @@ msgid "Specify name and type to create a storage pool" msgstr "" #, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr "" @@ -1059,6 +1072,20 @@ msgstr "" msgid "Unable to reboot host machine as there are running virtual machines" msgstr "" +msgid "No packages marked for update" +msgstr "" + +#, python-format +msgid "Package %(name)s is not marked to be updated." +msgstr "" + +#, python-format +msgid "Error while getting packages marked to be updated. Details: %(err)s" +msgstr "" + +msgid "There is no compatible package manager for this system." +msgstr "" + #, python-format msgid "Unable to find %(item)s in datastore" msgstr "" diff --git a/po/kimchi.pot b/po/kimchi.pot index a4b3935..87c015a 100755 --- a/po/kimchi.pot +++ b/po/kimchi.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-13 00:11-0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -894,6 +894,19 @@ msgid "Specify name and type to create a storage pool" msgstr "" #, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr "" @@ -1044,6 +1057,20 @@ msgstr "" msgid "Unable to reboot host machine as there are running virtual machines" msgstr "" +msgid "No packages marked for update" +msgstr "" + +#, python-format +msgid "Package %(name)s is not marked to be updated." +msgstr "" + +#, python-format +msgid "Error while getting packages marked to be updated. Details: %(err)s" +msgstr "" + +msgid "There is no compatible package manager for this system." +msgstr "" + #, python-format msgid "Unable to find %(item)s in datastore" msgstr "" diff --git a/po/pt_BR.po b/po/pt_BR.po index adf291a..4a08b49 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-13 00:11-0200\n" "PO-Revision-Date: 2013-06-27 10:48+0000\n" "Last-Translator: Crístian Viana <vianac@linux.vnet.ibm.com>\n" "Language-Team: Aline Manera <alinefm@br.ibm.com>\n" @@ -924,6 +924,19 @@ msgid "Specify name and type to create a storage pool" msgstr "" #, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr "" @@ -1074,6 +1087,20 @@ msgstr "" msgid "Unable to reboot host machine as there are running virtual machines" msgstr "" +msgid "No packages marked for update" +msgstr "" + +#, python-format +msgid "Package %(name)s is not marked to be updated." +msgstr "" + +#, python-format +msgid "Error while getting packages marked to be updated. Details: %(err)s" +msgstr "" + +msgid "There is no compatible package manager for this system." +msgstr "" + #, python-format msgid "Unable to find %(item)s in datastore" msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index 88ef9ed..59f9cc2 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-13 00:11-0200\n" "PO-Revision-Date: 2013-06-27 10:48+0000\n" "Last-Translator: ShaoHe Feng <shaohef@linux.vnet.ibm.com>\n" "Language-Team: ShaoHe Feng <shaohef@linux.vnet.ibm.com>\n" @@ -912,6 +912,19 @@ msgid "Specify name and type to create a storage pool" msgstr "" #, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr "" @@ -1062,6 +1075,20 @@ msgstr "" msgid "Unable to reboot host machine as there are running virtual machines" msgstr "" +msgid "No packages marked for update" +msgstr "" + +#, python-format +msgid "Package %(name)s is not marked to be updated." +msgstr "" + +#, python-format +msgid "Error while getting packages marked to be updated. Details: %(err)s" +msgstr "" + +msgid "There is no compatible package manager for this system." +msgstr "" + #, python-format msgid "Unable to find %(item)s in datastore" msgstr "" -- 1.7.10.4

From: Aline Manera <alinefm@br.ibm.com> When there is no package manager compatible with the system, UI needs to know to enable/disable the host update functionality. Also update API.md accordingly and add missing parameter system_report_tool there too. Signed-off-by: Aline Manera <alinefm@br.ibm.com> --- docs/API.md | 4 ++++ src/kimchi/model/config.py | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index 9b4e2fd..3951434 100644 --- a/docs/API.md +++ b/docs/API.md @@ -542,6 +542,10 @@ creation. * screenshot: True, if libvirt stream functionality can create screenshot file without problems; False, otherwise or None if the functionality was not tested yet + * system_report_tool: True if the is some debug report tool installed on + the system; False, otherwise. + * update_tool: True if there is a compatible package manager for the + system; False, otherwise * **POST**: *See Configuration Actions* **Actions (POST):** diff --git a/src/kimchi/model/config.py b/src/kimchi/model/config.py index 9b5814a..a940a32 100644 --- a/src/kimchi/model/config.py +++ b/src/kimchi/model/config.py @@ -29,6 +29,7 @@ from kimchi.exception import NotFoundError from kimchi.featuretests import FeatureTests from kimchi.model.debugreports import DebugReportsModel from kimchi.screenshot import VMScreenshot +from kimchi.swupdate import SoftwareUpdate from kimchi.utils import kimchi_log @@ -71,11 +72,18 @@ class CapabilitiesModel(object): def lookup(self, *ident): report_tool = DebugReportsModel.get_system_report_tool() + try: + SoftwareUpdate() + except Exception: + update_tool = False + else: + update_tool = True return {'libvirt_stream_protocols': self.libvirt_stream_protocols, 'qemu_stream': self.qemu_stream, 'screenshot': VMScreenshot.get_stream_test_result(), - 'system_report_tool': bool(report_tool)} + 'system_report_tool': bool(report_tool), + 'update_tool': update_tool} class DistrosModel(object): -- 1.7.10.4

Reviewed-by: Crístian Viana <vianac@linux.vnet.ibm.com> Am 13-02-2014 12:07, schrieb Aline Manera:
From: Aline Manera <alinefm@br.ibm.com>
Hi all,
I helped Paulo to apply the suggestions made on reviews and I am resending his patches. Following are the modifications I made.
V3 -> V4 - Update API.md - fix type and add the task resource will be returned when doing an update operation - Use cherrypy.expose to expose the update() handler for PackagesUpdate(Collection) - Return a task resource while performing an update operation That way UI can provide good progress messages to the user - Use apt-get command to update Ubuntu system instead of apt binding python to be able to get the output in live time. - Adjust errors messages according refactor exception patches - Expose update tool in Capabilities that way UI can disable update button when no supported tool is found. - Add tests for test_rest.py
------------------------------------------------------------------------------ This patch set provides support to host's software update operations.
To test the backend (ONLY mockmodel) execute the following commands: $ cd tests $ sudo ./run_tests.sh test_mockmodel.MockModelTests.test_packages_update
To test the REST API, execute the following commands (all them are agnostic of the host's distro):
1) Get list of all packages to be updated in the host: $ curl -H 'Content-type: application/json' -H 'Accept: application/json' http://localhost:8000/host/packagesupdate/ -X GET
2) Update the host system: $ curl -H 'Content-type: application/json' -H 'Accept: application/json' http://localhost:8000/host/packagesupdate/update -X POST -d ''
V2 -> V3: * Fixed indent and wrong bullets on docs/API.md * Changed to use Collection and Resource * Removed unnecessary 'exposed' * PEP8 compatibility on mockmodel.py * Removed debug prints from swupdate.py * Fixed indent in swupdate.py * Usage of utils.run_command() to execute Zypper update * Updated swupdate.py and mockmodel.py to make consistent with docs/API.md * Usage of Task element in mockmodel.py * Created test case in test_mockmodel.py
V1 -> V2: * rebased to use new model framework * added SLES as Zypper supported distro * PEP8 compatibility * changed import sentences * use of run_command() from utils.py to execute shell commands * usage of Task element to return update status
V1:
At this point, an agnostic class is providing support to backend and REST API operations. In addition, YUM (for RHEL and Fedora), APT (for Debian and Ubuntu) and ZYPPER (for OpenSuse) specific classes are provided to support the operation os each software update system.
Aline Manera (2): host update: Update po files host update: Expose update tool
Paulo Vital (5): Host's software update: Update API.md Host's software update: Update REST API Host's software update: Update backend. Host's software update: Update Makefile Host's software update: Update test cases.
Makefile.am | 1 + docs/API.md | 35 +++++++ po/en_US.po | 29 +++++- po/kimchi.pot | 29 +++++- po/pt_BR.po | 29 +++++- po/zh_CN.po | 29 +++++- src/kimchi/Makefile.am | 1 + src/kimchi/control/host.py | 29 ++++++ src/kimchi/i18n.py | 5 + src/kimchi/mockmodel.py | 51 +++++++++ src/kimchi/model/config.py | 10 +- src/kimchi/model/host.py | 45 +++++++- src/kimchi/swupdate.py | 247 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_mockmodel.py | 15 +++ tests/test_rest.py | 28 +++++ 15 files changed, 577 insertions(+), 6 deletions(-) create mode 100644 src/kimchi/swupdate.py

Tested-by: Daniel Barboza <danielhb@linux.vnet.ibm.com> Reviewed-by: Daniel Barboza <danielhb@linux.vnet.ibm.com> Comment: needs a rebase. On 02/13/2014 12:07 PM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Hi all,
I helped Paulo to apply the suggestions made on reviews and I am resending his patches. Following are the modifications I made.
V3 -> V4 - Update API.md - fix type and add the task resource will be returned when doing an update operation - Use cherrypy.expose to expose the update() handler for PackagesUpdate(Collection) - Return a task resource while performing an update operation That way UI can provide good progress messages to the user - Use apt-get command to update Ubuntu system instead of apt binding python to be able to get the output in live time. - Adjust errors messages according refactor exception patches - Expose update tool in Capabilities that way UI can disable update button when no supported tool is found. - Add tests for test_rest.py
------------------------------------------------------------------------------ This patch set provides support to host's software update operations.
To test the backend (ONLY mockmodel) execute the following commands: $ cd tests $ sudo ./run_tests.sh test_mockmodel.MockModelTests.test_packages_update
To test the REST API, execute the following commands (all them are agnostic of the host's distro):
1) Get list of all packages to be updated in the host: $ curl -H 'Content-type: application/json' -H 'Accept: application/json' http://localhost:8000/host/packagesupdate/ -X GET
2) Update the host system: $ curl -H 'Content-type: application/json' -H 'Accept: application/json' http://localhost:8000/host/packagesupdate/update -X POST -d ''
V2 -> V3: * Fixed indent and wrong bullets on docs/API.md * Changed to use Collection and Resource * Removed unnecessary 'exposed' * PEP8 compatibility on mockmodel.py * Removed debug prints from swupdate.py * Fixed indent in swupdate.py * Usage of utils.run_command() to execute Zypper update * Updated swupdate.py and mockmodel.py to make consistent with docs/API.md * Usage of Task element in mockmodel.py * Created test case in test_mockmodel.py
V1 -> V2: * rebased to use new model framework * added SLES as Zypper supported distro * PEP8 compatibility * changed import sentences * use of run_command() from utils.py to execute shell commands * usage of Task element to return update status
V1:
At this point, an agnostic class is providing support to backend and REST API operations. In addition, YUM (for RHEL and Fedora), APT (for Debian and Ubuntu) and ZYPPER (for OpenSuse) specific classes are provided to support the operation os each software update system.
Aline Manera (2): host update: Update po files host update: Expose update tool
Paulo Vital (5): Host's software update: Update API.md Host's software update: Update REST API Host's software update: Update backend. Host's software update: Update Makefile Host's software update: Update test cases.
Makefile.am | 1 + docs/API.md | 35 +++++++ po/en_US.po | 29 +++++- po/kimchi.pot | 29 +++++- po/pt_BR.po | 29 +++++- po/zh_CN.po | 29 +++++- src/kimchi/Makefile.am | 1 + src/kimchi/control/host.py | 29 ++++++ src/kimchi/i18n.py | 5 + src/kimchi/mockmodel.py | 51 +++++++++ src/kimchi/model/config.py | 10 +- src/kimchi/model/host.py | 45 +++++++- src/kimchi/swupdate.py | 247 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_mockmodel.py | 15 +++ tests/test_rest.py | 28 +++++ 15 files changed, 577 insertions(+), 6 deletions(-) create mode 100644 src/kimchi/swupdate.py
participants (5)
-
Aline Manera
-
Crístian Viana
-
Daniel H Barboza
-
Hongliang Wang
-
Sheldon