
- The client will be able to know if there is a package manager instance running and to follow the package manager logfiles to know what is happening within. Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- src/wok/control/base.py | 18 ++++++++++++ src/wok/plugins/kimchi/control/host.py | 15 +++++++++- src/wok/plugins/kimchi/model/host.py | 16 ++++++++++ src/wok/plugins/kimchi/swupdate.py | 53 ++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 1 deletion(-) diff --git a/src/wok/control/base.py b/src/wok/control/base.py index 5c5c95f..e9ed3c8 100644 --- a/src/wok/control/base.py +++ b/src/wok/control/base.py @@ -226,6 +226,24 @@ class Resource(object): return {} +class AsyncResource(Resource): + """ + AsyncResource is a specialized Resource to handle async task. + """ + def __init__(self, model, ident=None): + super(AsyncResource, self).__init__(model, ident) + + def lookup(self): + try: + lookup = getattr(self.model, model_fn(self, 'lookup')) + self.info = lookup(*self.model_args) + except AttributeError: + self.info = {} + + cherrypy.response.status = 202 + return wok.template.render('Task', self.info) + + class Collection(object): """ A Collection is a container for Resource objects. To create a new diff --git a/src/wok/plugins/kimchi/control/host.py b/src/wok/plugins/kimchi/control/host.py index 0a40f1b..9fe4c0a 100644 --- a/src/wok/plugins/kimchi/control/host.py +++ b/src/wok/plugins/kimchi/control/host.py @@ -17,7 +17,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 -from wok.control.base import Collection, Resource, SimpleCollection +from wok.control.base import AsyncResource, Collection +from wok.control.base import Resource, SimpleCollection from wok.control.utils import UrlSubNode from wok.exception import NotFoundError @@ -39,6 +40,7 @@ class Host(Resource): self.packagesupdate = PackagesUpdate(self.model) self.repositories = Repositories(self.model) self.swupdate = self.generate_action_handler_task('swupdate') + self.swupdateprogress = SoftwareUpdateProgress(self.model) self.cpuinfo = CPUInfo(self.model) @property @@ -46,6 +48,17 @@ class Host(Resource): return self.info +class SoftwareUpdateProgress(AsyncResource): + def __init__(self, model, id=None): + super(SoftwareUpdateProgress, self).__init__(model, id) + self.role_key = 'host' + self.admin_methods = ['GET'] + + @property + def data(self): + return self.info + + class HostStats(Resource): def __init__(self, model, id=None): super(HostStats, self).__init__(model, id) diff --git a/src/wok/plugins/kimchi/model/host.py b/src/wok/plugins/kimchi/model/host.py index f32cf62..9b1fc32 100644 --- a/src/wok/plugins/kimchi/model/host.py +++ b/src/wok/plugins/kimchi/model/host.py @@ -168,6 +168,22 @@ class HostModel(object): if (DOM_STATE_MAP[dom.info()[0]]) == state] +class SoftwareUpdateProgressModel(object): + def __init__(self, **kargs): + self.task = TaskModel(**kargs) + self.objstore = kargs['objstore'] + + def lookup(self, *name): + try: + swupdate = SoftwareUpdate() + except: + raise OperationFailed('KCHPKGUPD0004E') + + taskid = add_task('/plugins/kimchi/host/swupdateprogress', + swupdate.tailUpdateLogs, self.objstore, None) + return self.task.lookup(taskid) + + class HostStatsModel(object): __metaclass__ = Singleton diff --git a/src/wok/plugins/kimchi/swupdate.py b/src/wok/plugins/kimchi/swupdate.py index 73692d5..08f3b26 100644 --- a/src/wok/plugins/kimchi/swupdate.py +++ b/src/wok/plugins/kimchi/swupdate.py @@ -123,6 +123,59 @@ class SoftwareUpdate(object): os.setsid() signal.signal(signal.SIGTERM, signal.SIG_IGN) + def tailUpdateLogs(self, cb, params): + """ + When the package manager is already running (started outside kimchi or + if wokd is restarted) we can only know what's happening by reading the + logfiles. This method acts like a 'tail -f' on the default package + manager logfile. If the logfile is not found, a simple '*' is + displayed to track progress. This will be until the process finishes. + """ + if not self._pkg_mnger.isRunning(): + return + + fd = None + try: + fd = os.open(self._pkg_mnger.logfile, os.O_RDONLY) + + # cannot open logfile, print something to let users know that the + # system is being upgrading until the package manager finishes its + # job + except (TypeError, OSError): + msgs = [] + while self._pkg_mnger.isRunning(): + msgs.append('*') + cb(''.join(msgs)) + time.sleep(1) + msgs.append('\n') + cb(''.join(msgs), True) + return + + # go to the end of logfile and starts reading, if nothing is read or + # a pattern is not found in the message just wait and retry until + # the package manager finishes + os.lseek(fd, 0, os.SEEK_END) + msgs = [] + progress = [] + while True: + read = os.read(fd, 1024) + if not read: + if not self._pkg_mnger.isRunning(): + break + + if not msgs: + progress.append('*') + cb(''.join(progress)) + + time.sleep(1) + continue + + msgs.append(read) + cb(''.join(msgs)) + + os.close(fd) + return cb(''.join(msgs), True) + def doUpdate(self, cb, params): """ Execute the update -- 1.9.1