PageRenderTime 84ms CodeModel.GetById 44ms app.highlight 18ms RepoModel.GetById 19ms app.codeStats 1ms

/silversupport/appdata.py

https://bitbucket.org/ianb/silverlining/
Python | 194 lines | 152 code | 16 blank | 26 comment | 54 complexity | 34613d07fd503c9d17f4a7e3d6d0ed18 MD5 | raw file
  1"""Routines for handling /var/www/appdata.map"""
  2import os
  3from silversupport.appconfig import AppConfig
  4
  5APPDATA_MAP = '/var/www/appdata.map'
  6
  7__all__ = ['add_appdata', 'normalize_location',
  8           'remove_host', 'instance_for_location', 'all_app_instances',
  9           'list_instances']
 10
 11
 12def add_appdata(app_config, locations, debug_single_process=False,
 13                add_prev=True):
 14    """Adds new application deployment to appdata.map
 15
 16    This adds the given instance name for all the given locations.  If
 17    `add_prev` is true then any old instances at these locations will
 18    be moved to a prev.* hostname
 19    """
 20    if isinstance(app_config, basestring):
 21        app_config = AppConfig.from_instance_name(app_config)
 22    if debug_single_process:
 23        process_group = 'general_debug'
 24    else:
 25        process_group = 'general'
 26    locations = [normalize_location(l) for l in locations]
 27    new_lines = rewrite_lines(appdata_lines(), locations, add_prev, dict(
 28        instance_name=app_config.instance_name, platform=app_config.platform,
 29        process_group=process_group, php_root=app_config.php_root.rstrip('/'),
 30        write_root=app_config.writable_root_location))
 31    fp = open(APPDATA_MAP, 'w')
 32    fp.writelines(new_lines)
 33    fp.close()
 34
 35
 36def rewrite_lines(existing, locations, add_prev, vars):
 37    """Rewrite the lines in appdata.map"""
 38    if isinstance(existing, basestring):
 39        existing = existing.splitlines(True)
 40    lines = []
 41    for line in existing:
 42        if not line.strip() or line.strip().startswith('#'):
 43            lines.append(line)
 44            continue
 45        ex_hostname, ex_path, ex_data = line.split(None, 2)
 46        for hostname, path in locations:
 47            if add_prev:
 48                if 'prev.' + hostname == ex_hostname and path == ex_path:
 49                    # Overwrite the old (non-prev) hostname
 50                    break
 51                elif hostname == ex_hostname and path == ex_path:
 52                    # Rewrite matching lines to prev.
 53                    lines.append('prev.' + line)
 54                    break
 55            else:
 56                if hostname == ex_hostname and path == ex_path:
 57                    # Overwrite!
 58                    break
 59        else:
 60            lines.append(line)
 61    data = '%(instance_name)s|%(process_group)s|%(write_root)s|%(platform)s|%(php_root)s' % vars
 62    for hostname, path in locations:
 63        lines.append('%s %s %s\n' % (hostname, path, data))
 64    return ''.join(lines)
 65
 66
 67def normalize_location(location, empty_path='/'):
 68    """Normalize a string location into (hostname, path).
 69
 70    In most cases if no path is given, then ``/`` is implied, but some
 71    commands may want to distinguish this and so can use
 72    ``empty_path=None`` to note the difference
 73    """
 74    if isinstance(location, (list, tuple)) and len(location) == 2:
 75        # Already normalized
 76        return location
 77    if location.startswith('http://'):
 78        location = location[len('http://'):]
 79    if location.startswith('https://'):
 80        location = location[len('https://'):]
 81    if '/' in location:
 82        hostname, path = location.split('/', 1)
 83        path = '/' + path
 84    else:
 85        hostname = location
 86        path = empty_path
 87    return hostname, path
 88
 89
 90def remove_host(hostname, keep_prev=False, path=None):
 91    """Updates /var/www/appdata.map to remove the given hostname.
 92
 93    If `keep_prev` is True, then the prev.* hostname will be left
 94    in place, otherwise it will be deleted at the same time.
 95
 96    If path is given the deletions will be limited to that path
 97    (or if it is a list, to those paths).
 98
 99    This returns the list of lines removed.
100    """
101    new_lines = []
102    if path and isinstance(path, basestring):
103        path = [path]
104    removed = []
105    for line in appdata_lines():
106        if not line.strip() or line.strip().startswith('#'):
107            new_lines.append(line)
108            continue
109        line_hostname, line_path, data = line.split(None, 2)
110        if (hostname == line_hostname
111            or (not keep_prev and 'prev.'+hostname == line_hostname)):
112            if not path or line_path in path:
113                removed.append(line)
114                continue
115        new_lines.append(line)
116    fp = open(APPDATA_MAP, 'w')
117    fp.writelines(new_lines)
118    fp.close()
119    return removed
120
121
122def appdata_lines():
123    """Returns all lines in /var/www/appdata.map"""
124    fp = open(APPDATA_MAP)
125    try:
126        return fp.readlines()
127    finally:
128        fp.close()
129
130
131def instance_for_location(hostname, path='/'):
132    """Returns the instance name for the given hostname/path.
133
134    Returns None if no instance found"""
135    for line in appdata_lines():
136        if not line.strip() or line.strip().startswith('#'):
137            continue
138        line_hostname, line_path, data = line.split(None, 2)
139        if (line_hostname == hostname and line_path == path):
140            instance_name = data.split('|')[0]
141            return instance_name
142    return None
143
144
145def instance_for_app_name(hostname, app_name):
146    """Returns the instance name for a given hostname and app_name
147
148    Returns None if no instance found"""
149    for line in appdata_lines():
150        if not line.strip() or line.strip().startswith('#'):
151            continue
152        line_hostname, line_path, data = line.split(None, 2)
153        if line_hostname != hostname:
154            continue
155        line_instance_name = data.split('|')[0]
156        line_app_name = line_instance_name.split('.')[0]
157        if line_app_name == app_name:
158            return line_instance_name
159    return None
160
161
162def all_app_instances():
163    """Returns a dictionary of all app instances.
164
165    The dictionary is {instance_name: [(hostname, path), ...]}
166
167    Instances that have no active mappings will also be returned,
168    with empty lists."""
169    results = {}
170    for instance_name in list_instances():
171        results[instance_name] = []
172    for line in appdata_lines():
173        if not line.strip() or line.strip().startswith('#'):
174            continue
175        hostname, path, data = line.split(None, 2)
176        instance_name = data.split('|')[0]
177        results[instance_name].append((hostname, path))
178    return results
179
180
181def list_instances():
182    """Returns a list of all instance names"""
183    base_dir = '/var/www'
184    results = []
185    for name in os.listdir(base_dir):
186        if name.startswith('.'):
187            continue
188        if not os.path.isdir(os.path.join(base_dir, name)):
189            continue
190        if name == 'support':
191            # We're not using this now, but have in the past
192            continue
193        results.append(name)
194    return results