PageRenderTime 131ms CodeModel.GetById 11ms app.highlight 101ms RepoModel.GetById 2ms app.codeStats 1ms

/silverlining/config.py

https://bitbucket.org/ianb/silverlining/
Python | 231 lines | 217 code | 9 blank | 5 comment | 25 complexity | acd077e5ee87bcffc0e1d1a420912c69 MD5 | raw file
  1import sys
  2import os
  3from initools.configparser import ConfigParser
  4from UserDict import UserDict
  5import re
  6import fnmatch
  7import time
  8import cPickle as pickle
  9from cmdutils import CommandError
 10from libcloud.types import Provider
 11from libcloud.providers import get_driver as libcloud_get_driver
 12from silverlining import createconf
 13from silversupport.env import local_location
 14from silversupport.appdata import normalize_location
 15
 16
 17class Config(UserDict):
 18    """Represents the configuration, command-line arguments, and
 19    provider.
 20    """
 21
 22    def __init__(self, config_dict, args=None):
 23        self.data = config_dict
 24        self._driver = None
 25        self.args = args
 26        if args:
 27            self.logger = args.logger
 28
 29    @classmethod
 30    def from_config_file(cls, filename, section, args):
 31        """Instantiate from a configuration file"""
 32        parser = ConfigParser()
 33        parser.read([filename])
 34        full_config = parser.asdict()
 35        if section not in full_config:
 36            args.logger.fatal('No section [%s]' % section)
 37            args.logger.fatal('Available sections in %s:' % filename)
 38            for name in full_config:
 39                if name.startswith('provider:'):
 40                    args.logger.fatal('  [%s] (--provider=%s)'
 41                                 % (name, name[len('provider:'):]))
 42            raise CommandError("Bad --provider=%s"
 43                               % section[len('provider:'):])
 44        config = full_config[section]
 45        config['section_name'] = section
 46        return cls(config, args=args)
 47
 48    @property
 49    def driver(self):
 50        if self._driver is None:
 51            provider = self['provider']
 52            if ':' in provider:
 53                # Then it's a module import path
 54                mod, obj_name = provider.split(':', 1)
 55                __import__(mod)
 56                mod = sys.modules[mod]
 57                DriverClass = getattr(mod, obj_name)
 58            else:
 59                DriverClass = libcloud_get_driver(getattr(Provider, provider.upper()))
 60            self._driver = DriverClass(self['username'], self['secret'])
 61            if getattr(self.args, 'debug_libcloud', False):
 62                print 'XXX', self.args.debug_libcloud, self._driver.connection.conn_classes
 63                from libcloud.base import LoggingHTTPConnection, LoggingHTTPSConnection
 64                fp = open(self.args.debug_libcloud, 'a')
 65                LoggingHTTPConnection.log = LoggingHTTPSConnection.log = fp
 66                self._driver.connection.conn_classes = (
 67                    LoggingHTTPConnection, LoggingHTTPSConnection)
 68        return self._driver
 69
 70    @property
 71    def node_hostname(self):
 72        if getattr(self.args, 'node', None):
 73            return self.args.node
 74        if getattr(self.args, 'location'):
 75            return normalize_location(self.args.location)[0]
 76        raise CommandError(
 77            "You must give a --node option")
 78
 79    def select_image(self, image_match=None, image_id=None, images=None):
 80        if images is None:
 81            images = self.cached_images()
 82        if image_id:
 83            image_match = 'id %s' % image_id
 84        if not image_match:
 85            if self.get('image_id'):
 86                image_match = 'id %s' % self['image_id']
 87            elif self.get('image_name'):
 88                image_match = 'name %s' % self['image_name']
 89            elif self.get('image'):
 90                image_match = self['image']
 91        return self._match('image', image_match, images)
 92
 93    def select_size(self, size_match=None, size_id=None, sizes=None):
 94        if sizes is None:
 95            sizes = self.cached_sizes()
 96        if size_id:
 97            size_match = 'id %s' % size_id
 98        if not size_match:
 99            if self.get('size_id'):
