0d8483391d
With XenServer as hypervisor, the commands neutron-ovs-agent in compute node run are actually executed in Dom0. But current Dom0 plugin doesn't allow conntrack command, this patch is to add such support. Also, the exitcode the commands returned in Dom0 will pass through Dom0 to neutron to make sure the plugin is only aimed executing commands, it doesn't deal with business scenario. Closes-Bug: #1603400 Change-Id: I304788240bcd590ec211bca052fe64594a4e6eca
152 lines
5.1 KiB
Python
Executable File
152 lines
5.1 KiB
Python
Executable File
#!/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
|
|
|
|
from six.moves import configparser as ConfigParser
|
|
from oslo_serialization import jsonutils as json
|
|
|
|
import os
|
|
import select
|
|
import sys
|
|
|
|
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:
|
|
sys.stderr.write("%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]
|
|
|
|
sys.stderr.write("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:
|
|
sys.stderr.write("%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)
|
|
sys.stderr.write(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:
|
|
sys.stderr.write("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)
|
|
try:
|
|
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)})
|
|
result_dict = json.loads(result)
|
|
returncode = result_dict.get('returncode')
|
|
captured_stdout = result_dict.get('out')
|
|
captured_stderr = result_dict.get('err')
|
|
sys.stdout.write(captured_stdout)
|
|
sys.stderr.write(captured_stderr)
|
|
sys.exit(returncode)
|
|
finally:
|
|
session.xenapi.session.logout()
|
|
except Exception as e:
|
|
sys.stderr.write("Failed to execute command in Dom0, %s" % e)
|
|
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__':
|
|
main()
|