# # base.py # base actions # # Copyright (C) 2009 Red Hat, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Red Hat Author(s): Martin Gracik # import os import re import pwd import grp import glob from pylorax.utils.fileutils import copy, move, remove, touch, edit, replace, chmod # command:action mapping # maps a template command to an action class # if you want your new action to be supported, you have to include it in this mapping COMMANDS = { 'copy': 'Copy', 'move': 'Move', 'remove': 'Remove', 'link': 'Link', 'touch': 'Touch', 'edit': 'Edit', 'replace': 'Replace', 'makedir': 'MakeDir', 'chmod': 'Chmod', 'chown': 'Chown', 'genkey': 'GenerateSSHKey' } class LoraxAction(object): """Actions base class. To create your own custom action, subclass this class and override the methods you need. A valid action has to have a REGEX class variable, which specifies the format of the action command line, so the needed parameters can be properly extracted from it. All the work should be done in the execute method, which will be called from Lorax. At the end, set the success to False, or True depending on the success or failure of your action. If you need to install some package prior to executing the action, return an install pattern with the "install" property. Lorax will get this first, and will try to install the needed package. Don't forget to include a command:action map for your new action in the COMMANDS dictionary. Action classes which are not in the COMMANDS dictionary will not be loaded. You can take a look at some of the builtin actions to get an idea of how to create your own actions.""" REGEX = r'' # regular expression for extracting the parameters from the command line def __init__(self): if self.__class__ is LoraxAction: raise TypeError, 'LoraxAction is an abstract class, cannot be used this way' self._attrs = {} self._attrs['success'] = None # success is None, if the action wasn't executed yet def __str__(self): return '%s: %s' % (self.__class__.__name__, self._attrs) def execute(self, verbose=False): """This method is the main body of the action. Put all the "work" stuff in here.""" raise NotImplementedError, 'execute method not implemented for LoraxAction class' @property def success(self): """Returns if the action's execution was successful or not.""" return self._attrs['success'] @property def install(self): """Returns a pattern that needs to be installed, prior to calling the execute method.""" return None @property def getDeps(self): return None ##### builtin actions class Copy(LoraxAction): REGEX = r'^(?P.*?)\s(?P.*?)\sto\s(?P.*?)\s(?P.*?)(\s(?Pinstall))?(\s(?Pnolinks))?$' def __init__(self, **kwargs): LoraxAction.__init__(self) self._attrs['src_root'] = kwargs.get('src_root') self._attrs['src_path'] = kwargs.get('src_path') self._attrs['dst_root'] = kwargs.get('dst_root') self._attrs['dst_path'] = kwargs.get('dst_path') install = kwargs.get('install', False) if install: self._attrs['install'] = True else: self._attrs['install'] = False nolinks = kwargs.get('nolinks', False) if nolinks: self._attrs['nolinks'] = True else: self._attrs['nolinks'] = False def execute(self, verbose=False): copy(src_root=self.src_root, src_path=self.src_path, dst_root=self.dst_root, dst_path=self.dst_path, nolinks=self.nolinks, ignore_errors=False, verbose=verbose) self._attrs['success'] = True @property def src(self): path = os.path.join(self._attrs['src_root'], self._attrs['src_path']) return os.path.normpath(path) @property def src_root(self): return self._attrs['src_root'] @property def src_path(self): return self._attrs['src_path'] @property def dst(self): path = os.path.join(self._attrs['dst_root'], self._attrs['dst_path']) return os.path.normpath(path) @property def dst_root(self): return self._attrs['dst_root'] @property def dst_path(self): return self._attrs['dst_path'] @property def mode(self): return self._attrs['mode'] @property def install(self): if self._attrs['install']: return self._attrs['src'] else: return None @property def getDeps(self): return self.src @property def nolinks(self): return self._attrs['nolinks'] class Move(Copy): def execute(self, verbose=False): move(src_root=self.src_root, src_path=self.src_path, dst_root=self.dst_root, dst_path=self.dst_path, nolinks=self.nolinks, ignore_errors=False, verbose=verbose) self._attrs['success'] = True class Remove(LoraxAction): REGEX = r'^(?P.*?)$' def __init__(self, **kwargs): LoraxAction.__init__(self) self._attrs['filename'] = kwargs.get('filename') def execute(self, verbose=False): remove(self.filename, verbose=verbose) self._attrs['success'] = True @property def filename(self): return self._attrs['filename'] class Link(LoraxAction): REGEX = r'^(?P.*?)\sto\s(?P.*?)$' def __init__(self, **kwargs): LoraxAction.__init__(self) self._attrs['name'] = kwargs.get('name') self._attrs['target'] = kwargs.get('target') def execute(self, verbose=False): os.symlink(self.target, self.name) self._attrs['success'] = True @property def name(self): return self._attrs['name'] @property def target(self): return self._attrs['target'] @property def install(self): return self._attrs['target'] class Touch(LoraxAction): REGEX = r'^(?P.*?)$' def __init__(self, **kwargs): LoraxAction.__init__(self) self._attrs['filename'] = kwargs.get('filename') def execute(self, verbose=False): touch(filename=self.filename, verbose=verbose) self._attrs['success'] = True @property def filename(self): return self._attrs['filename'] class Edit(Touch): REGEX = r'^(?P.*?)\stext\s"(?P.*?)"(\s(?Pappend))?$' def __init__(self, **kwargs): Touch.__init__(self, **kwargs) self._attrs['text'] = kwargs.get('text') append = kwargs.get('append', False) if append: self._attrs['append'] = True else: self._attrs['append'] = False def execute(self, verbose=False): edit(filename=self.filename, text=self.text, append=self.append, verbose=verbose) self._attrs['success'] = True @property def text(self): return self._attrs['text'] @property def append(self): return self._attrs['append'] @property def install(self): return self._attrs['filename'] class Replace(Touch): REGEX = r'^(?P.*?)\sfind\s"(?P.*?)"\sreplace\s"(?P.*?)"$' def __init__(self, **kwargs): Touch.__init__(self, **kwargs) self._attrs['find'] = kwargs.get('find') self._attrs['replace'] = kwargs.get('replace') def execute(self, verbose=False): replace(filename=self.filename, find=self.find, replace=self.replace, verbose=verbose) self._attrs['success'] = True @property def find(self): return self._attrs['find'] @property def replace(self): return self._attrs['replace'] @property def install(self): return self._attrs['filename'] class MakeDir(LoraxAction): REGEX = r'^(?P.*?)(\smode\s(?P.*?))?$' def __init__(self, **kwargs): LoraxAction.__init__(self) self._attrs['dir'] = kwargs.get('dir') self._attrs['mode'] = kwargs.get('mode') def execute(self, verbose=False): if not os.path.isdir(self.dir): if self.mode: os.makedirs(self.dir, mode=int(self.mode)) else: os.makedirs(self.dir) self._attrs['success'] = True @property def dir(self): return self._attrs['dir'] @property def mode(self): return self._attrs['mode'] class Chmod(LoraxAction): REGEX = r'^(?P.*?)\smode\s(?P[0-9]*?)$' def __init__(self, **kwargs): LoraxAction.__init__(self) self._attrs['filename'] = kwargs.get('filename') self._attrs['mode'] = kwargs.get('mode') def execute(self, verbose=False): chmod(self.filename, self.mode) self._attrs['success'] = True @property def filename(self): return self._attrs['filename'] @property def mode(self): return self._attrs['mode'] class Chown(LoraxAction): REGEX = r'^(?P.*?)\suser\s(?P.*?)\sgroup\s(?P.*?)$' def __init__(self, **kwargs): LoraxAction.__init__(self) self._attrs['filename'] = kwargs.get('filename') self._attrs['user'] = kwargs.get('user') self._attrs['group'] = kwargs.get('group') def execute(self, verbose=False): uid = pwd.getpwnam(self.user)[2] gid = grp.getgrnam(self.group)[2] os.chown(self.filename, uid, gid) self._attrs['success'] = True @property def filename(self): return self._attrs['filename'] @property def user(self): return self._attrs['user'] @property def group(self): return self._attrs['group'] class GenerateSSHKey(LoraxAction): REGEX = r'^(?P.*?)\stype\s(?P.*?)$' def __init__(self, **kwargs): LoraxAction.__init__(self) self._attrs['file'] = kwargs.get('file') self._attrs['type'] = kwargs.get('type') def execute(self, verbose=False): cmd = "/usr/bin/ssh-keygen -q -t %s -f %s -C '' -N ''" % (self.type, self.file) err, output = commands.getstatusoutput(cmd) if not err: os.chmod(self.file, 0600) os.chmod(self.file + '.pub', 0644) self._attrs['success'] = True @property def file(self): return self._attrs['file'] @property def type(self): return self._attrs['type']