260 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			260 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
						|
 | 
						|
#    Copyright (C) 2012 Yahoo! Inc. All Rights Reserved.
 | 
						|
#
 | 
						|
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
						|
#    not use this file except in compliance with the License. You may obtain
 | 
						|
#    a copy of the License at
 | 
						|
#
 | 
						|
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
#
 | 
						|
#    Unless required by applicable law or agreed to in writing, software
 | 
						|
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
						|
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
						|
#    License for the specific language governing permissions and limitations
 | 
						|
#    under the License.
 | 
						|
 | 
						|
import json
 | 
						|
import os
 | 
						|
 | 
						|
from devstack import date
 | 
						|
from devstack import exceptions as excp
 | 
						|
from devstack import shell as sh
 | 
						|
 | 
						|
# Trace per line output format and file extension formats
 | 
						|
TRACE_FMT = ("%s - %s" + os.linesep)
 | 
						|
TRACE_EXT = ".trace"
 | 
						|
 | 
						|
# Common trace actions
 | 
						|
CFG_WRITING_FILE = "CFG_WRITING_FILE"
 | 
						|
SYMLINK_MAKE = "SYMLINK_MAKE"
 | 
						|
PKG_INSTALL = "PKG_INSTALL"
 | 
						|
PYTHON_INSTALL = "PYTHON_INSTALL"
 | 
						|
DIR_MADE = "DIR_MADE"
 | 
						|
FILE_TOUCHED = "FILE_TOUCHED"
 | 
						|
DOWNLOADED = "DOWNLOADED"
 | 
						|
AP_STARTED = "AP_STARTED"
 | 
						|
PIP_INSTALL = 'PIP_INSTALL'
 | 
						|
 | 
						|
# Common trace file types (or the expected common ones)
 | 
						|
PY_TRACE = "python"
 | 
						|
IN_TRACE = "install"
 | 
						|
START_TRACE = "start"
 | 
						|
 | 
						|
# Used to note version of trace
 | 
						|
TRACE_VERSION = "TRACE_VERSION"
 | 
						|
TRACE_VER = 0x1
 | 
						|
 | 
						|
 | 
						|
def trace_fn(root_dir, name):
 | 
						|
    return sh.joinpths(root_dir, name + TRACE_EXT)
 | 
						|
 | 
						|
 | 
						|
class TraceWriter(object):
 | 
						|
    def __init__(self, trace_filename):
 | 
						|
        self.trace_fn = trace_filename
 | 
						|
        self.started = False
 | 
						|
 | 
						|
    def trace(self, cmd, action=None):
 | 
						|
        if action is None:
 | 
						|
            action = date.rcf8222date()
 | 
						|
        if cmd is not None:
 | 
						|
            sh.append_file(self.trace_fn, TRACE_FMT % (cmd, action))
 | 
						|
 | 
						|
    def filename(self):
 | 
						|
        return self.trace_fn
 | 
						|
 | 
						|
    def _start(self):
 | 
						|
        if self.started:
 | 
						|
            return
 | 
						|
        else:
 | 
						|
            trace_dirs = sh.mkdirslist(sh.dirname(self.trace_fn))
 | 
						|
            sh.touch_file(self.trace_fn)
 | 
						|
            self.trace(TRACE_VERSION, str(TRACE_VER))
 | 
						|
            self.started = True
 | 
						|
            self.dirs_made(*trace_dirs)
 | 
						|
 | 
						|
    def py_installed(self, name, where):
 | 
						|
        self._start()
 | 
						|
        what = dict()
 | 
						|
        what['name'] = name
 | 
						|
        what['where'] = where
 | 
						|
        self.trace(PYTHON_INSTALL, json.dumps(what))
 | 
						|
 | 
						|
    def cfg_file_written(self, fn):
 | 
						|
        self._start()
 | 
						|
        self.trace(CFG_WRITING_FILE, fn)
 | 
						|
 | 
						|
    def symlink_made(self, link):
 | 
						|
        self._start()
 | 
						|
        self.trace(SYMLINK_MAKE, link)
 | 
						|
 | 
						|
    def download_happened(self, tgt, uri):
 | 
						|
        self._start()
 | 
						|
        what = dict()
 | 
						|
        what['target'] = tgt
 | 
						|
        what['from'] = uri
 | 
						|
        self.trace(DOWNLOADED, json.dumps(what))
 | 
						|
 | 
						|
    def pip_installed(self, pip_info):
 | 
						|
        self._start()
 | 
						|
        self.trace(PIP_INSTALL, json.dumps(pip_info))
 | 
						|
 | 
						|
    def dirs_made(self, *dirs):
 | 
						|
        self._start()
 | 
						|
        for d in dirs:
 | 
						|
            self.trace(DIR_MADE, d)
 | 
						|
 | 
						|
    def file_touched(self, fn):
 | 
						|
        self._start()
 | 
						|
        self.trace(FILE_TOUCHED, fn)
 | 
						|
 | 
						|
    def package_installed(self, pkg_info):
 | 
						|
        self._start()
 | 
						|
        self.trace(PKG_INSTALL, json.dumps(pkg_info))
 | 
						|
 | 
						|
    def started_info(self, name, info_fn):
 | 
						|
        self._start()
 | 
						|
        data = dict()
 | 
						|
        data['name'] = name
 | 
						|
        data['trace_fn'] = info_fn
 | 
						|
        self.trace(AP_STARTED, json.dumps(data))
 | 
						|
 | 
						|
 | 
						|
