commit:     c3b6f7c9de1bd5723cd132e2ed66a9e8e557f22a
Author:     Devan Franchini <twitch153 <AT> gentoo <DOT> org>
AuthorDate: Mon Aug 11 01:00:49 2014 +0000
Commit:     Devan Franchini <twitch153 <AT> gentoo <DOT> org>
CommitDate: Fri Aug 15 21:42:41 2014 +0000
URL:        
http://git.overlays.gentoo.org/gitweb/?p=proj/layman.git;a=commit;h=c3b6f7c9

Adds layman-mounter utility script

api.py: Adds Mounter() initialization in the LaymanAPI.
constants.py: Adds MOUNT_TYPES to constants to keep track of
mountable overlay classes.
archive.py: Adds check to unmount overlays if they are being
synced.

---
 bin/layman-mounter         |  42 ++++++
 layman/api.py              |   5 +
 layman/constants.py        |   9 +-
 layman/mounter.py          | 313 +++++++++++++++++++++++++++++++++++++++++++++
 layman/overlays/archive.py |   5 +
 5 files changed, 373 insertions(+), 1 deletion(-)

diff --git a/bin/layman-mounter b/bin/layman-mounter
new file mode 100755
index 0000000..cd41078
--- /dev/null
+++ b/bin/layman-mounter
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+################################################################################
+# LAYMAN - A UTILITY TO HANDLE MOUNTING OVERLAYS
+################################################################################
+# Distributed under the terms of the GNU General Public License v2
+#
+# Copyright:
+#             (c) 2014 Devan Franchini
+#             Distributed under the terms of the GNU General Public License v2
+#
+# Author(s):
+#             Devan Franchini <[email protected]>
+#
+
+__version__ = "0.1"
+
+#===============================================================================
+#
+# Dependencies
+#
+#-------------------------------------------------------------------------------
+
+from layman.api     import LaymanAPI
+from layman.config  import OptionConfig
+from layman.mounter import Interactive
+
+#===============================================================================
+#
+# MAIN
+#
+#-------------------------------------------------------------------------------
+
+config = OptionConfig()
+layman_api = LaymanAPI(config,
+                       report_errors=True,
+                       output=config['output'])
+
+main = Interactive(config=config, mounter=config['mounts'])
+main()
+

diff --git a/layman/api.py b/layman/api.py
index bae6972..0f43f28 100755
--- a/layman/api.py
+++ b/layman/api.py
@@ -27,6 +27,7 @@ from layman.overlays.source import require_supported
 #from layman.utils import path, delete_empty_directory
 from layman.compatibility   import encode
 from layman.utils           import verify_overlay_src
+from layman.mounter         import Mounter
 
 if sys.hexversion >= 0x30200f0:
     STR = str
@@ -68,6 +69,10 @@ class LaymanAPI(object):
         self._error_messages = []
         self.sync_results = []
 
