# dragonflow.sh - Devstack extras script to install Dragonflow # By default, dragonflow uses DFPlugin as core plugin. If you want to use # ML2 as core plugin, you can edit local.conf file to configure # "USE_ML2_PLUGIN=True" and "Q_ML2_PLUGIN_MECHANISM_DRIVERS=df" # if you want to use df-l3 as the l3 services plugin, you could edit # local.conf file to configure "ML2_L3_PLUGIN=df-l3" USE_ML2_PLUGIN=${USE_ML2_PLUGIN:-"False"} # Enable DPDK for Open vSwitch user space datapath ENABLE_DPDK=${ENABLE_DPDK:-False} DPDK_NUM_OF_HUGEPAGES=${DPDK_NUM_OF_HUGEPAGES:-1024} DPDK_BIND_DRIVER=${DPDK_BIND_DRIVER:-igb_uio} DPDK_NIC_NAME=${DPDK_NIC_NAME:-eth1} # The git repo to use OVS_REPO=${OVS_REPO:-http://github.com/openvswitch/ovs.git} OVS_REPO_NAME=$(basename ${OVS_REPO} | cut -f1 -d'.') # The branch to use from $OVS_REPO # TODO(gsagie) Currently take branch-2.5 branch as master is not stable OVS_BRANCH=${OVS_BRANCH:-branch-2.5} DEFAULT_TUNNEL_TYPE="geneve" DEFAULT_APPS_LIST="l2_app.L2App,l3_proactive_app.L3ProactiveApp,"\ "dhcp_app.DHCPApp,dnat_app.DNATApp,sg_app.SGApp,portsec_app.PortSecApp" ML2_APPS_LIST_DEFAULT="l2_ml2_app.L2App,l3_proactive_app.L3ProactiveApp,"\ "dhcp_app.DHCPApp,dnat_app.DNATApp,sg_app.SGApp,portsec_app.PortSecApp" if is_service_enabled df-metadata ; then DEFAULT_APPS_LIST="$DEFAULT_APPS_LIST,metadata_service_app.MetadataServiceApp" ML2_APPS_LIST_DEFAULT="$ML2_APPS_LIST_DEFAULT,metadata_service_app.MetadataServiceApp" fi DF_APPS_LIST=${DF_APPS_LIST:-$DEFAULT_APPS_LIST} ML2_APPS_LIST=${ML2_APPS_LIST:-$ML2_APPS_LIST_DEFAULT} TUNNEL_TYPE=${TUNNEL_TYPE:-$DEFAULT_TUNNEL_TYPE} # How to connect to the database storing the virtual topology. REMOTE_DB_IP=${REMOTE_DB_IP:-$HOST_IP} REMOTE_DB_PORT=${REMOTE_DB_PORT:-4001} REMOTE_DB_HOSTS=${REMOTE_DB_HOSTS:-"$REMOTE_DB_IP:$REMOTE_DB_PORT"} # OVS bridge definition PUBLIC_BRIDGE=${PUBLIC_BRIDGE:-br-ex} INTEGRATION_BRIDGE=${INTEGRATION_BRIDGE:-br-int} INTEGRATION_PEER_PORT=${INTEGRATION_PEER_PORT:-patch-ex} PUBLIC_PEER_PORT=${PUBLIC_PEER_PORT:-patch-int} # OVS related pid files #---------------------- OVS_DB_SERVICE="ovsdb-server" OVS_VSWITCHD_SERVICE="ovs-vswitchd" OVS_DIR="/var/run/openvswitch" OVS_DB_PID=$OVS_DIR"/"$OVS_DB_SERVICE".pid" OVS_VSWITCHD_PID=$OVS_DIR"/"$OVS_VSWITCHD_SERVICE".pid" OVS_VSWITCH_OCSSCHEMA_FILE=${OVS_VSWITCH_OCSSCHEMA_FILE:-"/usr/share/openvswitch/vswitch.ovsschema"} ACTION=$1 STAGE=$2 # Pluggable DB drivers #---------------------- function is_df_db_driver_selected { if [[ "$ACTION" == "stack" && "$STAGE" == "pre-install" ]]; then test -n "$NB_DRIVER_CLASS" return $? fi return 1 } if is_service_enabled df-etcd ; then is_df_db_driver_selected && die $LINENO "More than one database service is set for Dragonflow." source $DEST/dragonflow/devstack/etcd_driver NB_DRIVER_CLASS="etcd_nb_db_driver" fi if is_service_enabled df-ramcloud ; then is_df_db_driver_selected && die $LINENO "More than one database service is set for Dragonflow." source $DEST/dragonflow/devstack/ramcloud_driver NB_DRIVER_CLASS="ramcloud_nb_db_driver" fi if is_service_enabled df-zookeeper ; then is_df_db_driver_selected && die $LINENO "More than one database service is set for Dragonflow." source $DEST/dragonflow/devstack/zookeeper_driver NB_DRIVER_CLASS="zookeeper_nb_db_driver" fi if is_service_enabled df-redis ; then is_df_db_driver_selected && die $LINENO "More than one database service is set for Dragonflow." source $DEST/dragonflow/devstack/redis_driver NB_DRIVER_CLASS="redis_nb_db_driver" DF_REDIS_PUBSUB=${DF_REDIS_PUBSUB:-"True"} else DF_REDIS_PUBSUB="False" fi # As the function returns actual value only on pre-install, ignore it on later stages if [[ "$ACTION" == "stack" && "$STAGE" == "pre-install" ]]; then is_df_db_driver_selected || die $LINENO "No database service is set for Dragonflow." fi # Pub/Sub Service #---------------- # To be called to initialise params common to all pubsub drivers function init_pubsub { enable_service df-publisher-service DF_PUB_SUB="True" } if is_service_enabled df-zmq-publisher-service ; then init_pubsub source $DEST/dragonflow/devstack/zmq_pubsub_driver fi if [[ "$DF_REDIS_PUBSUB" == "True" ]]; then DF_PUB_SUB="True" DF_PUB_SUB_USE_MULTIPROC="False" source $DEST/dragonflow/devstack/redis_pubsub_driver fi # Dragonflow installation uses functions from these files source $TOP_DIR/lib/neutron_plugins/ovs_base if [[ "$ENABLE_DPDK" == "True" ]]; then source $DEST/dragonflow/devstack/ovs_dpdk_setup.sh else source $DEST/dragonflow/devstack/ovs_setup.sh fi # Entry Points # ------------ function configure_df_metadata_service { if is_service_enabled df-metadata ; then NOVA_CONF=${NOVA_CONF:-"/etc/nova/nova.conf"} iniset $NOVA_CONF neutron service_metadata_proxy True iniset $NOVA_CONF neutron metadata_proxy_shared_secret $METADATA_PROXY_SHARED_SECRET iniset $NEUTRON_CONF DEFAULT metadata_proxy_shared_secret $METADATA_PROXY_SHARED_SECRET iniset $DRAGONFLOW_CONF df_metadata ip "$DF_METADATA_SERVICE_IP" iniset $DRAGONFLOW_CONF df_metadata port "$DF_METADATA_SERVICE_PORT" iniset $DRAGONFLOW_CONF df metadata_interface "$DF_METADATA_SERVICE_INTERFACE" fi } function configure_df_plugin { echo "Configuring Neutron for Dragonflow" if is_service_enabled q-svc ; then # NOTE(gsagie) needed for tempest export NETWORK_API_EXTENSIONS=$(python -c \ 'from dragonflow.common import extensions ;\ print ",".join(extensions.SUPPORTED_API_EXTENSIONS)') if [[ "$USE_ML2_PLUGIN" == "False" ]]; then Q_PLUGIN_CLASS="dragonflow.neutron.plugin.DFPlugin" Q_SERVICE_PLUGIN_CLASSES="" else DF_APPS_LIST=$ML2_APPS_LIST fi # Generate configuration file local _pwd=$(pwd) (cd $DRAGONFLOW_DIR && exec ./tools/generate_config_file_samples.sh) cd $_pwd cp $DRAGONFLOW_DIR/etc/dragonflow.ini.sample $DRAGONFLOW_CONF iniset $DRAGONFLOW_CONF df remote_db_ip "$REMOTE_DB_IP" iniset $DRAGONFLOW_CONF df remote_db_port $REMOTE_DB_PORT iniset $DRAGONFLOW_CONF df remote_db_hosts "$REMOTE_DB_HOSTS" iniset $DRAGONFLOW_CONF df nb_db_class "$NB_DRIVER_CLASS" iniset $DRAGONFLOW_CONF df local_ip "$HOST_IP" iniset $DRAGONFLOW_CONF df tunnel_type "$TUNNEL_TYPE" iniset $DRAGONFLOW_CONF df integration_bridge "$INTEGRATION_BRIDGE" iniset $DRAGONFLOW_CONF df apps_list "$DF_APPS_LIST" iniset $DRAGONFLOW_CONF df monitor_table_poll_time "$DF_MONITOR_TABLE_POLL_TIME" iniset $DRAGONFLOW_CONF df_l2_app l2_responder "$DF_L2_RESPONDER" iniset $DRAGONFLOW_CONF df enable_df_pub_sub "$DF_PUB_SUB" iniset $DRAGONFLOW_CONF df pub_sub_use_multiproc "$DF_PUB_SUB_USE_MULTIPROC" iniset $DRAGONFLOW_CONF df publisher_rate_limit_timeout "$PUBLISHER_RATE_LIMIT_TIMEOUT" iniset $DRAGONFLOW_CONF df publisher_rate_limit_count "$PUBLISHER_RATE_LIMIT_COUNT" iniset $DRAGONFLOW_CONF df_dnat_app external_network_bridge "$PUBLIC_BRIDGE" iniset $DRAGONFLOW_CONF df_dnat_app int_peer_patch_port "$INTEGRATION_PEER_PORT" iniset $DRAGONFLOW_CONF df_dnat_app ex_peer_patch_port "$PUBLIC_PEER_PORT" iniset $NEUTRON_CONF DEFAULT advertise_mtu "True" iniset $NEUTRON_CONF DEFAULT core_plugin "$Q_PLUGIN_CLASS" iniset $NEUTRON_CONF DEFAULT service_plugins "$Q_SERVICE_PLUGIN_CLASSES" if is_service_enabled q-dhcp ; then iniset $DRAGONFLOW_CONF df use_centralized_ipv6_DHCP "True" else iniset $NEUTRON_CONF DEFAULT dhcp_agent_notification "False" fi if [[ "$DF_PUB_SUB" == "True" ]]; then DF_SELECTIVE_TOPO_DIST=${DF_SELECTIVE_TOPO_DIST:-"True"} else DF_SELECTIVE_TOPO_DIST="False" fi iniset $DRAGONFLOW_CONF df enable_selective_topology_distribution \ "$DF_SELECTIVE_TOPO_DIST" if [[ "$DF_RUNNING_IN_GATE" == "True" ]]; then iniset $NEUTRON_CONF quotas default_quota "-1" iniset $NEUTRON_CONF quotas quota_network "-1" iniset $NEUTRON_CONF quotas quota_subnet "-1" iniset $NEUTRON_CONF quotas quota_port "-1" iniset $NEUTRON_CONF quotas quota_router "-1" iniset $NEUTRON_CONF quotas quota_floatingip "-1" iniset $NEUTRON_CONF quotas quota_security_group_rule "-1" fi configure_df_metadata_service else _create_neutron_conf_dir # NOTE: We need to manually generate the neutron.conf file here. This # is normally done by a call to _configure_neutron_common in # neutron-lib, but we don't call that for compute nodes here. # Uses oslo config generator to generate core sample configuration files local _pwd=$(pwd) (cd $NEUTRON_DIR && exec ./tools/generate_config_file_samples.sh) (cd $DRAGONFLOW_DIR && exec ./tools/generate_config_file_samples.sh) cd $_pwd cp $NEUTRON_DIR/etc/neutron.conf.sample $NEUTRON_CONF cp $DRAGONFLOW_DIR/etc/dragonflow.ini.sample $DRAGONFLOW_CONF iniset $DRAGONFLOW_CONF df remote_db_ip "$REMOTE_DB_IP" iniset $DRAGONFLOW_CONF df remote_db_port $REMOTE_DB_PORT iniset $DRAGONFLOW_CONF df remote_db_hosts "$REMOTE_DB_HOSTS" iniset $DRAGONFLOW_CONF df nb_db_class "$NB_DRIVER_CLASS" iniset $DRAGONFLOW_CONF df local_ip "$HOST_IP" iniset $DRAGONFLOW_CONF df tunnel_type "$TUNNEL_TYPE" iniset $DRAGONFLOW_CONF df integration_bridge "$INTEGRATION_BRIDGE" iniset $DRAGONFLOW_CONF df apps_list "$DF_APPS_LIST" iniset $DRAGONFLOW_CONF df_l2_app l2_responder "$DF_L2_RESPONDER" iniset $DRAGONFLOW_CONF df enable_df_pub_sub "$DF_PUB_SUB" iniset $DRAGONFLOW_CONF df_dnat_app external_network_bridge "$PUBLIC_BRIDGE" iniset $DRAGONFLOW_CONF df_dnat_app int_peer_patch_port "$INTEGRATION_PEER_PORT" iniset $DRAGONFLOW_CONF df_dnat_app ex_peer_patch_port "$PUBLIC_PEER_PORT" if [[ "$DF_PUB_SUB" == "True" ]]; then DF_SELECTIVE_TOPO_DIST=${DF_SELECTIVE_TOPO_DIST:-"True"} else DF_SELECTIVE_TOPO_DIST="False" fi iniset $DRAGONFLOW_CONF df enable_selective_topology_distribution \ "$DF_SELECTIVE_TOPO_DIST" configure_df_metadata_service fi } function install_zeromq { if is_fedora; then install_package zeromq python-zmq elif is_ubuntu; then install_package libzmq1 python-zmq elif is_suse; then install_package libzmq1 python-pyzmq fi # Necessary directory for socket location. sudo mkdir -p /var/run/openstack sudo chown $STACK_USER /var/run/openstack } function install_df { # Obtain devstack directory for df-ext-services.sh sed -i "/^TOP_DIR=/cTOP_DIR=$TOP_DIR" $DEST/dragonflow/devstack/df-ext-services.sh install_zeromq if function_exists nb_db_driver_install_server; then nb_db_driver_install_server fi if function_exists nb_db_driver_install_client; then nb_db_driver_install_client fi setup_package $DRAGONFLOW_DIR } # The following returns "0" when service is live. # Zero (0) is considered a TRUE value in bash. function ovs_service_status { TEMP_PID=$OVS_DIR"/"$1".pid" if [ -e $TEMP_PID ] then TEMP_PID_VALUE=$(cat $TEMP_PID 2>/dev/null) if [ -e /proc/$TEMP_PID_VALUE ] then return 0 fi fi # service is dead return 1 } function is_module_loaded { return $(lsmod | grep -q $1) } function load_module_if_not_loaded { local module=$1 local fatal=$2 if is_module_loaded $module; then echo "Module already loaded: $module" else if [ "$(trueorfalse True fatal)" == "True" ]; then sudo modprobe $module || (die $LINENO "FAILED TO LOAD $module") else sudo modprobe $module || (echo "FAILED TO LOAD $module") fi fi } function unload_module_if_loaded { local module=$1 if is_module_loaded $module; then sudo rmmod $module || (die $LINENO "FAILED TO UNLOAD $module") else echo "Module is not loaded: $module" fi } # cleanup_nb_db() - Clean all the keys in the northbound database function cleanup_nb_db { # clean db only on the master node if is_service_enabled q-svc ; then if [[ "$DF_Q_SVC_MASTER" == "True" ]]; then df-db clean fi fi } # init_nb_db() - Create all the tables in northbound database function init_nb_db { # init db only on the master node if is_service_enabled q-svc ; then if [[ "$DF_Q_SVC_MASTER" == "True" ]]; then df-db init fi fi } # drop_nb_db() - Drop all the tables in northbound database function drop_nb_db { # drop db only on the master node if is_service_enabled q-svc ; then if [[ "$DF_Q_SVC_MASTER" == "True" ]]; then df-db dropall fi fi } # start_df() - Start running processes, including screen function start_df { echo "Starting Dragonflow" if is_service_enabled df-controller ; then sudo ovs-vsctl --no-wait set-controller $INTEGRATION_BRIDGE tcp:127.0.0.1:6633 run_process df-controller "$DF_LOCAL_CONTROLLER_BINARY --config-file $NEUTRON_CONF --config-file $DRAGONFLOW_CONF" run_process df-ext-services "bash $DEST/dragonflow/devstack/df-ext-services.sh" fi } # stop_df() - Stop running processes (non-screen) function stop_df { if is_service_enabled df-controller ; then stop_process df-controller fi cleanup_nb_db drop_nb_db if function_exists nb_db_driver_stop_server; then nb_db_driver_stop_server fi } function disable_libvirt_apparmor { if ! sudo aa-status --enabled ; then return 0 fi # NOTE(arosen): This is used as a work around to allow newer versions # of libvirt to work with ovs configured ports. See LP#1466631. # requires the apparmor-utils install_package apparmor-utils # disables apparmor for libvirtd sudo aa-complain /etc/apparmor.d/usr.sbin.libvirtd } function verify_ryu_version { # Verify ryu is installed. Version greater than 3.29. Does not return # on failure. RYU_VER_LINE=`ryu --version 2>&1 | head -n 1` RYU_VER=`echo $RYU_VER_LINE | cut -d' ' -f2` echo "Found ryu version $RYU_VER ($RYU_VER_LINE)" if [ `vercmp_numbers "$RYU_VER" "3.29.1"` -lt 0 ]; then die $LINENO "ryu version $RYU_VER too low. Version 3.29.1+ is required for Dragonflow." fi } function start_pubsub_service { echo "Starting Dragonflow publisher service" if is_service_enabled df-publisher-service ; then run_process df-publisher-service "$DF_PUBLISHER_SERVICE_BINARY --config-file $NEUTRON_CONF --config-file $DRAGONFLOW_CONF" fi } function stop_pubsub_service { if is_service_enabled df-publisher-service ; then stop_process df-publisher-service fi } function start_df_metadata_agent { if is_service_enabled df-metadata ; then echo "Starting Dragonflow metadata service" run_process df-metadata "python $DF_METADATA_SERVICE --config-file $NEUTRON_CONF --config-file $DRAGONFLOW_CONF" fi } function stop_df_metadata_agent { if is_service_enabled df-metadata ; then echo "Stopping Dragonflow metadata service" stop_process df-metadata sudo ovs-vsctl del-port br-int $DF_METADATA_SERVICE_INTERFACE fi } # main loop if [[ "$Q_ENABLE_DRAGONFLOW_LOCAL_CONTROLLER" == "True" ]]; then if [[ "$1" == "stack" && "$2" == "install" ]]; then if [[ "$OFFLINE" != "True" ]]; then if ! is_neutron_enabled ; then install_neutron fi install_df install_ovs fi setup_develop $DRAGONFLOW_DIR init_ovs # We have to start at install time, because Neutron's post-config # phase runs ovs-vsctl. start_ovs if function_exists nb_db_driver_start_server; then nb_db_driver_start_server fi disable_libvirt_apparmor elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then configure_ovs configure_df_plugin # initialize the nb db init_nb_db if [[ "$DF_PUB_SUB" == "True" ]]; then # Implemented by the pub/sub plugin configure_pubsub_service_plugin # Defaults, in case no Pub/Sub service was selected if [ -z $PUB_SUB_DRIVER ]; then die $LINENO "pub-sub enabled, but no pub-sub driver selected" fi PUB_SUB_MULTIPROC_DRIVER=${PUB_SUB_MULTIPROC_DRIVER:-$PUB_SUB_DRIVER} fi if is_service_enabled nova; then create_nova_conf_neutron fi if is_service_enabled df-publisher-service; then start_pubsub_service fi start_df start_df_metadata_agent fi if [[ "$1" == "unstack" ]]; then stop_df_metadata_agent stop_df cleanup_ovs stop_ovs uninstall_ovs if is_service_enabled df-publisher-service; then stop_pubsub_service fi fi fi