def _check_default_networks(self): networks = list(set(tmpl_defaults['networks'])) conn = self.conn.get() error_msg = ("Please, check the configuration in %s/template.conf to " "ensure it lists only valid networks." % PluginPaths('kimchi').conf_dir) for net_name in networks: try: net = conn.networkLookupByName(net_name) except libvirt.libvirtError, e: msg = "Fatal: Unable to find network %s." wok_log.error(msg, net_name) wok_log.error(error_msg) wok_log.error("Details: %s", e.message) sys.exit(1) if net.isActive() == 0: try: net.create() except libvirt.libvirtError as e: msg = "Fatal: Unable to activate network %s." wok_log.error(msg, net_name) wok_log.error(error_msg) wok_log.error("Details: %s", e.message) sys.exit(1)
def get_plugin_config_file(name): plugin_conf = PluginPaths(name).conf_file if not os.path.exists(plugin_conf): cherrypy.log.error_log.error( f"Plugin configuration file {plugin_conf} doesn't exist.") return None return plugin_conf
def _tar_create_archive(directory_path, archive_id, include, exclude_flag): archive_file = os.path.join(directory_path, archive_id + '.tar.gz') backup_dir = os.path.join( PluginPaths('ginger').state_dir, 'ginger_backups') bkp = re.compile(backup_dir) if filter(bkp.match, include) and (len(include) == 1): raise InvalidOperation('GINHBK0012E', {'dir': backup_dir}) exclude = ['--exclude=' + backup_dir] if exclude_flag: exclude.extend( ['--exclude=' + toExclude for toExclude in exclude_flag]) cmd = [ 'tar', '--create', '--ignore-failed-read', '--gzip', '--absolute-names', '--file', archive_file, '--selinux', '--acl', '--xattrs' ] + exclude + include out, err, rc = run_command(cmd) if rc != 0: if 'file changed as we read it' in err: raise OperationFailed('GINHBK0010E', {'file': err.split(': ')[1]}) raise OperationFailed('GINHBK0001E', { 'name': archive_file, 'cmd': ' '.join(cmd) }) return archive_file
def _restore_tar(self, archive_id): backup_dir = os.path.join(PluginPaths('ginger').state_dir, 'ginger_backups') backup_file = os.path.join(backup_dir, archive_id + '.tar.gz') cmd = ['tar', '-xzf', backup_file, '-C', '/'] out, err, rc = run_command(cmd) if rc != 0: raise OperationFailed('GINHBK0001E', {'name': backup_file, 'cmd': ' '.join(cmd)})
def __init__(self, wok_options): Resource.__init__(self, model) self.description = Description(model) self.rectangles = Rectangles(model) self.circles = Circles(model) self.paths = PluginPaths('sample') self.domain = 'sample' self.messages = messages self.api_schema = json.load(open(os.path.join(os.path.dirname( os.path.abspath(__file__)), 'API.json')))
def href(url, plugin=None): if plugin is None: base_path = paths.ui_dir else: base_path = PluginPaths(plugin).ui_dir # for error.html, url is absolute path f = os.path.join(base_path, url.lstrip('/')) mtime = os.path.getmtime(f) return f'{url}?cacheBust={mtime}'
def href(url, plugin=None): if plugin is None: basePath = paths.ui_dir else: basePath = PluginPaths(plugin).ui_dir # for error.html, url is absolute path f = os.path.join(basePath, url.lstrip("/")) mtime = os.path.getmtime(f) return "%s?cacheBust=%s" % (url, mtime)
def _load_plugin_conf(name): plugin_conf = PluginPaths(name).conf_file if not os.path.exists(plugin_conf): cherrypy.log.error_log.error("Plugin configuration file %s" " doesn't exist." % plugin_conf) return try: return Parser().dict_from_file(plugin_conf) except ValueError as e: cherrypy.log.error_log.error("Failed to load plugin " "conf from %s: %s" % (plugin_conf, e.message))
def _check_default_pools(self): pools = {} default_pool = tmpl_defaults['disks'][0]['pool']['name'] default_pool = default_pool.split('/')[-1] pools[default_pool] = {} if default_pool == 'default': pools[default_pool] = {'path': '/var/lib/libvirt/images'} if config.get('kimchi', {}).get('create_iso_pool', False): pools['ISO'] = {'path': '/var/lib/kimchi/isos'} error_msg = ("Please, check the configuration in %s/template.conf to " "ensure it has a valid storage pool." % PluginPaths('kimchi').conf_dir) conn = self.conn.get() for pool_name in pools: try: pool = conn.storagePoolLookupByName(pool_name) except libvirt.libvirtError, e: pool_path = pools[pool_name].get('path') if pool_path is None: msg = "Fatal: Unable to find storage pool %s. " + error_msg wok_log.error(msg % pool_name) wok_log.error("Details: %s", e.message) sys.exit(1) # Try to create the pool pool = E.pool(E.name(pool_name), type='dir') pool.append(E.target(E.path(pool_path))) xml = ET.tostring(pool) try: pool = conn.storagePoolDefineXML(xml, 0) except libvirt.libvirtError, e: msg = "Fatal: Unable to create storage pool %s. " msg += error_msg wok_log.error(msg % pool_name) wok_log.error("Details: %s", e.message) sys.exit(1) # Build and set autostart value to pool # Ignore error as the pool was already successfully created try: # Add build step to make sure target directory created # The build process may fail when the pool directory # already exists on system pool.build(libvirt.VIR_STORAGE_POOL_BUILD_NEW) pool.setAutostart(1) except: pass
def get_all_tabs(): files = [] for plugin, _ in get_enabled_plugins(): files.append( os.path.join(PluginPaths(plugin).ui_dir, 'config/tab-ext.xml')) tabs = [] for f in files: root = ET.parse(f) tabs.extend([t.text.lower() for t in root.getiterator('title')]) return tabs
def get_all_tabs(): files = [] for plugin, _ in get_enabled_plugins(): files.append(os.path.join(PluginPaths(plugin).ui_dir, 'config/tab-ext.xml')) tabs = [] for f in files: try: root = ET.parse(f) except (IOError): wok_log.debug("Unable to load %s", f) continue tabs.extend([t.text.lower() for t in root.getiterator('title')]) return tabs
def get_remote_iso_path(): """ Get a remote iso with the right arch from the distro files shipped with kimchi. """ host_arch = os.uname()[4] remote_path = '' with open(os.path.join(PluginPaths('kimchi').conf_dir, 'distros.d', 'fedora.json')) as fedora_isos: # Get a list of dicts json_isos_list = json.load(fedora_isos) for iso in json_isos_list: if (iso.get('os_arch')) == host_arch: remote_path = iso.get('path') break return remote_path
def __init__(self, wok_options=None): object_store = config.get_object_store() objstore_dir = os.path.dirname(os.path.abspath(object_store)) if not os.path.isdir(objstore_dir): os.makedirs(objstore_dir) self.model = GingerModel() super(Ginger, self).__init__(self.model) for ident, node in sub_nodes.items(): setattr(self, ident, node(self.model)) self.api_schema = json.load( open( os.path.join(os.path.dirname(os.path.abspath(__file__)), 'API.json'))) self.domain = "ginger" self.messages = messages self.paths = PluginPaths('ginger')
def __init__(self, wok_options=None): self.model = GingerModel() super(Ginger, self).__init__(self.model) self.backup = Backup(self.model) self.capabilities = Capabilities(self.model) self.dasddevs = DASDdevs(self.model) self.dasdpartitions = DASDPartitions(self.model) self.filesystems = FileSystems(self.model) self.firmware = Firmware(self.model) self.powerprofiles = PowerProfiles(self.model) self.sensors = Sensors(self.model) self.users = Users(self.model) self.network = Network(self.model) self.api_schema = json.load( open( os.path.join(os.path.dirname(os.path.abspath(__file__)), 'API.json'))) self.paths = PluginPaths('ginger') self.domain = "ginger" self.messages = messages self.san_adapters = SanAdapters(self.model) self.ibm_sep = Sep(self.model)
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import base64 import json import os import random import string import time from Crypto.Cipher import AES from wok.config import PluginPaths from wok.exception import OperationFailed, MissingParameter, InvalidParameter from wok.utils import run_command SERVERCONFIGPATH = os.path.join(PluginPaths('ginger').state_dir, 'server.config') ALPHABET = string.digits + string.ascii_letters SDR_LOCAL_CACHE_DIR = PluginPaths('ginger').state_dir SDR_CACHE = 'sdr_cache' def _check_if_server_with_name_exists(nameToCheck, existingServers): for server in existingServers: if nameToCheck == server['name']: return True else: continue return False
class ArchivesModel(object): _objstore_type = 'ginger_backup_archive' _archive_dir = os.path.join(PluginPaths('ginger').state_dir, 'ginger_backups') def __init__(self, **kargs): self._objstore = kargs['objstore'] self._create_archive_dir() @classmethod def _create_archive_dir(cls): try: os.makedirs(cls._archive_dir) except OSError as e: # It's OK if archive_dir already exists if e.errno != errno.EEXIST: wok_log.error('Error creating archive dir %s: %s', cls._archive_dir, e) raise OperationFailed('GINHBK0003E', {'dir': cls._archive_dir}) @property def _default_include(self): # This function builds a new copy of the list for each invocation, # so that the caller can modify the returned list as wish without # worrying about changing the original reference. return list(cherrypy.request.app.config['backup']['default_include']) @property def _default_exclude(self): # See _default_include() comments for explanation. return list(cherrypy.request.app.config['backup']['default_exclude']) def _create_archive(self, params): error = None try: params['file'] = _tar_create_archive( self._archive_dir, params['identity'], params['include'], params['exclude']) params['checksum'] = {'algorithm': 'sha256', 'value': _sha256sum(params['file'])} with self._objstore as session: session.store(self._objstore_type, params['identity'], params) except TimeoutExpired as e: error = e reason = 'GINHBK0010E' except Exception as e: error = e reason = 'GINHBK0009E' if error is not None: msg = 'Error creating archive %s: %s' % (params['identity'], error) wok_log.error(msg) try: with self._objstore as session: session.delete(self._objstore_type, params['identity'], ignore_missing=True) except Exception as e_session: wok_log.error('Error cleaning archive meta data %s. ' 'Error: %s', params['identity'], e_session) if params['file'] != '': try: os.unlink(params['file']) except Exception as e_file: wok_log.error('Error cleaning archive file %s. ' 'Error: %s', params['file'], e_file) raise OperationFailed(reason, {'identity': params['identity']}) def create(self, params): archive_id = str(uuid.uuid4()) stamp = int(time.mktime(time.localtime())) # Though formally we ask front-end to not send "include" at all when # it's empty, but in implementation we try to be tolerant. # Front-end can also send [] to indicate the "include" is empty. include = params.get('include') exclude = params.get('exclude', []) if not include: include = self._default_include if not exclude: exclude = self._default_exclude ar_params = {'identity': archive_id, 'include': include, 'exclude': exclude, 'description': params.get('description', ''), 'checksum': {}, 'timestamp': stamp, 'file': ''} self._create_archive(ar_params) return archive_id def _session_get_list(self, session): # Assume session is already locked. return session.get_list(self._objstore_type, sort_key='timestamp') def get_list(self): with self._objstore as session: return self._session_get_list(session)
import base64 import json import os import random import string import time from Crypto.Cipher import AES from wok.config import PluginPaths from wok.exception import OperationFailed, MissingParameter, InvalidParameter from wok.utils import run_command SERVERCONFIGPATH = os.path.join( PluginPaths('ginger').conf_dir, 'server.config') ALPHABET = string.digits + string.ascii_letters SDR_LOCAL_CACHE_DIR = PluginPaths('ginger').conf_dir SDR_CACHE = 'sdr_cache' def _check_if_server_with_name_exists(nameToCheck, existingServers): for server in existingServers: if nameToCheck == server['name']: return True else: continue return False def _check_if_server_with_ipaddr_exists(ipaddrToCheck, existingServers):
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import base64 import errno import os from multiprocessing import Process from websockify import WebSocketProxy from wok.config import config, paths, PluginPaths WS_TOKENS_DIR = os.path.join(PluginPaths('kimchi').state_dir, 'vnc-tokens') def new_ws_proxy(): try: os.makedirs(WS_TOKENS_DIR, mode=0755) except OSError as e: if e.errno == errno.EEXIST: pass cert = config.get('server', 'ssl_cert') key = config.get('server', 'ssl_key') if not (cert and key): cert = '%s/wok-cert.pem' % paths.conf_dir key = '%s/wok-key.pem' % paths.conf_dir
class ArchivesModel(object): _objstore_type = 'ginger_backup_archive' _archive_dir = os.path.join( PluginPaths('ginger').state_dir, 'ginger_backups') def __init__(self, **kargs): self._objstore = kargs['objstore'] self.task = TaskModel(**kargs) self._create_archive_dir() @classmethod def _create_archive_dir(cls): try: os.makedirs(cls._archive_dir) except OSError as e: # It's OK if archive_dir already exists if e.errno != errno.EEXIST: wok_log.error('Error creating archive dir %s: %s', cls._archive_dir, e) raise OperationFailed('GINHBK0003E', {'dir': cls._archive_dir}) @property def _default_include(self): # This function builds a new copy of the list for each invocation, # so that the caller can modify the returned list as wish without # worrying about changing the original reference. return list(cherrypy.request.app.config['backup']['default_include']) @property def _default_exclude(self): # See _default_include() comments for explanation. return list(cherrypy.request.app.config['backup']['default_exclude']) def _create_archive(self, params): error = None try: params['file'] = _tar_create_archive(self._archive_dir, params['identity'], params['include'], params['exclude']) params['checksum'] = { 'algorithm': 'sha256', 'value': _sha256sum(params['file']) } with self._objstore as session: session.store(self._objstore_type, params['identity'], params) except InvalidOperation: raise except OperationFailed: raise except Exception as e: error = e reason = 'GINHBK0009E' if error is not None: msg = 'Error creating archive %s: %s' % (params['identity'], error.message) wok_log.error(msg) try: with self._objstore as session: session.delete(self._objstore_type, params['identity'], ignore_missing=True) except Exception as e_session: wok_log.error( 'Error cleaning archive meta data %s. ' 'Error: %s', params['identity'], e_session) if params['file'] != '': try: os.unlink(params['file']) except Exception as e_file: wok_log.error( 'Error cleaning archive file %s. ' 'Error: %s', params['file'], e_file) raise OperationFailed(reason, {'identity': params['identity']}) def create(self, params): uuid_uuid4 = uuid.uuid4() if isinstance(uuid_uuid4, unicode): uuid_uuid4 = uuid_uuid4.encode('utf-8') archive_id = str(uuid_uuid4) stamp = int(time.mktime(time.localtime())) # Though formally we ask front-end to not send "include" at all when # it's empty, but in implementation we try to be tolerant. # Front-end can also send [] to indicate the "include" is empty. include = params.get('include') exclude = params.get('exclude', []) if not include: include = self._default_include if not exclude: exclude = self._default_exclude ar_params = { 'identity': archive_id, 'include': include, 'exclude': exclude, 'description': params.get('description', ''), 'checksum': {}, 'timestamp': stamp, 'file': '' } taskid = AsyncTask(u'/backup/create/%s' % (archive_id), self._create_task, ar_params).id return self.task.lookup(taskid) def _create_task(self, cb, params): cb('entering task to create config backup') try: self._create_archive(params) cb('OK', True) except (InvalidOperation) as e: cb(e.message, False) except (OperationFailed) as e: cb(e.message, False) raise OperationFailed('GINHBK0011E', { 'params': 'params', 'err': e.message }) def _session_get_list(self, session): # Assume session is already locked. return session.get_list(self._objstore_type, sort_key='timestamp') def get_list(self): with self._objstore as session: files = [x.split('.')[0] for x in os.listdir(self._archive_dir)] for db_file in self._session_get_list(session): if db_file not in files: session.delete(ArchivesModel._objstore_type, db_file) return self._session_get_list(session)
# You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import json import os from wok.config import CACHEEXPIRES, PluginConfig, PluginPaths from wok.control.base import Collection, Resource from wok.control.utils import UrlSubNode from wok.plugins.sample.i18n import messages from wok.plugins.sample.model import Model from wok.root import WokRoot samplePaths = PluginPaths('sample') """ The main class must be the plugin name starting with upper case. In this case, Sample. The Sample class is a WokRoot instance with plugin specific details. Each class attribute which is a Resource or a Collection will be translated as a new REST API. So self.config to /config API, self.description to /description API and so on. self.paths represents the plugin paths. Usually it is PluginPath(<plugin-name>) self.domain is the gettext domain name. Usually it is the plugin name. self.messages is a list of all i18n messages used on backend side.
'opensuse': '13.1', 'sles': '11sp3' }, 'ppc64le': { 'rhel': '6.5', 'fedora': '19', 'ubuntu': '14.04', 'opensuse': '13.1', 'sles': '11sp3' } } icon_available_distros = [ icon[5:-4] for icon in glob.glob1('%s/images/' % PluginPaths('kimchi').ui_dir, 'icon-*.png') ] def _get_arch(): for arch, sub_archs in SUPPORTED_ARCHS.iteritems(): if os.uname()[4] in sub_archs: return arch def _get_default_template_mem(): if hasattr(psutil, 'virtual_memory'): mem = psutil.virtual_memory().total >> 10 >> 10 else: mem = psutil.TOTAL_PHYMEM >> 10 >> 10 if _get_arch() == 'x86':
def _get_tmpl_defaults(): """ ConfigObj returns a dict like below when no changes were made in the template configuration file (template.conf) {'main': {}, 'storage': {'disk.0': {}}, 'processor': {}, 'graphics': {}} The default values should be like below: {'main': {'networks': ['default'], 'memory': '1024'}, 'storage': {'pool': 'default', 'disk.0': {'format': 'qcow2', 'size': '10'}}, 'processor': {'cpus': '1'}, 'graphics': {'type': 'spice', 'listen': '127.0.0.1'}} """ # Create dict with default values tmpl_defaults = defaultdict(dict) tmpl_defaults['main']['networks'] = ['default'] tmpl_defaults['main']['memory'] = _get_default_template_mem() tmpl_defaults['storage']['pool'] = 'default' tmpl_defaults['storage']['disk.0'] = {'size': 10, 'format': 'qcow2'} tmpl_defaults['processor']['cpus'] = 1 tmpl_defaults['graphics'] = {'type': 'vnc', 'listen': '127.0.0.1'} default_config = ConfigObj(tmpl_defaults) # Load template configuration file config_file = os.path.join(PluginPaths('kimchi').conf_dir, 'template.conf') config = ConfigObj(config_file) # Merge default configuration with file configuration default_config.merge(config) # Create a dict with default values according to data structure # expected by VMTemplate defaults = { 'domain': 'kvm', 'arch': os.uname()[4], 'cdrom_bus': 'ide', 'cdrom_index': 2, 'mouse_bus': 'ps2' } # Parse main section to get networks and memory values main_section = default_config.pop('main') defaults.update(main_section) # Parse storage section to get storage pool and disks values storage_section = default_config.pop('storage') defaults['storagepool'] = '/plugins/kimchi/storagepools/' + \ storage_section.pop('pool') defaults['disks'] = [] for disk in storage_section.keys(): data = storage_section[disk] data['index'] = int(disk.split('.')[1]) defaults['disks'].append(data) # Parse processor section to get cpus and cpu_topology values processor_section = default_config.pop('processor') defaults['cpus'] = processor_section.pop('cpus') defaults['cpu_info'] = {} if len(processor_section.keys()) > 0: defaults['cpu_info']['topology'] = processor_section # Update defaults values with graphics values defaults['graphics'] = default_config.pop('graphics') return defaults