+        self.config.set_option('mounts', Mounter(self._get_installed_db,
+                                                 self.get_installed,
+                                                 config=self.config))
+
 
     def is_repo(self, ovl):
         """validates that the ovl given is a known repo id

diff --git a/layman/constants.py b/layman/constants.py
index c526cb6..7379429 100644
--- a/layman/constants.py
+++ b/layman/constants.py
@@ -67,7 +67,6 @@ COMPONENT_DEFAULTS  = ['name', 'descriptions', 'owner', 
'type', 'sources']
 POSSIBLE_COMPONENTS = ['name', 'descriptions', 'homepage', 'owner', 'quality',
                        'priority', 'sources', 'branch', 'irc', 'feeds']
 
-
 ###############################################################################
 ##
 ## Archive overlay possible file extensions
@@ -77,3 +76,11 @@ POSSIBLE_COMPONENTS = ['name', 'descriptions', 'homepage', 
'owner', 'quality',
 FILE_EXTENSIONS = {'Tar': ('bz2', 'gz', 'lzma', 'xz', 'Z', 'tgz', 'tbz', 'taz',
                            'tlz', 'txz')
                   }
+
+###################################################################################
+##
+## Overlay types mountable by script
+##
+####################################################################################
+
+MOUNT_TYPES = []

diff --git a/layman/mounter.py b/layman/mounter.py
new file mode 100644
index 0000000..f041a9a
--- /dev/null
+++ b/layman/mounter.py
@@ -0,0 +1,313 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+###############################################################################
+# LAYMAN MOUNTING OVERLAY HANDLER
+###############################################################################
+# File:       mounter.py
+#
+#             Controls all mountable overlay types
+#
+# Copyright:
+#             (c) 2014 Devan Franchini
+#             Distributed under the terms of the GNU General Public License v2
+#            
+# Author(s):
+#             Devan Franchini <[email protected]>
+#
+'''
+Controls all mountable overlay types.
+'''
+#==============================================================================
+#
+# Dependencies
+#
+#------------------------------------------------------------------------------
+from __future__ import unicode_literals
+
+import argparse
+import copy
+import os
+import sys
+
+from  layman.constants  import MOUNT_TYPES
+from  layman.utils      import path, run_command
+from  layman.version    import VERSION
+
+
+if sys.hexversion >= 0x30200f0:
+    STR = str
+else:
+    STR = basestring
+
+MOUNT_ARGS = {'Squashfs': ['-o', 'loop', '-t', 'squashfs']}
+_USAGE = 'layman-mounter [-h] [-l] [-L] [-m MOUNT [MOUNT ...]]\n'\
+         '                      [-u UMOUNT [UMOUNT ...]] [-V]'
+
+def is_mounted(mdir):
+    '''
+    Determines whether or not an overlay is mounted at it's
+    installed overlay.
+
+    @rtype bool
+    '''
+    return os.path.ismount(mdir)
+
+
+class Mounter(object):
+    '''
+    Handles all mountable overlays.
+    '''
+    def __init__(self, database, overlays, config=None):
+        self.config = config
+        self.database = database
+        self.output = self.config['output']
+        self.overlays = overlays
+        self.storage = self.config['storage']
+
+
+    @property
+    def installed(self):
+        '''
+        Returns a dictionary of all installed overlays
+        and their overlay objects.
+
+        @rtype dict {'ovl1', <layman.overlays.Overlay object>,...}
+        '''
+        installed_db = {}
+
+        for overlay in self.overlays():
+            ovl_db = self.database().select(overlay)
+            installed_db[overlay] = ovl_db
+        return installed_db
+
+
+    @property
+    def mountables(self):
+        '''
+        Returns a dictionary of all mountable overlays and their
+        types.
+
+        @rtype dict {'ovl1': 'Squashfs',...}
+        '''
+        mountable_ovls = {}
+
+        for key in sorted(self.installed):
+            for ovl_type in self.installed[key].source_types():
+                if ovl_type in MOUNT_TYPES:
+                    mountable_ovls[key] = ovl_type
+        return mountable_ovls
+
+
+    @property
+    def mounted(self):
+        '''
+        Returns a dictionary of all mountable overlays and a
+        boolean reflecting their mounted status.
+
+        @rtype dict {'ovl1': True, 'ovl2': False,...}
+        '''
+        mounted_ovls = {}
+
+        for ovl in self.mountables:
+            mdir = path([self.storage, ovl])
+            mounted_ovls[ovl] = is_mounted(mdir)
+        return mounted_ovls
+
+
+    def _check_selection(self, repos):
+        '''
+        Internal function to validate the repo parameter.
+
+        @rtype tuple
+        '''
+        if 'ALL' in repo:
+            repos = sorted(self.mountables)
+        elif isinstance(repos, STR):
+            repos = [repos]
+
+        return repos
+
+
+    def mount(self, repo, dest=None, install=False, ovl_type=None, pkg=None):
+        '''
+        Mounts an overlay to it's installation directory.
+
+        @params repo: str of overlay name or "ALL".
+        @params dest: str of optional destination dir.
+        @params install: bool to reflect whether or not the overlay is being
+        installed.
+        @params ovl_type: str of optional overlay type.
+        @params pkg: str of optional location of package to mount.
+        @rtype int: reflects whether or not the overlay was mounted.
+        '''
+        result = 1
+
+        selection = self._check_selection(repo)
+
+        for i in selection:
+            name = {'ovl': i}
+
+            if i not in self.mountables and not install:
+                self.output.error('Overlay "%(ovl)s" cannot be mounted!'\
+                                    % name)
+                continue
+            if dest:
+                mdir = dest
+            else:
+                mdir = path([self.storage, i])
+
+            if not is_mounted(mdir):
+                if install:
+                    args = copy.deepcopy(MOUNT_ARGS[ovl_type])
+                else:
+                    args = copy.deepcopy(MOUNT_ARGS[self.mountables[i]])
+                
+                if not pkg:
+                    source = self.installed[i].sources[0].src
+
+                    if 'file://' in source:
+                        pkg = source.replace('file://', '')
+                    else:
+                        pkg = path([self.storage, i, source.get_extension()])
+
+                args.append(pkg)
+                args.append(mdir)
+                result = run_command(self.config, 'mount', args, cmd='mount')
+            else:
+                self.output.warn('Overlay "%(ovl)s" is already mounted!'\
+                                    % name)
+        return result
+
+
+    def umount(self, repo, dest=None, sync=False):
+        '''
+        Unmounts an overlay from it's installation directory.
+
+        @params repo: str of overlay name or "ALL".
+        @params dest: str of optional path to unmount.
+        @params sync: bool to reflect whether or not the overlay is being
+        synced.
+        @rtype int: reflects whether or not it was a successful unmount.
+        '''
+        result = 1
+
+        selection = self._check_selection(repo)
+            
+        for i in selection:
+            name = {'ovl': i}
+
+            if i not in self.mountables and not sync:
+                self.output.error('Overlay "%(ovl)s" cannot be mounted!'\
+                                    % name)
+                continue
+            if dest:
+                mdir = dest
+            else:
+                mdir = path([self.storage, i])
+
+            if is_mounted(mdir):
+                args = ['-l', mdir]
+                result = run_command(self.config, 'umount', args, cmd='umount')
+            else:
+                self.output.warn('Overlay "%(ovl)s" is already unmounted!'\
+                                    % name)
+
+        return result
+
+
+class Interactive(object):
+    '''
+    Interactive CLI session for the Mounter class
+    '''
+    def __init__(self, config=None, mounter=None):
+        self.args = None
+        self.parser = None
+        self.output = config['output']
+        self.storage = config['storage']
+        self.mount = mounter
+        self.mountables = self.mount.mountables
+
+
+    def args_parser(self):
+        '''
+        Parses all command line arguments.
+
+        @rtype argparse.NameSpace object.
+        '''
+        self.parser = argparse.ArgumentParser(prog='layman-mounter',
+            description='Layman\'s utility script to handle mountable '\
+                        'overlays.')
+        self.parser.add_argument('-l',
+                                 '--list-mountables',
+                                 action='store_true',
+                                 help='Lists all available overlays that'
+                                 ' support mounting')
+        self.parser.add_argument('-L',
+                                 '--list-mounted',
+                                 action='store_true',
+                                 help='Lists all mounted overlays')
+        self.parser.add_argument('-m',
+                                 '--mount',
+                                 nargs='+',
+                                 help='Mounts the selected overlay. Specify '\
+                                 '"ALL" to mount all possible overlays')
+        self.parser.add_argument('-u',
+                                 '--umount',
+                                 nargs='+',
+                                 help='Unmounts the selected overlay. Specify'\
+                                 ' "ALL" to unmount all possible overlays')
+        self.parser.add_argument('-V',
+                                 '--version',
+                                 action='version',
+                                 version='%(prog)s ' + VERSION)
+        self.args = self.parser.parse_args()
+
+
+    def __call__(self):
+        self.args_parser()
+        if len(sys.argv) == 1:
+            self.output.notice('usage: %(USAGE)s' % {'USAGE':_USAGE})
+            sys.exit(0)
+        options = {}
+
+        for key in vars(self.args):
+            options[key] = vars(self.args)[key]
+
+        for i in ('list_mountables', 'list_mounted'):
+            if options[i]:
+                getattr(self, i)()
+                self.output.notice('')
+
+        for i in ('umount', 'mount'):
+            if options[i]:
+                getattr(self.mount, '%(action)s' % {'action': i})(options[i])
+
+
+    def list_mountables(self):
+        '''
+        Lists all overlays that can be mounted.
+        '''
+        self.output.info('Mountable overlays:')
+        self.output.info('~~~~~~~~~~~~~~~~~~~')
+        if self.mountables:
+            for ovl in sorted(self.mountables):
+                self.output.info(ovl)
+        else:
+            self.output.warn('N/A')
+
+
+    def list_mounted(self):
+        '''
+        Lists all mounted overlays.
+        '''
+        mounted = self.mount.mounted
+
+        self.output.info('Overlays:')
+        self.output.info('~~~~~~~~~')
+        for i in mounted:
+            if mounted[i]:
+                status = 'Mounted'
+            else:
+                status = 'Unmounted'
+            stat_dict = {'ovl': i, 'status': status}
+            self.output.info('Name: %(ovl)s, Status: %(status)s' % stat_dict)

diff --git a/layman/overlays/archive.py b/layman/overlays/archive.py
index 68c8b47..b1907ee 100644
--- a/layman/overlays/archive.py
+++ b/layman/overlays/archive.py
@@ -98,6 +98,11 @@ class ArchiveOverlay(OverlaySource):
                 temp_path = final_path
                 if not os.path.exists(temp_path):
                     os.mkdir(temp_path)
+                else:
+                    if os.path.ismount(temp_path):
+                      self.config['mounts'].umount([self.parent.name],
+                                                   dest=temp_path,
+                                                   sync=True)
             pkg = self._fetch(base=base, archive_url=self.src,
                 dest_dir=temp_path)
             result = self.post_fetch(pkg, temp_path)

Reply via email to