class TraceReader(object):
 | 
						|
    def __init__(self, trace_filename):
 | 
						|
        self.trace_fn = trace_filename
 | 
						|
        self.contents = None
 | 
						|
 | 
						|
    def filename(self):
 | 
						|
        return self.trace_fn
 | 
						|
 | 
						|
    def _parse(self):
 | 
						|
        fn = self.trace_fn
 | 
						|
        if not sh.isfile(fn):
 | 
						|
            msg = "No trace found at filename %s" % (fn)
 | 
						|
            raise excp.NoTraceException(msg)
 | 
						|
        contents = sh.load_file(fn)
 | 
						|
        lines = contents.splitlines()
 | 
						|
        accum = list()
 | 
						|
        for line in lines:
 | 
						|
            ep = self._split_line(line)
 | 
						|
            if ep is None:
 | 
						|
                continue
 | 
						|
            accum.append(tuple(ep))
 | 
						|
        return accum
 | 
						|
 | 
						|
    def read(self):
 | 
						|
        if self.contents is None:
 | 
						|
            self.contents = self._parse()
 | 
						|
        return self.contents
 | 
						|
 | 
						|
    def _split_line(self, line):
 | 
						|
        pieces = line.split("-", 1)
 | 
						|
        if len(pieces) == 2:
 | 
						|
            cmd = pieces[0].rstrip()
 | 
						|
            action = pieces[1].lstrip()
 | 
						|
            return (cmd, action)
 | 
						|
        else:
 | 
						|
            return None
 | 
						|
 | 
						|
    def exists(self):
 | 
						|
        return sh.exists(self.trace_fn)
 | 
						|
 | 
						|
    def py_listing(self):
 | 
						|
        lines = self.read()
 | 
						|
        py_entries = list()
 | 
						|
        for (cmd, action) in lines:
 | 
						|
            if cmd == PYTHON_INSTALL and len(action):
 | 
						|
                entry = json.loads(action)
 | 
						|
                if type(entry) is dict:
 | 
						|
                    py_entries.append((entry.get("name"), entry.get("where")))
 | 
						|
        return py_entries
 | 
						|
 | 
						|
    def download_locations(self):
 | 
						|
        lines = self.read()
 | 
						|
        locations = list()
 | 
						|
        for (cmd, action) in lines:
 | 
						|
            if cmd == DOWNLOADED and len(action):
 | 
						|
                entry = json.loads(action)
 | 
						|
                if type(entry) is dict:
 | 
						|
                    locations.append((entry.get('target'), entry.get('uri')))
 | 
						|
        return locations
 | 
						|
 | 
						|
    def _sort_paths(self, pths):
 | 
						|
        # Ensure in correct order (ie /tmp is before /)
 | 
						|
        pths = list(set(pths))
 | 
						|
        pths.sort()
 | 
						|
        pths.reverse()
 | 
						|
        return pths
 | 
						|
 | 
						|
    def files_touched(self):
 | 
						|
        lines = self.read()
 | 
						|
        files = list()
 | 
						|
        for (cmd, action) in lines:
 | 
						|
            if cmd == FILE_TOUCHED and len(action):
 | 
						|
                files.append(action)
 | 
						|
        return self._sort_paths(files)
 | 
						|
 | 
						|
    def dirs_made(self):
 | 
						|
        lines = self.read()
 | 
						|
        dirs = list()
 | 
						|
        for (cmd, action) in lines:
 | 
						|
            if cmd == DIR_MADE and len(action):
 | 
						|
                dirs.append(action)
 | 
						|
        return self._sort_paths(dirs)
 | 
						|
 | 
						|
    def apps_started(self):
 | 
						|
        lines = self.read()
 | 
						|
        app_info = list()
 | 
						|
        for (cmd, action) in lines:
 | 
						|
            if cmd == AP_STARTED and len(action):
 | 
						|
                entry = json.loads(action)
 | 
						|
                if type(entry) is dict:
 | 
						|
                    app_info.append((entry.get('trace_fn'), entry.get('name')))
 | 
						|
        return app_info
 | 
						|
 | 
						|
    def symlinks_made(self):
 | 
						|
        lines = self.read()
 | 
						|
        links = list()
 | 
						|
        for (cmd, action) in lines:
 | 
						|
            if cmd == SYMLINK_MAKE and len(action):
 | 
						|
                links.append(action)
 | 
						|
        return links
 | 
						|
 | 
						|
    def files_configured(self):
 | 
						|
        lines = self.read()
 | 
						|
        files = list()
 | 
						|
        for (cmd, action) in lines:
 | 
						|
            if cmd == CFG_WRITING_FILE and len(action):
 | 
						|
                files.append(action)
 | 
						|
        files = list(set(files))
 | 
						|
        files.sort()
 | 
						|
        return files
 | 
						|
 | 
						|
    def pips_installed(self):
 | 
						|
        lines = self.read()
 | 
						|
        pips_installed = list()
 | 
						|
        pip_list = list()
 | 
						|
        for (cmd, action) in lines:
 | 
						|
            if cmd == PIP_INSTALL and len(action):
 | 
						|
                pip_list.append(action)
 | 
						|
        for pip_data in pip_list:
 | 
						|
            pip_info_full = json.loads(pip_data)
 | 
						|
            if type(pip_info_full) is dict:
 | 
						|
                pips_installed.append(pip_info_full)
 | 
						|
        return pips_installed
 | 
						|
 | 
						|
    def packages_installed(self):
 | 
						|
        lines = self.read()
 | 
						|
        pkgs_installed = list()
 | 
						|
        pkg_list = list()
 | 
						|
        for (cmd, action) in lines:
 | 
						|
            if cmd == PKG_INSTALL and len(action):
 | 
						|
                pkg_list.append(action)
 | 
						|
        for pkg_data in pkg_list:
 | 
						|
            pkg_info = json.loads(pkg_data)
 | 
						|
            if type(pkg_info) is dict:
 | 
						|
                pkgs_installed.append(pkg_info)
 | 
						|
        return pkgs_installed
 |