From fdb22a0376436334a4b3aa34d174adac5bef9805 Mon Sep 17 00:00:00 2001 From: Vlastimil Holer Date: Wed, 28 Feb 2018 17:31:38 +0100 Subject: [PATCH] F #1498: Open vSwitch VXLAN driver --- include/VirtualNetwork.h | 77 +++++++--- install.sh | 9 ++ share/etc/oned.conf | 2 + src/cli/one_helper/onevnet_helper.rb | 5 +- .../app/tabs/vnets-tab/form-panels/create.js | 25 ++++ .../vnets-tab/form-panels/create/wizard.hbs | 27 +++- .../public/app/tabs/vnets-tab/panels/info.js | 14 +- .../app/tabs/vnets-tab/panels/info/html.hbs | 15 ++ src/vmm/LibVirtDriverKVM.cc | 4 +- src/vmm_mad/remotes/kvm/attach_nic | 4 +- src/vnm/VirtualNetwork.cc | 86 +++++++++-- src/vnm/VirtualNetworkPool.cc | 123 ++++++++++++---- src/vnm_mad/remotes/OpenNebulaNetwork.conf | 10 ++ src/vnm_mad/remotes/lib/vnm_driver.rb | 5 + src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb | 139 +++++++++++++++++- src/vnm_mad/remotes/ovswitch/pre | 15 +- .../ovswitch_vxlan/OpenvSwitchVXLAN.rb | 44 ++++++ src/vnm_mad/remotes/ovswitch_vxlan/clean | 29 ++++ src/vnm_mad/remotes/ovswitch_vxlan/post | 30 ++++ src/vnm_mad/remotes/ovswitch_vxlan/pre | 30 ++++ src/vnm_mad/remotes/ovswitch_vxlan/update_sg | 21 +++ src/vnm_mad/remotes/vxlan/vxlan.rb | 75 ++++++++++ src/vnm_mad/remotes/vxlan/vxlan_driver.rb | 55 +------ 23 files changed, 713 insertions(+), 131 deletions(-) create mode 100644 src/vnm_mad/remotes/ovswitch_vxlan/OpenvSwitchVXLAN.rb create mode 100755 src/vnm_mad/remotes/ovswitch_vxlan/clean create mode 100755 src/vnm_mad/remotes/ovswitch_vxlan/post create mode 100755 src/vnm_mad/remotes/ovswitch_vxlan/pre create mode 100755 src/vnm_mad/remotes/ovswitch_vxlan/update_sg create mode 100644 src/vnm_mad/remotes/vxlan/vxlan.rb diff --git a/include/VirtualNetwork.h b/include/VirtualNetwork.h index cd73646326..eb4f3ed000 100644 --- a/include/VirtualNetwork.h +++ b/include/VirtualNetwork.h @@ -52,28 +52,30 @@ public: */ enum VirtualNetworkDriver { - NONE = 0, - DUMMY = 1, - VLAN = 2, - EBTABLES = 3, - FW = 4, - OVSWITCH = 5, - VXLAN = 6, - VCENTER = 7 + NONE = 0, + DUMMY = 1, + VLAN = 2, + EBTABLES = 3, + FW = 4, + OVSWITCH = 5, + VXLAN = 6, + VCENTER = 7, + OVSWITCH_VXLAN = 8 }; static string driver_to_str(VirtualNetworkDriver ob) { switch (ob) { - case NONE: return ""; - case DUMMY: return "dummy"; - case VLAN: return "802.1Q"; - case EBTABLES: return "ebtables"; - case FW: return "fw"; - case OVSWITCH: return "ovswitch"; - case VXLAN: return "vxlan"; - case VCENTER: return "vcenter"; + case NONE: return ""; + case DUMMY: return "dummy"; + case VLAN: return "802.1Q"; + case EBTABLES: return "ebtables"; + case FW: return "fw"; + case OVSWITCH: return "ovswitch"; + case VXLAN: return "vxlan"; + case VCENTER: return "vcenter"; + case OVSWITCH_VXLAN: return "ovswitch_vxlan"; } }; @@ -107,6 +109,10 @@ public: { return VCENTER; } + else if ( ob == "ovswitch_vxlan" ) + { + return OVSWITCH_VXLAN; + } else { return NONE; @@ -473,6 +479,15 @@ public: new_vn->replace("VLAN_ID", vlan_id); } + if ( outer_vlan_id.empty() ) + { + new_vn->replace("AUTOMATIC_OUTER_VLAN_ID", "NO"); + } + else + { + new_vn->replace("OUTER_VLAN_ID", outer_vlan_id); + } + return new_vn; }; @@ -507,15 +522,27 @@ private: string phydev; /** - * VLAN ID of the NIC + * VLAN ID of the NIC. When more than VLAN ID is used this refers to the + * link layer or outer/service VLAN_ID */ string vlan_id; + /** + * Used for double tagging of VM traffic. This id refers to the transport + * layer or outer/service VLAN_ID + */ + string outer_vlan_id; + /** * If the VLAN has been set automatically */ bool vlan_id_automatic; + /** + * If the outer VLAN has been set automatically + */ + bool outer_vlan_id_automatic; + /** * Parent VNET ID if any */ @@ -536,6 +563,22 @@ private: */ ObjectCollection vrouters; + // ************************************************************************* + // VLAN ID functions + // ************************************************************************* + + /** + * This function parses the VLAN attribute and clears the associated + * automatic flag if set. + * @param id_name of the VLAN attribute VLAN_ID or OUTER_VLAN_ID + * @param auto_name of automatic flag AUTOMATIC_VLAN_ID or + * AUTOMATIC_OUTER_VLAN_ID + * @param id the associated vlan variable + * @param auto the associated automatic variable + */ + void parse_vlan_id(const char * id_name, const char * auto_name, + string& id, bool& auto_id); + // ************************************************************************* // Address allocation funtions // ************************************************************************* diff --git a/install.sh b/install.sh index 77f1216940..0ec85e1347 100755 --- a/install.sh +++ b/install.sh @@ -270,6 +270,7 @@ VAR_DIRS="$VAR_LOCATION/remotes \ $VAR_LOCATION/remotes/vnm/ebtables \ $VAR_LOCATION/remotes/vnm/fw \ $VAR_LOCATION/remotes/vnm/ovswitch \ + $VAR_LOCATION/remotes/vnm/ovswitch_vxlan \ $VAR_LOCATION/remotes/vnm/vcenter \ $VAR_LOCATION/remotes/tm/ \ $VAR_LOCATION/remotes/tm/dummy \ @@ -435,6 +436,7 @@ INSTALL_FILES=( NETWORK_EBTABLES_FILES:$VAR_LOCATION/remotes/vnm/ebtables NETWORK_FW_FILES:$VAR_LOCATION/remotes/vnm/fw NETWORK_OVSWITCH_FILES:$VAR_LOCATION/remotes/vnm/ovswitch + NETWORK_OVSWITCH_VXLAN_FILES:$VAR_LOCATION/remotes/vnm/ovswitch_vxlan NETWORK_VCENTER_FILES:$VAR_LOCATION/remotes/vnm/vcenter EXAMPLE_SHARE_FILES:$SHARE_LOCATION/examples WEBSOCKIFY_SHARE_FILES:$SHARE_LOCATION/websockify @@ -896,6 +898,7 @@ NETWORK_VXLAN_FILES="src/vnm_mad/remotes/vxlan/clean \ src/vnm_mad/remotes/vxlan/post \ src/vnm_mad/remotes/vxlan/pre \ src/vnm_mad/remotes/vxlan/update_sg \ + src/vnm_mad/remotes/vxlan/vxlan.rb \ src/vnm_mad/remotes/vxlan/vxlan_driver.rb" @@ -921,6 +924,12 @@ NETWORK_OVSWITCH_FILES="src/vnm_mad/remotes/ovswitch/clean \ src/vnm_mad/remotes/ovswitch/update_sg \ src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb" +NETWORK_OVSWITCH_VXLAN_FILES="src/vnm_mad/remotes/ovswitch_vxlan/clean \ + src/vnm_mad/remotes/ovswitch_vxlan/post \ + src/vnm_mad/remotes/ovswitch_vxlan/pre \ + src/vnm_mad/remotes/ovswitch_vxlan/update_sg \ + src/vnm_mad/remotes/ovswitch_vxlan/OpenvSwitchVXLAN.rb" + NETWORK_VCENTER_FILES="src/vnm_mad/remotes/vcenter/pre \ src/vnm_mad/remotes/vcenter/post \ src/vnm_mad/remotes/vcenter/clean" diff --git a/share/etc/oned.conf b/share/etc/oned.conf index e86637b04d..0ea3ace8fb 100644 --- a/share/etc/oned.conf +++ b/share/etc/oned.conf @@ -984,6 +984,7 @@ VNET_RESTRICTED_ATTR = "VLAN_ID" VNET_RESTRICTED_ATTR = "BRIDGE" VNET_RESTRICTED_ATTR = "CONF" VNET_RESTRICTED_ATTR = "BRIDGE_CONF" +VNET_RESTRICTED_ATTR = "OVS_BRIDGE_CONF" VNET_RESTRICTED_ATTR = "IP_LINK_CONF" VNET_RESTRICTED_ATTR = "AR/VN_MAD" @@ -1057,6 +1058,7 @@ INHERIT_VNET_ATTR = "OUTBOUND_PEAK_BW" INHERIT_VNET_ATTR = "OUTBOUND_PEAK_KB" INHERIT_VNET_ATTR = "CONF" INHERIT_VNET_ATTR = "BRIDGE_CONF" +INHERIT_VNET_ATTR = "OVS_BRIDGE_CONF" INHERIT_VNET_ATTR = "IP_LINK_CONF" INHERIT_VNET_ATTR = "VCENTER_NET_REF" diff --git a/src/cli/one_helper/onevnet_helper.rb b/src/cli/one_helper/onevnet_helper.rb index 0647ceeb57..97f57029cc 100644 --- a/src/cli/one_helper/onevnet_helper.rb +++ b/src/cli/one_helper/onevnet_helper.rb @@ -214,7 +214,7 @@ class OneVNetHelper < OpenNebulaHelper::OneHelper CLIHelper.print_header(str_h1 % ["VIRTUAL NETWORK #{vn.id.to_s} INFORMATION"]) - str="%-15s: %-20s" + str="%-25s: %-20s" puts str % ["ID", vn.id.to_s] puts str % ["NAME", vn['NAME']] puts str % ["USER", vn['UNAME']] @@ -226,6 +226,9 @@ class OneVNetHelper < OpenNebulaHelper::OneHelper puts str % ["VN_MAD", vn['VN_MAD']] if !vn['VN_MAD'].empty? puts str % ["PHYSICAL DEVICE", vn["PHYDEV"]] if !vn["PHYDEV"].empty? puts str % ["VLAN ID", vn["VLAN_ID"]] if !vn["VLAN_ID"].empty? + puts str % ["AUTOMATIC VLAN ID", vn["VLAN_ID_AUTOMATIC"]=="1" ? "YES" : "NO"] + puts str % ["OUTER VLAN ID", vn["OUTER_VLAN_ID"]] if !vn["OUTER_VLAN_ID"].empty? + puts str % ["AUTOMATIC OUTER VLAN ID", vn["OUTER_VLAN_ID_AUTOMATIC"]=="1" ? "YES" : "NO"] puts str % ["USED LEASES", vn['USED_LEASES']] puts diff --git a/src/sunstone/public/app/tabs/vnets-tab/form-panels/create.js b/src/sunstone/public/app/tabs/vnets-tab/form-panels/create.js index b0ec3e59ee..cc502b9a2e 100644 --- a/src/sunstone/public/app/tabs/vnets-tab/form-panels/create.js +++ b/src/sunstone/public/app/tabs/vnets-tab/form-panels/create.js @@ -171,6 +171,13 @@ define(function(require) { $("#vnetCreateSecurityTab-label").hide(); $("div.mode_param.ovswitch [wizard_field]", context).prop("wizard_field_disabled", false); + $("input#bridge", context).attr("required", ""); + break; + case "ovswitch_vxlan": + $("div.mode_param.ovswitch_vxlan", context).show(); + $("#vnetCreateSecurityTab-label").hide(); + $("div.mode_param.ovswitch_vxlan [wizard_field]", context).prop("wizard_field_disabled", false); + $("input#bridge", context).attr("required", ""); break; case "vcenter": @@ -219,9 +226,18 @@ define(function(require) { } }); + $("select[wizard_field=AUTOMATIC_OUTER_VLAN_ID]", context).change(function(){ + if($(this).val() != "") { + $("input[wizard_field=\"OUTER_VLAN_ID\"]", context).hide().prop("wizard_field_disabled", true); + } else { + $("input[wizard_field=\"OUTER_VLAN_ID\"]", context).show().prop("wizard_field_disabled", false); + } + }); + //Initialize shown options $("#network_mode", context).trigger("change"); $("select[wizard_field=AUTOMATIC_VLAN_ID]", context).trigger("change"); + $("select[wizard_field=AUTOMATIC_OUTER_VLAN_ID]", context).trigger("change"); this.securityGroupsTable.initialize(); @@ -243,6 +259,7 @@ define(function(require) { $("#network_mode option[value=\"802.1Q\"]", context).hide(); $("#network_mode option[value=\"vxlan\"]", context).hide(); $("#network_mode option[value=\"ovswitch\"]", context).hide(); + $("#network_mode option[value=\"ovswitch_vxlan\"]", context).hide(); } return false; } @@ -393,6 +410,14 @@ define(function(require) { attr("disabled", "disabled").trigger("change"); } + if (element.OUTER_VLAN_ID_AUTOMATIC== 1){ + $("select[wizard_field=AUTOMATIC_OUTER_VLAN_ID]", context).val("YES"). + attr("disabled", "disabled").trigger("change"); + } else { + $("select[wizard_field=AUTOMATIC_OUTER_VLAN_ID]", context).val(""). + attr("disabled", "disabled").trigger("change"); + } + WizardFields.fill($("#vnetCreateGeneralTab", context), element.TEMPLATE); WizardFields.fill($("#vnetCreateBridgeTab", context), element.TEMPLATE); WizardFields.fill($("#vnetCreateQoSTab", context), element.TEMPLATE); diff --git a/src/sunstone/public/app/tabs/vnets-tab/form-panels/create/wizard.hbs b/src/sunstone/public/app/tabs/vnets-tab/form-panels/create/wizard.hbs index ed0ac1ee01..cc6398b837 100644 --- a/src/sunstone/public/app/tabs/vnets-tab/form-panels/create/wizard.hbs +++ b/src/sunstone/public/app/tabs/vnets-tab/form-panels/create/wizard.hbs @@ -87,6 +87,7 @@ + @@ -116,6 +117,9 @@
{{tr "Open vSwitch, restrict network access with Open vSwitch Virtual Switch. Security Groups are not applied."}}
+
+ {{tr "Open vSwitch on VXLAN L2 network overlay. Security Groups are not applied."}} +
{{tr "vSphere standard switches or distributed switches with port groups. Security Groups are not applied."}}
@@ -126,7 +130,7 @@
-
+
-
+
-
+
-
+
-
+
+
+ +
diff --git a/src/sunstone/public/app/tabs/vnets-tab/panels/info.js b/src/sunstone/public/app/tabs/vnets-tab/panels/info.js index 1d2a4f12ab..46c89b8a64 100644 --- a/src/sunstone/public/app/tabs/vnets-tab/panels/info.js +++ b/src/sunstone/public/app/tabs/vnets-tab/panels/info.js @@ -117,6 +117,16 @@ define(function(require) { $(".reserve-sunstone-info").removeAttr("title"); } //==== + var auto_vlan_id = Locale.tr("NO"); + var auto_outer_vlan_id = Locale.tr("NO"); + + if (this.element.VLAN_ID_AUTOMATIC == "1") { + auto_vlan_id = Locale.tr("YES"); + } + + if (this.element.OUTER_VLAN_ID_AUTOMATIC == "1") { + auto_outer_vlan_id = Locale.tr("YES"); + } return TemplateInfo({ 'element': this.element, @@ -124,7 +134,9 @@ define(function(require) { 'reservationTrHTML': reservationTrHTML, 'permissionsTableHTML': permissionsTableHTML, 'templateTableHTML': templateTableHTML, - 'templateTableVcenterHTML': templateTableVcenterHTML + 'templateTableVcenterHTML': templateTableVcenterHTML, + 'autoVlanID': auto_vlan_id, + 'autoOuterVlanID': auto_outer_vlan_id, }); } diff --git a/src/sunstone/public/app/tabs/vnets-tab/panels/info/html.hbs b/src/sunstone/public/app/tabs/vnets-tab/panels/info/html.hbs index 1343269e89..79cb2dc548 100644 --- a/src/sunstone/public/app/tabs/vnets-tab/panels/info/html.hbs +++ b/src/sunstone/public/app/tabs/vnets-tab/panels/info/html.hbs @@ -35,6 +35,21 @@ {{valOrDefault element.VLAN_ID "--"}} + + {{tr "AUTO VLAN ID"}} + {{autoVlanID}} + + + + {{tr "OUTER VLAN ID"}} + {{valOrDefault element.OUTER_VLAN_ID "--"}} + + + + {{tr "AUTO OUTER VLAN ID"}} + {{autoOuterVlanID}} + +
diff --git a/src/vmm/LibVirtDriverKVM.cc b/src/vmm/LibVirtDriverKVM.cc index f133f46e4c..b21e6044a7 100644 --- a/src/vmm/LibVirtDriverKVM.cc +++ b/src/vmm/LibVirtDriverKVM.cc @@ -929,8 +929,8 @@ int LibVirtDriver::deployment_description_kvm( { file << "\t\t" << endl; - - if (VirtualNetwork::str_to_driver(vn_mad) == VirtualNetwork::OVSWITCH) + if (VirtualNetwork::str_to_driver(vn_mad) == VirtualNetwork::OVSWITCH || + VirtualNetwork::str_to_driver(vn_mad) == VirtualNetwork::OVSWITCH_VXLAN) { file << "\t\t\t" << endl; } diff --git a/src/vmm_mad/remotes/kvm/attach_nic b/src/vmm_mad/remotes/kvm/attach_nic index d0bae65905..6a5d23a241 100755 --- a/src/vmm_mad/remotes/kvm/attach_nic +++ b/src/vmm_mad/remotes/kvm/attach_nic @@ -34,7 +34,9 @@ if [ -z "${BRIDGE}" ]; then DEVICE+="" else DEVICE+="" - [ "${VN_MAD}" = 'ovswitch' ] && DEVICE+=" " + if [ "${VN_MAD}" = 'ovswitch' ] || [ "${VN_MAD}" = 'ovswitch_vxlan' ]; then + DEVICE+=" " + fi DEVICE+=" " fi diff --git a/src/vnm/VirtualNetwork.cc b/src/vnm/VirtualNetwork.cc index 1c6b93aa42..e29033915e 100644 --- a/src/vnm/VirtualNetwork.cc +++ b/src/vnm/VirtualNetwork.cc @@ -46,6 +46,7 @@ VirtualNetwork::VirtualNetwork(int _uid, Clusterable(_cluster_ids), bridge(""), vlan_id_automatic(false), + outer_vlan_id_automatic(false), parent_vid(_pvid), vrouters("VROUTERS") { @@ -98,6 +99,30 @@ const char * VirtualNetwork::db_bootstrap = "CREATE TABLE IF NOT EXISTS" /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +void VirtualNetwork::parse_vlan_id(const char * id_name, const char * auto_name, + string& id, bool& auto_id) +{ + string vis; + + if ( PoolObjectSQL::get_template_attribute(id_name, vis) && !vis.empty() ) + { + erase_template_attribute(id_name, id); + + add_template_attribute(id_name, id); + + remove_template_attribute(auto_name); + + auto_id = false; + } + else + { + erase_template_attribute(auto_name, auto_id); + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + int VirtualNetwork::insert(SqlDB * db, string& error_str) { vector ars; @@ -137,22 +162,12 @@ int VirtualNetwork::insert(SqlDB * db, string& error_str) add_template_attribute("PHYDEV", phydev); - // ---- VLAN_ID if not set allocated in VirtualNetworkPool, if needed ----- + // ---- VLAN_IDs if not set will be allocated in VirtualNetworkPool ----- - if (PoolObjectSQL::get_template_attribute("VLAN_ID", vis) && !vis.empty()) - { - erase_template_attribute("VLAN_ID", vlan_id); + parse_vlan_id("VLAN_ID", "AUTOMATIC_VLAN_ID", vlan_id, vlan_id_automatic); - add_template_attribute("VLAN_ID", vlan_id); - - remove_template_attribute("AUTOMATIC_VLAN_ID"); - - vlan_id_automatic = false; - } - else - { - erase_template_attribute("AUTOMATIC_VLAN_ID", vlan_id_automatic); - } + parse_vlan_id("OUTER_VLAN_ID", "AUTOMATIC_OUTER_VLAN_ID", outer_vlan_id, + outer_vlan_id_automatic); // ------------ BRIDGE -------------------- @@ -284,6 +299,8 @@ int VirtualNetwork::post_update_template(string& error) remove_template_attribute("AUTOMATIC_VLAN_ID"); + remove_template_attribute("AUTOMATIC_OUTER_VLAN_ID"); + if (!vlan_id_automatic) { erase_template_attribute("VLAN_ID", vlan_id); @@ -294,6 +311,16 @@ int VirtualNetwork::post_update_template(string& error) remove_template_attribute("VLAN_ID"); } + if (!outer_vlan_id_automatic) + { + erase_template_attribute("OUTER_VLAN_ID", outer_vlan_id); + add_template_attribute("OUTER_VLAN_ID", outer_vlan_id); + } + else + { + remove_template_attribute("OUTER_VLAN_ID"); + } + erase_template_attribute("BRIDGE",new_bridge); if (!new_bridge.empty()) @@ -431,6 +458,7 @@ string& VirtualNetwork::to_xml_extended(string& xml, bool extended, string lock_str; int int_vlan_id_automatic = vlan_id_automatic ? 1 : 0; + int int_outer_vlan_id_automatic = outer_vlan_id_automatic ? 1 : 0; os << "" << @@ -481,8 +509,21 @@ string& VirtualNetwork::to_xml_extended(string& xml, bool extended, os << ""; } + if (!outer_vlan_id.empty()) + { + os << "" << one_util::escape_xml(outer_vlan_id) + << ""; + } + else + { + os << ""; + } + os << "" << int_vlan_id_automatic <<""; + os << "" << int_outer_vlan_id_automatic + << ""; + os << ""<< ar_pool.get_used_addr() << ""; os << vrouters.to_xml(vrouters_xml); @@ -506,7 +547,9 @@ int VirtualNetwork::from_xml(const string &xml_str) vector content; int rc = 0; + int int_vlan_id_automatic; + int int_outer_vlan_id_automatic; // Initialize the internal XML object update_from_str(xml_str); @@ -527,11 +570,17 @@ int VirtualNetwork::from_xml(const string &xml_str) xpath(vn_mad, "/VNET/VN_MAD", ""); xpath(phydev, "/VNET/PHYDEV", ""); - xpath(vlan_id,"/VNET/VLAN_ID",""); + + xpath(vlan_id, "/VNET/VLAN_ID", ""); + xpath(outer_vlan_id, "/VNET/OUTER_VLAN_ID", ""); + + xpath(int_vlan_id_automatic, "/VNET/VLAN_ID_AUTOMATIC", 0); + xpath(int_outer_vlan_id_automatic, "/VNET/OUTER_VLAN_ID_AUTOMATIC", 0); + xpath(parent_vid,"/VNET/PARENT_NETWORK_ID",-1); - xpath(int_vlan_id_automatic,"/VNET/VLAN_ID_AUTOMATIC",0); vlan_id_automatic = int_vlan_id_automatic; + outer_vlan_id_automatic = int_outer_vlan_id_automatic; // Set of cluster IDs rc += Clusterable::from_xml(this, "/VNET/"); @@ -621,6 +670,11 @@ int VirtualNetwork::nic_attribute( nic->replace("VLAN_ID", vlan_id); } + if (!outer_vlan_id.empty()) + { + nic->replace("OUTER_VLAN_ID", outer_vlan_id); + } + if (parent_vid != -1) { nic->replace("PARENT_NETWORK_ID", parent_vid); diff --git a/src/vnm/VirtualNetworkPool.cc b/src/vnm/VirtualNetworkPool.cc index d048c39142..19e4357c1b 100644 --- a/src/vnm/VirtualNetworkPool.cc +++ b/src/vnm/VirtualNetworkPool.cc @@ -342,67 +342,124 @@ void VirtualNetworkPool::authorize_nic( /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int VirtualNetworkPool::set_vlan_id(VirtualNetwork * vn) +static int set_8021Q_id(int vnid, string& vlan_var, bool& auto_var, + BitMap<4096>& bitmap) { - string vn_mad; - - unsigned int start_vlan, hint_vlan; - unsigned int vlan_id; - - ostringstream oss; - - if ( !vn->vlan_id.empty() || !vn->vlan_id_automatic ) + if ( !vlan_var.empty() || !auto_var ) { return 0; } + unsigned int vlan_id; + ostringstream oss; + + unsigned int start_vlan = bitmap.get_start_bit(); + unsigned int hint_vlan = start_vlan + (vnid % (4095 - start_vlan)); + + if ( bitmap.get(hint_vlan, vlan_id) != 0 ) + { + return -1; + } + + oss << vlan_id; + + vlan_var = oss.str(); + auto_var = true; + + return vlan_id; +} + +static int set_vxlan_id(int vnid, string& vlan_var, bool& auto_var, + unsigned int start_vlan) +{ + if ( !vlan_var.empty() || !auto_var ) + { + return 0; + } + + ostringstream oss; + unsigned int vlan_id = start_vlan + vnid; + + oss << vlan_id; + + vlan_var = oss.str(); + + auto_var = true; + + return vlan_id; + +} + +int VirtualNetworkPool::set_vlan_id(VirtualNetwork * vn) +{ + unsigned int start_vlan; + int rc, rcx; + if ( vn->vn_mad.empty() ) { return 0; } + if (vxlan_conf.vector_value("START", start_vlan) != 0) + { + start_vlan = 2; //default in oned.conf + } + switch (VirtualNetwork::str_to_driver(vn->vn_mad)) { - case VirtualNetwork::VXLAN: - if (vxlan_conf.vector_value("START", start_vlan) != 0) + case VirtualNetwork::OVSWITCH_VXLAN: + rc = set_8021Q_id(vn->get_oid(), vn->vlan_id, vn->vlan_id_automatic, + vlan_id_bitmap); + + if ( rc == -1 ) { - start_vlan = 2; //default in oned.conf + return -1; + } + else if ( rc != 0 ) + { + vlan_id_bitmap.update(db); } - vlan_id = start_vlan + vn->get_oid(); - oss << vlan_id; + rcx = set_vxlan_id(vn->get_oid(), vn->outer_vlan_id, + vn->outer_vlan_id_automatic, start_vlan); - vn->vlan_id = oss.str(); + if ( rc != 0 || rcx != 0 ) + { + update(vn); + } + break; - vn->vlan_id_automatic = true; - - update(vn); + case VirtualNetwork::VXLAN: + rcx = set_vxlan_id(vn->get_oid(), vn->vlan_id, vn->vlan_id_automatic, + start_vlan); + if ( rcx != 0 ) + { + update(vn); + } break; case VirtualNetwork::VLAN: case VirtualNetwork::VCENTER: case VirtualNetwork::OVSWITCH: - start_vlan = vlan_id_bitmap.get_start_bit(); - hint_vlan = start_vlan + (vn->get_oid() % (4095 - start_vlan )); + rc = set_8021Q_id(vn->get_oid(), vn->vlan_id, vn->vlan_id_automatic, + vlan_id_bitmap); - if ( vlan_id_bitmap.get(hint_vlan, vlan_id) != 0 ) + if ( rc == -1 ) { return -1; } - - oss << vlan_id; - - vn->vlan_id = oss.str(); - vn->vlan_id_automatic = true; - - vlan_id_bitmap.update(db); - - update(vn); - + else if ( rc != 0 ) + { + vlan_id_bitmap.update(db); + update(vn); + } break; - default: + case VirtualNetwork::NONE: + case VirtualNetwork::DUMMY: + case VirtualNetwork::EBTABLES: + case VirtualNetwork::FW: break; } @@ -439,7 +496,9 @@ void VirtualNetworkPool::release_vlan_id(VirtualNetwork *vn) switch (VirtualNetwork::str_to_driver(vn->vn_mad)) { case VirtualNetwork::VLAN: + case VirtualNetwork::VCENTER: case VirtualNetwork::OVSWITCH: + case VirtualNetwork::OVSWITCH_VXLAN: vlan_id_bitmap.reset(vlan_id); vlan_id_bitmap.update(db); break; diff --git a/src/vnm_mad/remotes/OpenNebulaNetwork.conf b/src/vnm_mad/remotes/OpenNebulaNetwork.conf index 6a1b0ee7e7..1f206ca5ef 100644 --- a/src/vnm_mad/remotes/OpenNebulaNetwork.conf +++ b/src/vnm_mad/remotes/OpenNebulaNetwork.conf @@ -23,6 +23,7 @@ # # CONF="vxlan_mc=239.0.100.0,test=false,validate_vlan_id=true" # BRIDGE_CONF="sethello=6" +# OVS_BRIDGE_CONF="stp_enable=true" # IP_LINK_CONF="tos=10,udpcsum=,udp6zerocsumrx=__delete__" # # Options can have empty value when they don't need a parameter. Also the @@ -77,6 +78,15 @@ # :stp: on +# These options are set on the OvS bridge. For example, +# this command will be trigged for the following option: +# +# ovs-vsctl set-bridge stp_enable=true +# +# :ovs_bridge_conf: +# :stp_enable: true + + # These options will be added to the ip link add command. For example: # # sudo ip link add lxcbr0.260 type vxlan id 260 group 239.0.101.4 \ diff --git a/src/vnm_mad/remotes/lib/vnm_driver.rb b/src/vnm_mad/remotes/lib/vnm_driver.rb index ed8bdd419b..c1eb899a19 100644 --- a/src/vnm_mad/remotes/lib/vnm_driver.rb +++ b/src/vnm_mad/remotes/lib/vnm_driver.rb @@ -73,6 +73,7 @@ module VNMMAD blk = lambda do |nic| add_nic_conf(nic) add_bridge_conf(nic) + add_ovs_bridge_conf(nic) add_ip_link_conf(nic) block.call(nic) @@ -106,6 +107,10 @@ module VNMMAD add_command_conf(nic, :bridge_conf) end + def add_ovs_bridge_conf(nic) + add_command_conf(nic, :ovs_bridge_conf) + end + def add_ip_link_conf(nic) add_command_conf(nic, :ip_link_conf) end diff --git a/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb b/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb index f4f45f80de..4367ab37b7 100644 --- a/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb +++ b/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb @@ -34,16 +34,44 @@ class OpenvSwitchVLAN < VNMMAD::VNMDriver end end - def activate + def activate(pre_action=false) lock + @bridges = get_bridges + process do |nic| @nic = nic + # Get the name of the link vlan device. + get_vlan_dev_name + + # Create the bridge. + create_bridge + + # Check that no other vlans are connected to this bridge + validate_vlan_id if @nic[:conf][:validate_vlan_id] + + if @nic[:vlan_dev] + unless @bridges[@nic[:bridge]].include? @nic[:vlan_dev] + create_vlan_dev + + add_bridge_port(@nic[:vlan_dev]) + end + elsif @nic[:phydev] + add_bridge_port(@nic[:phydev]) + end + if @nic[:tap].nil? - STDERR.puts "No tap device found for nic #{@nic[:nic_id]}" - unlock - exit 1 + # In net/pre action, we just need to ensure the bridge is + # created so the libvirt/QEMU can add VM interfaces into that. + # Any other driver actions are done in net/post action. + if pre_action + next + else + STDERR.puts "No tap device found for nic #{@nic[:nic_id]}" + unlock + exit 1 + end end # Apply VLAN @@ -96,6 +124,8 @@ class OpenvSwitchVLAN < VNMMAD::VNMDriver def deactivate lock + @bridges = get_bridges + attach_nic_id = @vm['TEMPLATE/NIC[ATTACH="YES"]/NIC_ID'] process do |nic| @@ -107,6 +137,27 @@ class OpenvSwitchVLAN < VNMMAD::VNMDriver # Remove flows del_flows + + next if @nic[:phydev].nil? + next if @bridges[@nic[:bridge]].nil? + + # Get the name of the vlan device. + get_vlan_dev_name + + # Return if the bridge doesn't exist because it was already deleted (handles last vm with multiple nics on the same vlan) + next if !@bridges.include? @nic[:bridge] + + # Return if the vlan device is not the only left device in the bridge. + next if @bridges[@nic[:bridge]].length > 1 or + (@nic[:vlan_dev] and + !@bridges[@nic[:bridge]].include? @nic[:vlan_dev]) + + if @nic[:vlan_dev] + delete_vlan_dev + @bridges[@nic[:bridge]].delete(@nic[:vlan_dev]) + end + + delete_bridge end unlock @@ -287,4 +338,84 @@ class OpenvSwitchVLAN < VNMMAD::VNMDriver def range?(range) !range.to_s.match(/^\d+(,\d+)*$/).nil? end + +private + # Generate the name of the vlan device which will be added to the bridge. + def get_vlan_dev_name + nil + end + + # Create a VLAN device. + # NEEDS to be implemented by the subclass. + def create_vlan_dev + nil + end + + # Delete a VLAN device + # NEEDS to be implemented by the subclass. + def delete_vlan_dev + nil + end + + # Creates an OvS bridge if it does not exists, and brings it up. + # This function IS FINAL, exits if action cannot be completed + def create_bridge + return if @bridges.keys.include? @nic[:bridge] + + OpenNebula.exec_and_log("#{command(:ovs_vsctl)} add-br #{@nic[:bridge]}") + + set_bridge_options + + @bridges[@nic[:bridge]] = Array.new + + OpenNebula.exec_and_log("#{command(:ip)} link set #{@nic[:bridge]} up") + end + + # Delete OvS bridge + def delete_bridge + OpenNebula.exec_and_log("#{command(:ovs_vsctl)} del-br #{@nic[:bridge]}") + + @bridges.delete(@nic[:bridge]) + end + + # Add port into OvS bridge + def add_bridge_port(port) + return if @bridges[@nic[:bridge]].include? port + + OpenNebula.exec_and_log("#{command(:ovs_vsctl)} add-port #{@nic[:bridge]} #{port}") + + @bridges[@nic[:bridge]] << port + end + + # Calls ovs-vsctl set bridge to set options stored in ovs_bridge_conf + def set_bridge_options + @nic[:ovs_bridge_conf].each do |option, value| + cmd = "#{command(:ovs_vsctl)} set bridge " << + "#{@nic[:bridge]} #{option}=#{value}" + + OpenNebula.exec_and_log(cmd) + end + end + + # Get hypervisor bridges + # @return [Hash] with the bridge names + def get_bridges + bridges = Hash.new + + list_br =`#{command(:ovs_vsctl)} list-br` + list_br.split.each do |bridge| + bridge = bridge.strip + + if bridge + list_ports =`#{command(:ovs_vsctl)} list-ports #{bridge}` + bridges[bridge] = list_ports.split("\n") + end + end + + bridges + end + + def validate_vlan_id + OpenNebula.log_error("VLAN ID validation not supported with Open vSwitch, skipped.") + end end diff --git a/src/vnm_mad/remotes/ovswitch/pre b/src/vnm_mad/remotes/ovswitch/pre index b3570d8b45..98b99b450b 100755 --- a/src/vnm_mad/remotes/ovswitch/pre +++ b/src/vnm_mad/remotes/ovswitch/pre @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env ruby # -------------------------------------------------------------------------- # # Copyright 2002-2018, OpenNebula Project, OpenNebula Systems # @@ -16,4 +16,15 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -exit 0 \ No newline at end of file +$: << File.dirname(__FILE__) +$: << File.join(File.dirname(__FILE__), "..") + +require 'OpenvSwitch' + +template64 = ARGV[0] +deploy_id = ARGV[1] +xpath_filter = OpenvSwitchVLAN::XPATH_FILTER + +ovs = OpenvSwitchVLAN.from_base64(template64, xpath_filter, deploy_id) + +ovs.activate(pre_action=true) diff --git a/src/vnm_mad/remotes/ovswitch_vxlan/OpenvSwitchVXLAN.rb b/src/vnm_mad/remotes/ovswitch_vxlan/OpenvSwitchVXLAN.rb new file mode 100644 index 0000000000..75e719345e --- /dev/null +++ b/src/vnm_mad/remotes/ovswitch_vxlan/OpenvSwitchVXLAN.rb @@ -0,0 +1,44 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2018, OpenNebula Project, OpenNebula Systems # +# # +# 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. # +#--------------------------------------------------------------------------- # + +require 'vnmmad' +require 'ovswitch/OpenvSwitch' +require 'vxlan/vxlan' + +class OpenvSwitchVXLAN < OpenvSwitchVLAN + include VXLAN + + # VXLAN orrides + ATTR_VLAN_ID = :outer_vlan_id + + # DRIVER name and XPATH for relevant NICs + DRIVER = "ovswitch_vxlan" + XPATH_FILTER = "TEMPLATE/NIC[VN_MAD='ovswitch_vxlan']" + + def initialize(vm, xpath_filter = nil, deploy_id = nil) + @attr_vlan_id = ATTR_VLAN_ID + @attr_vlan_dev = ATTR_VLAN_DEV + + xpath_filter ||= XPATH_FILTER + super(vm, xpath_filter, deploy_id) + end + +private + # Generate the name of the vlan device which will be added to the bridge. + def get_vlan_dev_name + @nic[:vlan_dev] = "#{@nic[:phydev]}.#{@nic[@attr_vlan_id]}" + end +end diff --git a/src/vnm_mad/remotes/ovswitch_vxlan/clean b/src/vnm_mad/remotes/ovswitch_vxlan/clean new file mode 100755 index 0000000000..bcbd30a601 --- /dev/null +++ b/src/vnm_mad/remotes/ovswitch_vxlan/clean @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby + +# -------------------------------------------------------------------------- # +# Copyright 2002-2018, OpenNebula Project, OpenNebula Systems # +# # +# 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. # +#--------------------------------------------------------------------------- # + +$: << File.dirname(__FILE__) +$: << File.join(File.dirname(__FILE__), "..") + +require 'OpenvSwitchVXLAN' + +template64 = ARGV[0] +deploy_id = nil +xpath_filter = OpenvSwitchVXLAN::XPATH_FILTER + +ovs = OpenvSwitchVXLAN.from_base64(template64, xpath_filter, deploy_id) +ovs.deactivate diff --git a/src/vnm_mad/remotes/ovswitch_vxlan/post b/src/vnm_mad/remotes/ovswitch_vxlan/post new file mode 100755 index 0000000000..ad8fbdb3bc --- /dev/null +++ b/src/vnm_mad/remotes/ovswitch_vxlan/post @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby + +# -------------------------------------------------------------------------- # +# Copyright 2002-2018, OpenNebula Project, OpenNebula Systems # +# # +# 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. # +#--------------------------------------------------------------------------- # + +$: << File.dirname(__FILE__) +$: << File.join(File.dirname(__FILE__), "..") + +require 'OpenvSwitchVXLAN' + +template64 = ARGV[0] +deploy_id = ARGV[1] +xpath_filter = OpenvSwitchVXLAN::XPATH_FILTER + +ovs = OpenvSwitchVXLAN.from_base64(template64, xpath_filter, deploy_id) + +ovs.activate diff --git a/src/vnm_mad/remotes/ovswitch_vxlan/pre b/src/vnm_mad/remotes/ovswitch_vxlan/pre new file mode 100755 index 0000000000..49db5bd618 --- /dev/null +++ b/src/vnm_mad/remotes/ovswitch_vxlan/pre @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby + +# -------------------------------------------------------------------------- # +# Copyright 2002-2018, OpenNebula Project, OpenNebula Systems # +# # +# 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. # +#--------------------------------------------------------------------------- # + +$: << File.dirname(__FILE__) +$: << File.join(File.dirname(__FILE__), "..") + +require 'OpenvSwitchVXLAN' + +template64 = ARGV[0] +deploy_id = ARGV[1] +xpath_filter = OpenvSwitchVXLAN::XPATH_FILTER + +ovs = OpenvSwitchVXLAN.from_base64(template64, xpath_filter, deploy_id) + +ovs.activate(pre_action=true) diff --git a/src/vnm_mad/remotes/ovswitch_vxlan/update_sg b/src/vnm_mad/remotes/ovswitch_vxlan/update_sg new file mode 100755 index 0000000000..c604b3134e --- /dev/null +++ b/src/vnm_mad/remotes/ovswitch_vxlan/update_sg @@ -0,0 +1,21 @@ +#!/bin/bash + +# -------------------------------------------------------------------------- # +# Copyright 2002-2018, OpenNebula Project, OpenNebula Systems # +# # +# 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. # +#--------------------------------------------------------------------------- # +source $(dirname $0)/../../scripts_common.sh + +log_error "Security groups not supported for ovswitch" +exit 1 \ No newline at end of file diff --git a/src/vnm_mad/remotes/vxlan/vxlan.rb b/src/vnm_mad/remotes/vxlan/vxlan.rb new file mode 100644 index 0000000000..47f6cb1965 --- /dev/null +++ b/src/vnm_mad/remotes/vxlan/vxlan.rb @@ -0,0 +1,75 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2018, OpenNebula Project, OpenNebula Systems # +# # +# 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. # +#--------------------------------------------------------------------------- # + +require 'ipaddr' + +module VXLAN + ATTR_VLAN_ID = :vlan_id + ATTR_VLAN_DEV = :vlan_dev + + ############################################################################ + # This function creates and activate a VLAN device + ############################################################################ + def create_vlan_dev + begin + ipaddr = IPAddr.new @nic[:conf][:vxlan_mc] + rescue + ipaddr = IPAddr.new "239.0.0.0" + end + + mc = ipaddr.to_i + @nic[@attr_vlan_id].to_i + mcs = IPAddr.new(mc, Socket::AF_INET).to_s + + mtu = @nic[:mtu] ? "mtu #{@nic[:mtu]}" : "mtu #{@nic[:conf][:vxlan_mtu]}" + ttl = @nic[:conf][:vxlan_ttl] ? "ttl #{@nic[:conf][:vxlan_ttl]}" : "" + + ip_link_conf = "" + + @nic[:ip_link_conf].each do |option, value| + case value + when true + value = "on" + when false + value = "off" + end + + ip_link_conf << "#{option} #{value} " + end + + OpenNebula.exec_and_log("#{command(:ip)} link add #{@nic[@attr_vlan_dev]}"\ + " #{mtu} type vxlan id #{@nic[@attr_vlan_id]} group #{mcs} #{ttl}"\ + " dev #{@nic[:phydev]} #{ip_link_conf}") + + OpenNebula.exec_and_log("#{command(:ip)} link set #{@nic[@attr_vlan_dev]} up") + end + + def delete_vlan_dev + OpenNebula.exec_and_log("#{command(:ip)} link delete #{@nic[@attr_vlan_dev]}") + end + + def get_interface_vlan(name) + text = %x(#{command(:ip)} -d link show #{name}) + return nil if $?.exitstatus != 0 + + text.each_line do |line| + m = line.match(/^\s*vxlan id (\d+)/) + + return m[1] if m + end + + nil + end +end diff --git a/src/vnm_mad/remotes/vxlan/vxlan_driver.rb b/src/vnm_mad/remotes/vxlan/vxlan_driver.rb index 7fd47e5ad7..110aed7d7f 100644 --- a/src/vnm_mad/remotes/vxlan/vxlan_driver.rb +++ b/src/vnm_mad/remotes/vxlan/vxlan_driver.rb @@ -15,7 +15,7 @@ #--------------------------------------------------------------------------- # require 'vnmmad' -require 'ipaddr' +require 'vxlan' ################################################################################ # This driver tag VM traffic with a VLAN_ID using VXLAN protocol. Features: @@ -25,6 +25,7 @@ require 'ipaddr' # Once activated the VM will be attached to this bridge ################################################################################ class VXLANDriver < VNMMAD::VLANDriver + include VXLAN # DRIVER name and XPATH for relevant NICs DRIVER = "vxlan" @@ -36,56 +37,10 @@ class VXLANDriver < VNMMAD::VLANDriver def initialize(vm, xpath_filter = nil, deploy_id = nil) @locking = true + @attr_vlan_id = ATTR_VLAN_ID + @attr_vlan_dev = ATTR_VLAN_DEV + xpath_filter ||= XPATH_FILTER super(vm, xpath_filter, deploy_id) end - - ############################################################################ - # This function creates and activate a VLAN device - ############################################################################ - def create_vlan_dev - begin - ipaddr = IPAddr.new @nic[:conf][:vxlan_mc] - rescue - ipaddr = IPAddr.new "239.0.0.0" - end - - mc = ipaddr.to_i + @nic[:vlan_id].to_i - mcs = IPAddr.new(mc, Socket::AF_INET).to_s - - mtu = @nic[:mtu] ? "mtu #{@nic[:mtu]}" : "mtu #{@nic[:conf][:vxlan_mtu]}" - ttl = @nic[:conf][:vxlan_ttl] ? "ttl #{@nic[:conf][:vxlan_ttl]}" : "" - - ip_link_conf = "" - - @nic[:ip_link_conf].each do |option, value| - case value - when true - value = "on" - when false - value = "off" - end - - ip_link_conf << "#{option} #{value} " - end - - OpenNebula.exec_and_log("#{command(:ip)} link add #{@nic[:vlan_dev]}"\ - " #{mtu} type vxlan id #{@nic[:vlan_id]} group #{mcs} #{ttl}"\ - " dev #{@nic[:phydev]} #{ip_link_conf}") - - OpenNebula.exec_and_log("#{command(:ip)} link set #{@nic[:vlan_dev]} up") - end - - def get_interface_vlan(name) - text = %x(#{command(:ip)} -d link show #{name}) - return nil if $?.exitstatus != 0 - - text.each_line do |line| - m = line.match(/^\s*vxlan id (\d+)/) - - return m[1] if m - end - - nil - end end