| # 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) |