# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you 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. # Common function for Cloudstack's XenAPI plugins import ConfigParser import logging import os import subprocess from time import localtime, asctime DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" DEFAULT_LOG_FILE = "/var/log/cloudstack_plugins.log" PLUGIN_CONFIG_PATH = "/etc/xensource/cloudstack_plugins.conf" OVSDB_PID_PATH = "/var/run/openvswitch/ovsdb-server.pid" OVSDB_DAEMON_PATH = "ovsdb-server" OVS_PID_PATH = "/var/run/openvswitch/ovs-vswitchd.pid" OVS_DAEMON_PATH = "ovs-vswitchd" VSCTL_PATH = "/usr/bin/ovs-vsctl" OFCTL_PATH = "/usr/bin/ovs-ofctl" XE_PATH = "/opt/xensource/bin/xe" class PluginError(Exception): """Base Exception class for all plugin errors.""" def __init__(self, *args): Exception.__init__(self, *args) def setup_logging(log_file=None): debug = False verbose = False log_format = DEFAULT_LOG_FORMAT log_date_format = DEFAULT_LOG_DATE_FORMAT # try to read plugin configuration file if os.path.exists(PLUGIN_CONFIG_PATH): config = ConfigParser.ConfigParser() config.read(PLUGIN_CONFIG_PATH) try: options = config.options('LOGGING') if 'debug' in options: debug = config.getboolean('LOGGING', 'debug') if 'verbose' in options: verbose = config.getboolean('LOGGING', 'verbose') if 'format' in options: log_format = config.get('LOGGING', 'format') if 'date_format' in options: log_date_format = config.get('LOGGING', 'date_format') if 'file' in options: log_file_2 = config.get('LOGGING', 'file') except ValueError: # configuration file contained invalid attributes # ignore them pass except ConfigParser.NoSectionError: # Missing 'Logging' section in configuration file pass root_logger = logging.root if debug: root_logger.setLevel(logging.DEBUG) elif verbose: root_logger.setLevel(logging.INFO) else: root_logger.setLevel(logging.WARNING) formatter = logging.Formatter(log_format, log_date_format) log_filename = log_file or log_file_2 or DEFAULT_LOG_FILE logfile_handler = logging.FileHandler(log_filename) logfile_handler.setFormatter(formatter) root_logger.addHandler(logfile_handler) def do_cmd(cmd): """Abstracts out the basics of issuing system commands. If the command returns anything in stderr, a PluginError is raised with that information. Otherwise, the output from stdout is returned. """ pipe = subprocess.PIPE logging.debug("Executing:%s", cmd) proc = subprocess.Popen(cmd, shell=False, stdin=pipe, stdout=pipe, stderr=pipe, close_fds=True) ret_code = proc.wait() err = proc.stderr.read() if ret_code: logging.debug("The command exited with the error code: " + "%s (stderr output:%s)" % (ret_code, err)) raise PluginError(err) output = proc.stdout.read() if output.endswith('\n'): output = output[:-1] return output def _is_process_run(pidFile, name): try: fpid = open(pidFile, "r") pid = fpid.readline() fpid.close() except IOError, e: return -1 pid = pid[:-1] ps = os.popen("ps -ae") for l in ps: if pid in l and name in l: ps.close() return 0 ps.close() return -2 def _is_tool_exist(name): if os.path.exists(name): return 0 return -1 def check_switch(): global result ret = _is_process_run(OVSDB_PID_PATH, OVSDB_DAEMON_PATH) if ret < 0: if ret == -1: return "NO_DB_PID_FILE" if ret == -2: return "DB_NOT_RUN" ret = _is_process_run(OVS_PID_PATH, OVS_DAEMON_PATH) if ret < 0: if ret == -1: return "NO_SWITCH_PID_FILE" if ret == -2: return "SWITCH_NOT_RUN" if _is_tool_exist(VSCTL_PATH) < 0: return "NO_VSCTL" if _is_tool_exist(OFCTL_PATH) < 0: return "NO_OFCTL" return "SUCCESS" def _build_flow_expr(**kwargs): is_delete_expr = kwargs.get('delete', False) flow = "" if not is_delete_expr: flow = "hard_timeout=%s,idle_timeout=%s,priority=%s"\ % (kwargs.get('hard_timeout', '0'), kwargs.get('idle_timeout', '0'), kwargs.get('priority', '1')) in_port = 'in_port' in kwargs and ",in_port=%s" % kwargs['in_port'] or '' dl_type = 'dl_type' in kwargs and ",dl_type=%s" % kwargs['dl_type'] or '' dl_src = 'dl_src' in kwargs and ",dl_src=%s" % kwargs['dl_src'] or '' dl_dst = 'dl_dst' in kwargs and ",dl_dst=%s" % kwargs['dl_dst'] or '' nw_src = 'nw_src' in kwargs and ",nw_src=%s" % kwargs['nw_src'] or '' nw_dst = 'nw_dst' in kwargs and ",nw_dst=%s" % kwargs['nw_dst'] or '' proto = 'proto' in kwargs and ",%s" % kwargs['proto'] or '' ip = ('nw_src' in kwargs or 'nw_dst' in kwargs) and ',ip' or '' flow = (flow + in_port + dl_type + dl_src + dl_dst + (ip or proto) + nw_src + nw_dst) return flow def add_flow(bridge, **kwargs): """ Builds a flow expression for **kwargs and adds the flow entry to an Open vSwitch instance """ flow = _build_flow_expr(**kwargs) actions = 'actions' in kwargs and ",actions=%s" % kwargs['actions'] or '' flow = flow + actions addflow = [OFCTL_PATH, "add-flow", bridge, flow] do_cmd(addflow) def del_flows(bridge, **kwargs): """ Removes flows according to criteria passed as keyword. """ flow = _build_flow_expr(delete=True, **kwargs) # out_port condition does not exist for all flow commands out_port = ("out_port" in kwargs and ",out_port=%s" % kwargs['out_port'] or '') flow = flow + out_port delFlow = [OFCTL_PATH, 'del-flows', bridge, flow] do_cmd(delFlow) def del_all_flows(bridge): delFlow = [OFCTL_PATH, "del-flows", bridge] do_cmd(delFlow) normalFlow = "priority=0 idle_timeout=0 hard_timeout=0 actions=normal" add_flow(bridge, normalFlow) def del_port(bridge, port): delPort = [VSCTL_PATH, "del-port", bridge, port] do_cmd(delPort)