#!/usr/bin/env python # Copyright (c) 2012 Openstack Foundation # 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. """Neutron root wrapper for dom0. Executes networking commands in dom0. The XenAPI plugin is responsible determining whether a command is safe to execute. """ from __future__ import print_function import ConfigParser import json import os import select import sys import traceback import XenAPI RC_UNAUTHORIZED = 99 RC_NOCOMMAND = 98 RC_BADCONFIG = 97 RC_XENAPI_ERROR = 96 def parse_args(): # Split arguments, require at least a command exec_name = sys.argv.pop(0) # argv[0] required; path to conf file if len(sys.argv) < 2: print("%s: No command specified" % exec_name) sys.exit(RC_NOCOMMAND) config_file = sys.argv.pop(0) user_args = sys.argv[:] return exec_name, config_file, user_args def _xenapi_section_name(config): sections = [sect for sect in config.sections() if sect.lower() == "xenapi"] if len(sections) == 1: return sections[0] print("Multiple [xenapi] sections or no [xenapi] section found!") sys.exit(RC_BADCONFIG) def load_configuration(exec_name, config_file): config = ConfigParser.RawConfigParser() config.read(config_file) try: exec_dirs = config.get("DEFAULT", "exec_dirs").split(",") filters_path = config.get("DEFAULT", "filters_path").split(",") section = _xenapi_section_name(config) url = config.get(section, "xenapi_connection_url") username = config.get(section, "xenapi_connection_username") password = config.get(section, "xenapi_connection_password") except ConfigParser.Error: print("%s: Incorrect configuration file: %s" % (exec_name, config_file)) sys.exit(RC_BADCONFIG) if not url or not password: msg = ("%s: Must specify xenapi_connection_url, " "xenapi_connection_username (optionally), and " "xenapi_connection_password in %s") % (exec_name, config_file) print(msg) sys.exit(RC_BADCONFIG) return dict( filters_path=filters_path, url=url, username=username, password=password, exec_dirs=exec_dirs, ) def filter_command(exec_name, filters_path, user_args, exec_dirs): # Add ../ to sys.path to allow running from branch possible_topdir = os.path.normpath(os.path.join(os.path.abspath(exec_name), os.pardir, os.pardir)) if os.path.exists(os.path.join(possible_topdir, "neutron", "__init__.py")): sys.path.insert(0, possible_topdir) from oslo.rootwrap import wrapper # Execute command if it matches any of the loaded filters filters = wrapper.load_filters(filters_path) filter_match = wrapper.match_filter( filters, user_args, exec_dirs=exec_dirs) if not filter_match: print("Unauthorized command: %s" % ' '.join(user_args)) sys.exit(RC_UNAUTHORIZED) def run_command(url, username, password, user_args, cmd_input): try: session = XenAPI.Session(url) session.login_with_password(username, password) host = session.xenapi.session.get_this_host(session.handle) result = session.xenapi.host.call_plugin( host, 'netwrap', 'run_command', {'cmd': json.dumps(user_args), 'cmd_input': json.dumps(cmd_input)}) return json.loads(result) except Exception as e: traceback.print_exc() sys.exit(RC_XENAPI_ERROR) def main(): exec_name, config_file, user_args = parse_args() config = load_configuration(exec_name, config_file) filter_command(exec_name, config['filters_path'], user_args, config['exec_dirs']) # If data is available on the standard input, we need to pass it to the # command executed in dom0 cmd_input = None if select.select([sys.stdin,],[],[],0.0)[0]: cmd_input = "".join(sys.stdin) return run_command(config['url'], config['username'], config['password'], user_args, cmd_input) if __name__ == '__main__': print(main())