100                size_match = 'id %s' % self['size_id']
101            elif self.get('size'):
102                size_match = self['size']
103        return self._match('size', size_match, sizes)
104
105    def _match(self, type, matcher, items):
106        matcher = matcher or ''
107        match_pattern = matcher
108        if ' ' in matcher:
109            match_type, matcher = matcher.split(None, 1)
110        else:
111            match_type = 'name'
112        select_first = False
113        if matcher.split()[0] == 'first':
114            select_first = True
115            matcher = matcher.split(None, 1)[1]
116        if not matcher:
117            raise LookupError("No matcher available")
118        possible = []
119        for item in items:
120            if match_type == 'id':
121                if item.id == matcher:
122                    possible.append(item)
123            if match_type == 'name':
124                if '*' in matcher:
125                    if re.match(fnmatch.translate(matcher), item.name, re.I):
126                        possible.append(item)
127                else:
128                    if item.name.lower() == matcher.lower():
129                        possible.append(item)
130            if match_type == 'ram':
131                if self._softint(matcher) == self._softint(item.ram):
132                    possible.append(item)
133        if not possible:
134            raise LookupError(
135                "Could not find any %s that matches the pattern %s"
136                % (type, match_pattern))
137        if select_first:
138            if match_type == 'name':
139                possible.sort(key=lambda x: x.name.lower())
140            elif match_type == 'id':
141                possible.sort(key=lambda x: int(x.id))
142            elif match_type == 'ram':
143                possible.sort(key=lambda x: self._softint(x.ram))
144        if not select_first and len(possible) > 1:
145            raise LookupError(
146                "Multiple %ss matched the pattern %s: %s"
147                % (type, match_pattern, ', '.join(repr(i) for i in possible)))
148        return possible[0]
149
150    @staticmethod
151    def _softint(v):
152        if isinstance(v, int):
153            return v
154        v = re.sub(r'[^\d]', '', v)
155        try:
156            return int(v)
157        except ValueError:
158            return None
159
160    def ask(self, query):
161        if getattr(self.args, 'yes', False):
162            self.logger.warn(
163                "%s YES [auto]" % query)
164            return True
165        while 1:
166            response = raw_input(query+" [y/n] ")
167            response = response.strip().lower()
168            if not response:
169                continue
170            if 'all' in response and response[0] == 'y':
171                self.args.yes = True
172                return True
173            if response[0] == 'y':
174                return True
175            if response[0] == 'n':
176                return False
177            print 'I did not understand the response: %s' % response
178
179    def cache_object(self, name, expiration=None, driver=None):
180        path = local_location(name)
181        if os.path.exists(path):
182            if expiration is not None:
183                age = time.time() - os.path.getmtime(path)
184                if age > expiration:
185                    return None
186            fp = open(path, 'rb')
187            obj = pickle.load(fp)
188            fp.close()
189            if driver:
190                for item in obj:
191                    item.driver = driver
192            return obj
193        return None
194
195    def set_cache_object(self, name, obj):
196        ## FIXME: some objects are pickleable :(
197        path = local_location(name)
198        saved = {}
199        if isinstance(obj, list):
200            for item in obj:
201                if getattr(item, 'driver', None):
202                    saved[item] = item.driver
203                    item.driver = None
204        try:
205            try:
206                fp = open(path, 'wb')
207                pickle.dump(obj, fp)
208                fp.close()
209            except:
210                if os.path.exists(path):
211                    os.unlink(path)
212                raise
213        finally:
214            for item, driver in saved.iteritems():
215                item.driver = driver
216
217    def cached_images(self, expiration=None):
218        name = self['provider'] + '-images.cache'
219        obj = self.cache_object(name, expiration, driver=self.driver)
220        if obj is None:
221            obj = self.driver.list_images()
222            self.set_cache_object(name, obj)
223        return obj
224
225    def cached_sizes(self, expiration=None):
226        name = self['provider'] + '-sizes.cache'
227        obj = self.cache_object(name, expiration, driver=self.driver)
228        if obj is None:
229            obj = self.driver.list_sizes()
230            self.set_cache_object(name, obj)
231        return obj