diff --git a/include/NebulaUtil.h b/include/NebulaUtil.h index e4319cbede..2de7d98b40 100644 --- a/include/NebulaUtil.h +++ b/include/NebulaUtil.h @@ -158,6 +158,15 @@ namespace one_util * @return */ std::string float_to_str(const float &num); + + /** + * Checks if a strings matches a regular expression + * + * @param pattern PCRE extended pattern + * @param subject the string to test + * @return 0 on match, another value otherwise + */ + int regex_match(const char *pattern, const char *subject); }; #endif /* _NEBULA_UTIL_H_ */ diff --git a/install.sh b/install.sh index ed8d0231d7..6c9907c2fd 100755 --- a/install.sh +++ b/install.sh @@ -932,17 +932,22 @@ AUTH_PLAIN_FILES="src/authm_mad/remotes/plain/authenticate" # Virtual Network Manager drivers to be installed under $REMOTES_LOCATION/vnm #------------------------------------------------------------------------------- -NETWORK_FILES="src/vnm_mad/remotes/OpenNebulaNetwork.rb \ +NETWORK_FILES="src/vnm_mad/remotes/lib/vnm_driver.rb \ + src/vnm_mad/remotes/lib/vnmmad.rb \ src/vnm_mad/remotes/OpenNebulaNetwork.conf \ - src/vnm_mad/remotes/Firewall.rb \ - src/vnm_mad/remotes/SecurityGroups.rb \ - src/vnm_mad/remotes/IPNetmask.rb \ - src/vnm_mad/remotes/OpenNebulaNic.rb" + src/vnm_mad/remotes/lib/fw_driver.rb \ + src/vnm_mad/remotes/lib/sg_driver.rb \ + src/vnm_mad/remotes/lib/address.rb \ + src/vnm_mad/remotes/lib/command.rb \ + src/vnm_mad/remotes/lib/vm.rb \ + src/vnm_mad/remotes/lib/security_groups.rb \ + src/vnm_mad/remotes/lib/security_groups_iptables.rb \ + src/vnm_mad/remotes/lib/nic.rb" NETWORK_8021Q_FILES="src/vnm_mad/remotes/802.1Q/clean \ src/vnm_mad/remotes/802.1Q/post \ src/vnm_mad/remotes/802.1Q/pre \ - src/vnm_mad/remotes/802.1Q/HostManaged.rb" + src/vnm_mad/remotes/802.1Q/vlan_driver.rb" NETWORK_DUMMY_FILES="src/vnm_mad/remotes/dummy/clean \ src/vnm_mad/remotes/dummy/post \ diff --git a/src/common/NebulaUtil.cc b/src/common/NebulaUtil.cc index 7c47e2c5b9..edd42ad6f5 100644 --- a/src/common/NebulaUtil.cc +++ b/src/common/NebulaUtil.cc @@ -28,6 +28,8 @@ #include #include #include +#include +#include using namespace std; @@ -260,3 +262,24 @@ string one_util::float_to_str(const float &num) return oss.str(); } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int one_util::regex_match(const char *pattern, const char *subject) +{ + int rc; + regex_t re; + + rc = regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB); + + if (rc != 0) + { + return(rc); + } + + rc = regexec(&re, subject, 0, 0, 0); + regfree(&re); + + return rc; +} diff --git a/src/secgroup/SecurityGroup.cc b/src/secgroup/SecurityGroup.cc index c519484fec..814f26d8fc 100644 --- a/src/secgroup/SecurityGroup.cc +++ b/src/secgroup/SecurityGroup.cc @@ -349,6 +349,16 @@ bool SecurityGroup::isValidRule(const VectorAttribute * rule, string& error) con return false; } + if (!value.empty()) + { + const char *range_pattern = "^(([[:digit:]]+|[[:digit:]]+:[[:digit:]]+),)*([[:digit:]]+|[[:digit:]]+:[[:digit:]]+)$"; + if (one_util::regex_match(range_pattern, value.c_str()) != 0) + { + error = "Invalid RANGE specification."; + return false; + } + } + value = rule->vector_value("ICMP_TYPE"); if (!value.empty()) diff --git a/src/vnm_mad/remotes/802.1Q/pre b/src/vnm_mad/remotes/802.1Q/pre index a3be35cc4d..656fca6415 100755 --- a/src/vnm_mad/remotes/802.1Q/pre +++ b/src/vnm_mad/remotes/802.1Q/pre @@ -19,7 +19,7 @@ $: << File.dirname(__FILE__) $: << File.join(File.dirname(__FILE__), "..") -require 'HostManaged' +require 'vlan_driver' -hm = OpenNebulaHM.from_base64(ARGV[0]) +hm = VLANDriver.from_base64(ARGV[0]) exit hm.activate diff --git a/src/vnm_mad/remotes/802.1Q/HostManaged.rb b/src/vnm_mad/remotes/802.1Q/vlan_driver.rb similarity index 64% rename from src/vnm_mad/remotes/802.1Q/HostManaged.rb rename to src/vnm_mad/remotes/802.1Q/vlan_driver.rb index b6883661d6..f3dcf5dabc 100644 --- a/src/vnm_mad/remotes/802.1Q/HostManaged.rb +++ b/src/vnm_mad/remotes/802.1Q/vlan_driver.rb @@ -14,15 +14,26 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -require 'OpenNebulaNetwork' +require 'vnmmad' -class OpenNebulaHM < OpenNebulaNetwork - DRIVER = "802.1Q" +################################################################################ +# This driver tag VM traffic with a VLAN_ID using 802.1Q protocol. Features: +# - Creates a bridge and bind phisycal device if not present +# - Creates a tagged interface for the VM dev.vlan_id +# +# Once activated the VM will be attached to this bridge +################################################################################ +class VLANDriver < VNMMAD::VNMDriver + # DRIVER name and XPATH for relevant NICs + DRIVER = "802.1Q" XPATH_FILTER = "TEMPLATE/NIC[VLAN='YES']" + ############################################################################ + # Creatges the driver device operations are not locked + ############################################################################ def initialize(vm, deploy_id = nil, hypervisor = nil) - super(vm,XPATH_FILTER,deploy_id,hypervisor) + super(vm, XPATH_FILTER, deploy_id, hypervisor) @locking = false lock @@ -30,10 +41,14 @@ class OpenNebulaHM < OpenNebulaNetwork unlock end + ############################################################################ + # Activate the driver and creates bridges and tags devices as needed. + ############################################################################ def activate lock vm_id = @vm['ID'] + process do |nic| bridge = nic[:bridge] dev = nic[:phydev] @@ -66,23 +81,29 @@ class OpenNebulaHM < OpenNebulaNetwork return 0 end + ############################################################################ + # Private interface, methods to manage bridges and VLAN tags through the + # brctl and ip commands + ############################################################################ + private + def bridge_exists?(bridge) @bridges.keys.include? bridge end def create_bridge(bridge) - OpenNebula.exec_and_log("#{COMMANDS[:brctl]} addbr #{bridge}") + OpenNebula.exec_and_log("#{command(:brctl)} addbr #{bridge}") @bridges[bridge] = Array.new end def device_exists?(dev, vlan=nil) dev = "#{dev}.#{vlan}" if vlan - `#{COMMANDS[:ip]} link show #{dev}` + `#{command(:ip)} link show #{dev}` $?.exitstatus == 0 end def create_dev_vlan(dev, vlan) - cmd = "#{COMMANDS[:ip]} link add link #{dev}" + cmd = "#{command(:ip)} link add link #{dev}" cmd << " name #{dev}.#{vlan} type vlan id #{vlan}" OpenNebula.exec_and_log(cmd) @@ -90,18 +111,20 @@ class OpenNebulaHM < OpenNebulaNetwork def attached_bridge_dev?(bridge, dev, vlan=nil) return false if !bridge_exists? bridge + dev = "#{dev}.#{vlan}" if vlan @bridges[bridge].include? dev end def attach_brigde_dev(bridge, dev, vlan=nil) dev = "#{dev}.#{vlan}" if vlan - OpenNebula.exec_and_log("#{COMMANDS[:brctl]} addif #{bridge} #{dev}") + + OpenNebula.exec_and_log("#{command(:brctl)} addif #{bridge} #{dev}") @bridges[bridge] << dev end def ifup(dev, vlan=nil) dev = "#{dev}.#{vlan}" if vlan - OpenNebula.exec_and_log("#{COMMANDS[:ip]} link set #{dev} up") + OpenNebula.exec_and_log("#{command(:ip)} link set #{dev} up") end end diff --git a/src/vnm_mad/remotes/Firewall.rb b/src/vnm_mad/remotes/Firewall.rb deleted file mode 100644 index 9440eb474b..0000000000 --- a/src/vnm_mad/remotes/Firewall.rb +++ /dev/null @@ -1,180 +0,0 @@ -# -------------------------------------------------------------------------- # -# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # -# # -# 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. # -#--------------------------------------------------------------------------- # - -class OpenNebulaFirewall < OpenNebulaNetwork - DRIVER = "fw" - - XPATH_FILTER = "TEMPLATE/NIC[ICMP|WHITE_PORTS_TCP|WHITE_PORTS_UDP|" << - "BLACK_PORTS_TCP|BLACK_PORTS_UDP]" - - def initialize(vm, deploy_id = nil, hypervisor = nil) - super(vm,XPATH_FILTER,deploy_id,hypervisor) - @locking = true - end - - def activate - lock - - vm_id = @vm['ID'] - process do |nic| - #:white_ports_tcp => iptables_range - #:white_ports_udp => iptables_range - #:black_ports_tcp => iptables_range - #:black_ports_udp => iptables_range - #:icmp => 'DROP' or 'NO' - - nic_rules = Array.new - - chain = "one-#{vm_id}-#{nic[:network_id]}" - tap = nic[:tap] - - next if chain_exists?(chain) - - if tap - #TCP - if range = nic[:white_ports_tcp] - nic_rules << filter_established(chain, :tcp, :accept) - nic_rules << filter_ports(chain, :tcp, range, :accept) - nic_rules << filter_protocol(chain, :tcp, :drop) - elsif range = nic[:black_ports_tcp] - nic_rules << filter_ports(chain, :tcp, range, :drop) - end - - #UDP - if range = nic[:white_ports_udp] - nic_rules << filter_established(chain, :udp, :accept) - nic_rules << filter_ports(chain, :udp, range, :accept) - nic_rules << filter_protocol(chain, :udp, :drop) - elsif range = nic[:black_ports_udp] - nic_rules << filter_ports(chain, :udp, range, :drop) - end - - #ICMP - if nic[:icmp] - if %w(no drop).include? nic[:icmp].downcase - nic_rules << filter_established(chain, :icmp, :accept) - nic_rules << filter_protocol(chain, :icmp, :drop) - end - end - - process_chain(chain, tap, nic_rules) - end - end - - unlock - end - - def deactivate - lock - - vm_id = @vm['ID'] - process do |nic| - chain = "one-#{vm_id}-#{nic[:network_id]}" - iptables_out = `#{COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` - if m = iptables_out.match(/.*#{chain}.*/) - rule_num = m[0].split(/\s+/)[0] - purge_chain(chain, rule_num) - end - end - - unlock - end - - def purge_chain(chain, rule_num) - rules = Array.new - rules << rule("-D FORWARD #{rule_num}") - rules << rule("-F #{chain}") - rules << rule("-X #{chain}") - run_rules rules - end - - def process_chain(chain, tap, nic_rules) - rules = Array.new - if !nic_rules.empty? - # new chain - rules << new_chain(chain) - # move tap traffic to chain - rules << tap_to_chain(tap, chain) - - rules << nic_rules - end - run_rules rules - end - - def filter_established(chain, protocol, policy) - policy = policy.to_s.upcase - rule "-A #{chain} -p #{protocol} -m state --state ESTABLISHED -j #{policy}" - end - - def run_rules(rules) - rules.flatten.each do |rule| - OpenNebula.exec_and_log(rule) - end - end - - def range?(range) - range.match(/^(?:(?:\d+|\d+:\d+),)*(?:\d+|\d+:\d+)$/) - end - - def filter_protocol(chain, protocol, policy) - policy = policy.to_s.upcase - rule "-A #{chain} -p #{protocol} -j #{policy}" - end - - def filter_ports(chain, protocol, range, policy) - policy = policy.to_s.upcase - range.gsub!(/\s+/,"") - - if range? range - rule "-A #{chain} -p #{protocol} -m multiport --dports #{range} -j #{policy}" - end - end - - def tap_to_chain(tap, chain) - iptables_out = `#{COMMANDS[:iptables]} -n -v --line-numbers -L FORWARD` - - # Insert the rule on top of the 'opennebula' chain if it exists, so it - # doesn't conflict with the security groups driver - index = nil - iptables_out.lines.each do |line| - fields = line.split - if fields.include?("opennebula") && fields.include?("--physdev-is-bridged") - index = fields[0] - break - end - end - - if index - rule "-I FORWARD #{index} -m physdev --physdev-out #{tap} -j #{chain}" - else - rule "-A FORWARD -m physdev --physdev-out #{tap} -j #{chain}" - end - end - - def new_chain(chain) - rule "-N #{chain}" - end - - def chain_exists?(chain) - iptables_nl =`#{COMMANDS[:iptables]} -nL` - chains = iptables_nl.scan(/(one-.*?) .*references/).flatten - chains.include? chain - end - - def rule(rule) - "#{COMMANDS[:iptables]} #{rule}" - end -end diff --git a/src/vnm_mad/remotes/IPNetmask.rb b/src/vnm_mad/remotes/IPNetmask.rb deleted file mode 100644 index e112ab391d..0000000000 --- a/src/vnm_mad/remotes/IPNetmask.rb +++ /dev/null @@ -1,198 +0,0 @@ -# -------------------------------------------------------------------------- # -# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # -# # -# 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. # -#--------------------------------------------------------------------------- # - -################################################################################ -# IP and NETMASK Library -################################################################################ - -class IP - include Comparable - attr_accessor :ip - - def initialize(ip) - @ip = ip - end - - def to_s - @ip - end - - def to_i - @ip.split(".").inject(0) {|t,e| (t << 8) + e.to_i } - end - - def to_hex - "0x" + to_i.to_s(16).rjust(8, '0') - end - - def to_bin - "0b" + to_i.to_s(2).rjust(16, '0') - end - - def to_hex_groups(p="") - to_i.to_s(16).rjust(8, '0').scan(/.{2}/).collect{|e| p+e}.join('.') - end - - def to_bin_groups(p="") - to_i.to_s(2).rjust(16, '0').scan(/.{8}/).collect{|e| p+e}.join('.') - end - - def self.from_i(i) - ip = 3.downto(0).collect {|s| (i >> 8*s) & 0xff }.join('.') - self.new(ip) - end - - def &(another_ip) - IP.from_i(self.to_i & another_ip.to_i) - end - - def +(size) - IP.from_i(self.to_i + size) - end - - def -(e) - if e.instance_of? Fixnum - IP.from_i(self.to_i - e) - else - e.to_i - self.to_i - end - end - - def <=>(another_ip) - self.to_i <=> another_ip.to_i - end -end - -class Netmask < IP - def self.from_cidr(cidr) - self.from_i(0xffffffff ^ 2**(32-cidr)-1) - end - - def to_cidr - 32 - Math.log((to_i ^ 0xffffffff) + 1, 2).to_i - end -end - -class Net - attr_accessor :ip, :netmask - - def initialize(ip, netmask = nil) - if netmask - @ip, @netmask = ip, netmask - else - ip, netmask = ip.split('/') - @ip = IP.new(ip) - @netmask = Netmask.from_cidr(netmask.to_i) if netmask - end - - @network_address = network_address - @last_address = last_address - end - - def network_address - IP.from_i(@ip.to_i & @netmask.to_i) - end - - def last_address - IP.from_i(@ip.to_i | (@netmask.to_i ^ 0xffffffff)) - end - - def info - s = "" - s << @network_address.to_s.ljust(15) - s << " /" - s << @netmask.to_cidr.to_s.rjust(2) - s << " " - s << @network_address.to_s.ljust(15) - s << " " - s << last_address.to_s.ljust(15) - - s - end - - def to_s - "#{@network_address}/#{@netmask.to_cidr}" - end - - def next_net - next_ip = IP.from_i(last_address.to_i + 1) - Net.new(next_ip, @netmask) - end - - def between?(ip_start, ip_end) - network_address >= ip_start && last_address <= ip_end - end -end - -class Range - def initialize(ip_start, size) - @ip_start = IP.new(ip_start) - @ip_end = @ip_start + size - end - - def get_nets - self.class.get_nets(@ip_start, @ip_end) - end - - def largest_subnet - self.class.largest_subnet(@ip_start, @ip_end) - end - - def self.get_nets(ip_start, ip_end) - nets = [] - - net_m = largest_subnet(ip_start, ip_end) - - # left scraps - if ip_start < net_m.network_address - nets.concat get_nets(ip_start, net_m.network_address - 1) - end - - nets << net_m - - # right scraps - if net_m.last_address < ip_end - nets.concat get_nets(net_m.last_address + 1, ip_end) - end - - nets - end - - def self.largest_subnet(ip_start, ip_end) - size = ip_start - ip_end - - # start with the largest subnet - if size > 0 - cidr = 32 - Math.log(size, 2).floor - else - cidr = 32 - end - - fits = false - - while !fits - net = Net.new(ip_start, Netmask.from_cidr(cidr)) - net = net.next_net if ip_start > net.network_address - - cidr += 1 - break if cidr > 32 - - fits = net.between?(ip_start, ip_end) - end - - net - end -end diff --git a/src/vnm_mad/remotes/OpenNebulaNetwork.rb b/src/vnm_mad/remotes/OpenNebulaNetwork.rb deleted file mode 100644 index fdff12df33..0000000000 --- a/src/vnm_mad/remotes/OpenNebulaNetwork.rb +++ /dev/null @@ -1,248 +0,0 @@ -# -------------------------------------------------------------------------- # -# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # -# # -# 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 'rexml/document' -require 'base64' -require 'yaml' - -require 'OpenNebulaNic' - -require 'scripts_common' - -include OpenNebula - -begin - CONF = YAML.load_file( - File.join(File.dirname(__FILE__), "OpenNebulaNetwork.conf") - ) -rescue - CONF = { - :start_vlan => 2 - } -end - -def get_xen_command - if system("ps axuww | grep -v grep | grep '\\bxen\\b'") - "sudo xm" - else - "sudo xl" - end -end - -COMMANDS = { - :ebtables => "sudo ebtables", - :iptables => "sudo iptables", - :brctl => "sudo brctl", - :ip => "sudo ip", - :virsh => "virsh -c qemu:///system", - :xm => get_xen_command, - :ovs_vsctl=> "sudo ovs-vsctl", - :ovs_ofctl=> "sudo ovs-ofctl", - :lsmod => "lsmod", - :ipset => "sudo ipset" -} - -# Set PATH -ENV['PATH'] = "#{ENV['PATH']}:/bin:/sbin:/usr/bin" - -class VM - attr_accessor :nics, :vm_info, :deploy_id, :vm_root - - def nic_build_hash(nic_element,nic) - nic_element.elements.each('*') do |nic_attribute| - key = nic_attribute.name.downcase.to_sym - - - if nic_attribute.has_elements? - data = {} - nic_build_hash(nic_attribute,data) - else - data = nic_attribute.text - end - - if nic[key] - if nic[key].instance_of?(Array) - nic[key] << data - else - nic[key] = [nic[key], data] - end - else - nic[key] = data - end - end - end - - def initialize(vm_root, xpath_filter, deploy_id, hypervisor) - @vm_root = vm_root - @xpath_filter = xpath_filter - @deploy_id = deploy_id - @hypervisor = hypervisor - @vm_info = Hash.new - - @deploy_id = nil if deploy_id == "-" - - nics = Nics.new(@hypervisor) - - @vm_root.elements.each(@xpath_filter) do |nic_element| - nic = nics.new_nic - - nic_build_hash(nic_element,nic) - - nic.get_info(self) - nic.get_tap(self) - - nics << nic - end - - @nics = nics - end - - def each_nic(block) - if @nics != nil - @nics.each do |the_nic| - block.call(the_nic) - end - end - end - - def [](element) - if @vm_root - val = @vm_root.elements[element] - if !val.nil? and val.text - return val.text - end - end - nil - end -end - -class OpenNebulaNetwork - attr_reader :hypervisor, :vm - - def self.from_base64(vm_64, deploy_id = nil, hypervisor = nil) - vm_xml = Base64::decode64(vm_64) - self.new(vm_xml, deploy_id, hypervisor) - end - - def self.filter_driver(vm_64, deploy_id = nil, hypervisor = nil) - vm_xml = Base64::decode64(vm_64) - - if self.has_fw_attrs?(vm_xml) - OpenNebulaFirewall.new(vm_xml, deploy_id, hypervisor) - else - OpenNebulaSG.new(vm_xml, deploy_id, hypervisor) - end - end - - def initialize(vm_tpl, xpath_filter, deploy_id = nil, hypervisor = nil) - @locking = false - - if !hypervisor - @hypervisor = detect_hypervisor - else - @hypervisor = hypervisor - end - - @vm = VM.new(REXML::Document.new(vm_tpl).root, xpath_filter, deploy_id, @hypervisor) - end - - def lock - if @locking - driver_name = self.class.name.downcase - @locking_file = File.open("/tmp/onevnm-#{driver_name}-lock","w") - @locking_file.flock(File::LOCK_EX) - end - end - - def unlock - if @locking - @locking_file.close - end - end - - def process(&block) - @vm.each_nic(block) - end - - def detect_hypervisor - lsmod = `#{COMMANDS[:lsmod]}` - xen_file = "/proc/xen/capabilities" - - if File.exists?(xen_file) - "xen" - elsif lsmod.match(/kvm/) - "kvm" - else - nil - end - end - - def get_interfaces - bridges = Hash.new - brctl_exit =`#{COMMANDS[:brctl]} show` - - cur_bridge = "" - - brctl_exit.split("\n")[1..-1].each do |l| - l = l.split - - if l.length > 1 - cur_bridge = l[0] - - bridges[cur_bridge] = Array.new - bridges[cur_bridge] << l[3] if l[3] - else - bridges[cur_bridge] << l[0] - end - end - - bridges - end -end - -# Dynamic factory method for the filter class -require 'Firewall' -require 'SecurityGroups' -class OpenNebulaNetwork - # Returns a filter object based on the contents of the template - # - # @return OpenNebulaFirewall or OpenNebulaSG object - def self.filter_driver(vm_64, deploy_id = nil, hypervisor = nil) - vm_xml = Base64::decode64(vm_64) - - if self.has_fw_attrs?(vm_xml) - OpenNebulaFirewall.new(vm_xml, deploy_id, hypervisor) - else - OpenNebulaSG.new(vm_xml, deploy_id, hypervisor) - end - end - - # Returns true if the template contains the deprecated firewall attributes: - # - ICMP - # - WHITE_PORTS_TCP - # - WHITE_PORTS_UDP - # - BLACK_PORTS_TCP - # - BLACK_PORTS_UDP - # - # @return Boolean - def self.has_fw_attrs?(vm_xml) - vm_root = REXML::Document.new(vm_xml).root - !vm_root.elements[OpenNebulaFirewall::XPATH_FILTER].nil? - end -end diff --git a/src/vnm_mad/remotes/OpenNebulaNic.rb b/src/vnm_mad/remotes/OpenNebulaNic.rb deleted file mode 100644 index 24b4ee7643..0000000000 --- a/src/vnm_mad/remotes/OpenNebulaNic.rb +++ /dev/null @@ -1,136 +0,0 @@ -# -------------------------------------------------------------------------- # -# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # -# # -# 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. # -#--------------------------------------------------------------------------- # - -# This class represents the NICS of a VM -class Nics < Array - def initialize(hypervisor) - case hypervisor - when "kvm" - @nicClass = NicKVM - when "xen" - @nicClass = NicXen - when "vmware" - @nicClass = NicVMware - end - end - - def new_nic - @nicClass.new - end -end - -# A NIC using KVM. This class implements functions to get the physical interface -# that the NIC is using -class NicKVM < Hash - def initialize - super(nil) - end - - def get_info(vm) - if vm.deploy_id - deploy_id = vm.deploy_id - else - deploy_id = vm['DEPLOY_ID'] - end - - if deploy_id and vm.vm_info[:dumpxml].nil? - vm.vm_info[:dumpxml] = `#{COMMANDS[:virsh]} dumpxml #{deploy_id} \ - 2>/dev/null` - - vm.vm_info.each_key do |k| - vm.vm_info[k] = nil if vm.vm_info[k].to_s.strip.empty? - end - end - end - - def get_tap(vm) - dumpxml = vm.vm_info[:dumpxml] - - if dumpxml - dumpxml_root = REXML::Document.new(dumpxml).root - - xpath = "devices/interface[@type='bridge']/" - xpath << "mac[@address='#{self[:mac]}']/../target" - tap = dumpxml_root.elements[xpath] - - if tap - self[:tap] = tap.attributes['dev'] - end - end - self - end -end - - -# A NIC using Xen. This class implements functions to get the physical interface -# that the NIC is using -class NicXen < Hash - def initialize - super(nil) - end - - def get_info(vm) - if vm.deploy_id - deploy_id = vm.deploy_id - else - deploy_id = vm['DEPLOY_ID'] - end - - if deploy_id and (vm.vm_info[:domid].nil? or vm.vm_info[:networks].nil?) - vm.vm_info[:domid] =`#{COMMANDS[:xm]} domid #{deploy_id}`.strip - vm.vm_info[:networks] =`#{COMMANDS[:xm]} network-list #{deploy_id}` - - vm.vm_info.each_key do |k| - vm.vm_info[k] = nil if vm.vm_info[k].to_s.strip.empty? - end - end - end - - def get_tap(vm) - domid = vm.vm_info[:domid] - - if domid - networks = vm.vm_info[:networks].split("\n")[1..-1] - networks.each do |net| - n = net.split - - iface_id = n[0] - iface_mac = n[2] - - if iface_mac == self[:mac] - self[:tap] = "vif#{domid}.#{iface_id}" - break - end - end - end - self - end -end - -# A NIC using VMware. This class implements functions to get the physical interface -# that the NIC is using -class NicVMware < Hash - def initialize - super(nil) - end - - def get_info(vm) - end - - def get_tap(vm) - self - end -end diff --git a/src/vnm_mad/remotes/SecurityGroups.rb b/src/vnm_mad/remotes/SecurityGroups.rb deleted file mode 100644 index c79edeb4a8..0000000000 --- a/src/vnm_mad/remotes/SecurityGroups.rb +++ /dev/null @@ -1,597 +0,0 @@ -# -------------------------------------------------------------------------- # -# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # -# # -# 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 'IPNetmask' - -################################################################################ -# SecurityGroups and Rules -################################################################################ - -class CommandsError < StandardError; end - -class Commands - def initialize - clear! - end - - def add(cmd) - if cmd.instance_of?(String) - @commands << cmd - else - @commands.concat(cmd.to_a) - end - end - - def method_missing(m, *args, &block) - if COMMANDS.keys.include?(m) - @commands << "#{COMMANDS[m]} #{args.join(' ')}" - else - super - end - end - - def run! - out = "" - - @commands.each{|c| - - out << `#{c}` - - if !$?.success? - clear! - raise CommandsError.new(c), "Command Error: #{c}" - end - } - - clear! - out - end - - def uniq! - @commands.uniq! - end - - def clear! - @commands = [] - end - - def to_a - @commands - end -end - -class RuleError < StandardError; end - -class Rule - TYPES = { - # PROTOCOL, RULE_TYPE, NET, RANGE, ICMP_TYPE - [ 1, 1, 0, 0, 0 ] => :protocol, - [ 1, 1, 0, 1, 0 ] => :portrange, - [ 1, 1, 0, 0, 1 ] => :icmp_type, - [ 1, 1, 1, 0, 0 ] => :net, - [ 1, 1, 1, 1, 0 ] => :net_portrange, - [ 1, 1, 1, 0, 1 ] => :net_icmp_type - } - - ICMP_TYPES = %w{3 5 11 12 0 4 8 9 10 13 14 17 18} - - ICMP_TYPES_EXPANDED = { - 3 => [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15], - 5 => [0, 1, 2, 3], - 11 => [0, 1], - 12 => [0, 1] - } - - def initialize(rule) - @rule = rule - @commands = Commands.new - - if !valid? - raise RuleError.new, "Invalid Rule: #{error_message}" - end - end - - # Getters - def protocol - p = @rule[:protocol].downcase.to_sym rescue nil - - if p == :ipsec - :esp - else - p - end - end - - def rule_type - @rule[:rule_type].downcase.to_sym rescue nil - end - - def range - @rule[:range] - end - - def net - return nil if @rule[:ip].nil? || @rule[:size].nil? - - r = Range.new(@rule[:ip], @rule[:size].to_i) - r.get_nets.collect{|n| n.to_s} - end - - def icmp_type - @rule[:icmp_type] - end - - def icmp_type_expand - if (codes = ICMP_TYPES_EXPANDED[icmp_type.to_i]) - codes.collect{|e| "#{icmp_type}/#{e}"} - else - ["#{icmp_type}/0"] - end - end - - # Helper - - def valid? - valid = true - error_message = [] - - if type.nil? - error_message << "Invalid combination of rule attributes: " - error_message << type(true).to_s - valid = false - end - - if !protocol || ![:all, :tcp, :udp, :icmp, :esp].include?(protocol) - error_message << "Invalid protocol: #{protocol}" - valid = false - end - - if !rule_type || ![:inbound, :outbound].include?(rule_type) - error_message << "Invalid rule_type: #{rule_type}" - valid = false - end - - if range && !range.match(/^(?:(?:\d+|\d+:\d+),)*(?:\d+|\d+:\d+)$/) - error_message << "Invalid range: #{range}" - valid = false - end - - if icmp_type && !ICMP_TYPES.include?(icmp_type) - error_message << "ICMP Type '#{icmp_type}' not supported. Valid list is '#{ICMP_TYPES.join(',')}'" - end - - if icmp_type && !(protocol == :icmp) - error_message << "Protocol '#{protocol}' does not support ICMP TYPES" - valid = false - end - - if range && ![:tcp, :udp].include?(protocol) - error_message << "Protocol '#{protocol}' does not support port ranges" - valid = false - end - - if net && !valid_net? - error_message << "Invalid net: IP:'#{@rule[:ip]}' SIZE:'#{@rule[:size]}'" - valid = false - end - - return [valid, error_message.join("\n")] - end - - def valid_net? - @rule[:ip].match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) && \ - @rule[:size].match(/^\d+$/) - end - - # Returns the rule type. Rules currently support these (final and relevant) - # attributes. - # - # PROTOCOL (mandatory) - # - Specifies the protocol of the rule - # - values: ['ALL', 'TCP', 'UDP', 'ICMP', 'IPSEC'] - # - # RULE_TYPE (mandatory) - # - Specifies the direction of application of the rule - # - values: ['INBOUND', 'OUTBOUND'] - # - # RANGE (optional) - # - only works for protocols ['TCP', 'UDP'] - # - uses the iptables multiports syntax - # - # ICMP_TYPE (optional) - # - Only works for protocol 'ICMP' - # - Is either in the form of '' or '/', where both - # '' and '' are integers. This class has a helper method - # tgat expands '' into all the '/' subtypes. - # - # IP and SIZE (optional but must be specified together) - # - Can be applied to any protocol - # - IP is the first valid IP and SIZE is the number of consecutive IPs - # - # Depending on the combination of these attributes we can obtaine 4 rule - # rule types (some with subtypes): - # - # ['PROTOCOL', 'RULE_TYPE'] => Type 1: 'protocol' - # ['PROTOCOL', 'RULE_TYPE', 'RANGE'] => Type 2A: 'portrange' - # ['PROTOCOL', 'RULE_TYPE', 'ICMP_TYPE'] => Type 2B: 'icmp_type' - # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE'] => Type 3: 'net' - # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE', 'RANGE'] => Type 4A: 'net_portrange' - # ['PROTOCOL', 'RULE_TYPE', 'IP', 'SIZE', 'ICMP_TYPE'] => Type 4B: 'net_icmp_type' - # - # @return [Symbol] The rule type - def type(only_key = false) - key = [protocol, rule_type, net, range, icmp_type].collect do |e| - !!e ? 1 : 0 - end - - only_key ? key : TYPES[key] - end -end - -class SecurityGroup - def initialize(vm, nic, sg_id, rules) - @vm = vm - @nic = nic - @sg_id = sg_id - - @rules = [] - @invalid_rules = [] - - rules.each do |rule| - @rules << Rule.new(rule) - end if rules - end -end - -################################################################################ -# IPTables Implementation -################################################################################ - -class SecurityGroupIPTables < SecurityGroup - GLOBAL_CHAIN = "opennebula" - - def initialize(vm, nic, sg_id, rules) - super - - @commands = Commands.new - - @vars = self.class.vars(@vm, @nic, @sg_id) - - @chain_in = @vars[:chain_in] - @chain_out = @vars[:chain_out] - @set_sg_in = @vars[:set_sg_in] - @set_sg_out = @vars[:set_sg_out] - end - - def process_rules - @rules.each do |rule| - case rule.type - when :protocol - chain = rule.rule_type == :inbound ? @chain_in : @chain_out - @commands.iptables("-A #{chain} -p #{rule.protocol} -j RETURN") - - when :portrange - chain = rule.rule_type == :inbound ? @chain_in : @chain_out - @commands.iptables("-A #{chain} -p #{rule.protocol} -m multiport --dports #{rule.range} -j RETURN") - - when :icmp_type - chain = rule.rule_type == :inbound ? @chain_in : @chain_out - @commands.iptables("-A #{chain} -p icmp --icmp-type #{rule.icmp_type} -j RETURN") - - when :net - if rule.rule_type == :inbound - chain = @chain_in - set = "#{@set_sg_in}-#{rule.protocol}-n" - dir = "src" - else - chain = @chain_out - set = "#{@set_sg_out}-#{rule.protocol}-n" - dir = "dst" - end - - @commands.ipset("create #{set} hash:net") - @commands.iptables("-A #{chain} -p #{rule.protocol} -m set --match-set #{set} #{dir} -j RETURN") - - rule.net.each do |n| - @commands.ipset("add -exist #{set} #{n}") - end - - when :net_portrange - if rule.rule_type == :inbound - chain = @chain_in - set = @set_sg_in + "-nr" - dir = "src,dst" - else - chain = @chain_in - set = @set_sg_in + "-n" - dir = "dst,dst" - end - - @commands.ipset("create #{set} hash:net,port") - @commands.iptables("-A #{chain} -m set --match-set #{set} #{dir} -j RETURN") - - rule.net.each do |n| - rule.range.split(",").each do |r| - r.gsub!(":","-") - net_range = "#{n},#{rule.protocol}:#{r}" - @commands.ipset("add -exist #{set} #{net_range}") - end - end - - when :net_icmp_type - if rule.rule_type == :inbound - chain = @chain_in - set = @set_sg_in + "-nr" - dir = "src,dst" - else - chain = @chain_in - set = @set_sg_in + "-n" - dir = "dst,dst" - end - - @commands.ipset("create #{set} hash:net,port") - @commands.iptables("-A #{chain} -m set --match-set #{set} #{dir} -j RETURN") - - rule.net.each do |n| - rule.icmp_type_expand.each do |type_code| - net_range = "#{n},icmp:#{type_code}" - @commands.ipset("add -exist #{set} #{net_range}") - end if rule.icmp_type_expand - end - end - end - - @commands.uniq! - end - - def run! - @commands.run! - end - - ############################################################################ - # Static methods - ############################################################################ - - def self.global_bootstrap - info = self.info - - if !info[:iptables_s].split("\n").include? "-N #{GLOBAL_CHAIN}" - commands = Commands.new - - commands.iptables "-N #{GLOBAL_CHAIN}" - commands.iptables "-A FORWARD -m physdev --physdev-is-bridged -j #{GLOBAL_CHAIN}" - commands.iptables "-A #{GLOBAL_CHAIN} -j ACCEPT" - - commands.run! - end - end - - def self.nic_pre(vm, nic) - commands = Commands.new - - vars = self.vars(vm, nic) - - chain = vars[:chain] - chain_in = vars[:chain_in] - chain_out = vars[:chain_out] - - # create chains - commands.iptables "-N #{chain_in}" # inbound - commands.iptables "-N #{chain_out}" # outbound - - # Send traffic to the NIC chains - commands.iptables"-I #{GLOBAL_CHAIN} -m physdev --physdev-out #{nic[:tap]} --physdev-is-bridged -j #{chain_in}" - commands.iptables"-I #{GLOBAL_CHAIN} -m physdev --physdev-in #{nic[:tap]} --physdev-is-bridged -j #{chain_out}" - - # Mac-spofing - if nic[:filter_mac_spoofing] == "YES" - commands.iptables"-A #{chain_out} -m mac ! --mac-source #{nic[:mac]} -j DROP" - end - - # IP-spofing - if nic[:filter_ip_spoofing] == "YES" - commands.iptables"-A #{chain_out} ! --source #{nic[:ip]} -j DROP" - end - - # Related, Established - commands.iptables"-A #{chain_in} -m state --state ESTABLISHED,RELATED -j ACCEPT" - commands.iptables"-A #{chain_out} -m state --state ESTABLISHED,RELATED -j ACCEPT" - - commands.run! - end - - def self.nic_post(vm, nic) - vars = self.vars(vm, nic) - chain_in = vars[:chain_in] - chain_out = vars[:chain_out] - - commands = Commands.new - commands.iptables("-A #{chain_in} -j DROP") - commands.iptables("-A #{chain_out} -j DROP") - - commands.run! - end - - def self.nic_deactivate(vm, nic) - vars = self.vars(vm, nic) - chain = vars[:chain] - chain_in = vars[:chain_in] - chain_out = vars[:chain_out] - - info = self.info - iptables_forwards = info[:iptables_forwards] - iptables_s = info[:iptables_s] - ipset_list = info[:ipset_list] - - commands = Commands.new - - iptables_forwards.lines.reverse_each do |line| - fields = line.split - if [chain_in, chain_out].include?(fields[1]) - n = fields[0] - commands.iptables("-D #{GLOBAL_CHAIN} #{n}") - end - end - - remove_chains = [] - iptables_s.lines.each do |line| - if line.match(/^-N #{chain}/) - remove_chains << line.split[1] - end - end - remove_chains.each {|c| commands.iptables("-F #{c}") } - remove_chains.each {|c| commands.iptables("-X #{c}") } - - ipset_list.lines.each do |line| - if line.match(/^#{chain}/) - set = line.strip - commands.ipset("destroy #{set}") - end - end - - commands.run! - end - - def self.info - commands = Commands.new - - commands.iptables("-S") - iptables_s = commands.run! - - if iptables_s.match(/^-N #{GLOBAL_CHAIN}$/) - commands.iptables("-L #{GLOBAL_CHAIN} --line-numbers") - iptables_forwards = commands.run! - else - iptables_forwards = "" - end - - commands.ipset("list -name") - ipset_list = commands.run! - - { - :iptables_forwards => iptables_forwards, - :iptables_s => iptables_s, - :ipset_list => ipset_list - } - end - - - def self.vars(vm, nic, sg_id = nil) - vm_id = vm['ID'] - nic_id = nic[:nic_id] - - vars = {} - - vars[:vm_id] = vm_id, - vars[:nic_id] = nic_id, - vars[:chain] = "one-#{vm_id}-#{nic_id}", - vars[:chain_in] = "#{vars[:chain]}-i", - vars[:chain_out] = "#{vars[:chain]}-o" - - if sg_id - vars[:set_sg_in] = "#{vars[:chain]}-#{sg_id}-i" - vars[:set_sg_out] = "#{vars[:chain]}-#{sg_id}-o" - end - - vars - end -end - -################################################################################ -# OpenNebula Firewall with Security Groups Based on IPTables (KVM and Xen) -################################################################################ - -class OpenNebulaSG < OpenNebulaNetwork - DRIVER = "sg" - XPATH_FILTER = "TEMPLATE/NIC" - SECURITY_GROUP_CLASS = SecurityGroupIPTables - - def initialize(vm, deploy_id = nil, hypervisor = nil) - super(vm, XPATH_FILTER, deploy_id, hypervisor) - @locking = true - @commands = Commands.new - - get_security_group_rules - end - - def get_security_group_rules - rules = {} - @vm.vm_root.elements.each('TEMPLATE/SECURITY_GROUP_RULE') do |r| - security_group_rule = {} - - r.elements.each do |e| - key = e.name.downcase.to_sym - security_group_rule[key] = e.text - end - - id = security_group_rule[:security_group_id] - rules[id] = [] if rules[id].nil? - rules[id] << security_group_rule - end - @security_group_rules = rules - end - - def activate - deactivate - lock - - # Global Bootstrap - SECURITY_GROUP_CLASS.global_bootstrap - - # Process the rules - @vm.nics.each do |nic| - next if nic[:security_groups].nil? \ - && nic[:filter_mac_spoofing] != "YES" \ - && nic[:filter_ip_spoofing] != "YES" - - - SECURITY_GROUP_CLASS.nic_pre(@vm, nic) - - sg_ids = nic[:security_groups].split(",") - sg_ids.each do |sg_id| - rules = @security_group_rules[sg_id] - sg = SECURITY_GROUP_CLASS.new(@vm, nic, sg_id, rules) - - begin - sg.process_rules - sg.run! - rescue Exception => e - unlock - deactivate - raise e - end - end - - SECURITY_GROUP_CLASS.nic_post(@vm, nic) - end - - unlock - end - - def deactivate - lock - - begin - @vm.nics.each do |nic| - SECURITY_GROUP_CLASS.nic_deactivate(@vm, nic) - end - rescue Exception => e - raise e - ensure - unlock - end - end -end diff --git a/src/vnm_mad/remotes/ebtables/Ebtables.rb b/src/vnm_mad/remotes/ebtables/Ebtables.rb index 4e48fd38ff..bae81aabd9 100644 --- a/src/vnm_mad/remotes/ebtables/Ebtables.rb +++ b/src/vnm_mad/remotes/ebtables/Ebtables.rb @@ -14,9 +14,9 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -require 'OpenNebulaNetwork' +require 'vnmmad' -class EbtablesVLAN < OpenNebulaNetwork +class EbtablesVLAN < VNMMAD::VNMDriver DRIVER = "ebtables" XPATH_FILTER = "TEMPLATE/NIC[VLAN='YES']" diff --git a/src/vnm_mad/remotes/ebtables/clean b/src/vnm_mad/remotes/ebtables/clean index 2e462e6d94..e552268bc9 100755 --- a/src/vnm_mad/remotes/ebtables/clean +++ b/src/vnm_mad/remotes/ebtables/clean @@ -19,7 +19,6 @@ $: << File.dirname(__FILE__) $: << File.join(File.dirname(__FILE__), "..") -require 'OpenNebulaNetwork' require 'Ebtables' template64 = ARGV[0] @@ -27,5 +26,5 @@ template64 = ARGV[0] onevlan = EbtablesVLAN.from_base64(template64) onevlan.deactivate -filter_driver = OpenNebulaNetwork.filter_driver(template64) +filter_driver = VNMMAD::VNMDriver.filter_driver(template64) filter_driver.deactivate diff --git a/src/vnm_mad/remotes/ebtables/post b/src/vnm_mad/remotes/ebtables/post index 88cada3ec7..7199b51231 100755 --- a/src/vnm_mad/remotes/ebtables/post +++ b/src/vnm_mad/remotes/ebtables/post @@ -19,7 +19,6 @@ $: << File.dirname(__FILE__) $: << File.join(File.dirname(__FILE__), "..") -require 'OpenNebulaNetwork' require 'Ebtables' template64 = ARGV[0] @@ -29,7 +28,7 @@ onevlan = EbtablesVLAN.from_base64(template64, deploy_id) onevlan.activate begin - filter_driver = OpenNebulaNetwork.filter_driver(template64, deploy_id) + filter_driver = VNMMAD::VNMDriver.filter_driver(template64, deploy_id) filter_driver.activate rescue Exception => e OpenNebula.log_error(e.message) diff --git a/src/vnm_mad/remotes/fw/clean b/src/vnm_mad/remotes/fw/clean index ea753aa57d..489c5296df 100755 --- a/src/vnm_mad/remotes/fw/clean +++ b/src/vnm_mad/remotes/fw/clean @@ -19,12 +19,12 @@ $: << File.dirname(__FILE__) $: << File.join(File.dirname(__FILE__), "..") -require 'OpenNebulaNetwork' +require 'vnmmad' template64 = ARGV[0] begin - filter_driver = OpenNebulaNetwork.filter_driver(template64) + filter_driver = VNMMAD::VNMDriver.filter_driver(template64) filter_driver.deactivate rescue Exception => e OpenNebula.log_error(e.message) diff --git a/src/vnm_mad/remotes/fw/post b/src/vnm_mad/remotes/fw/post index 76ec74671c..c8d1b74edb 100755 --- a/src/vnm_mad/remotes/fw/post +++ b/src/vnm_mad/remotes/fw/post @@ -19,13 +19,13 @@ $: << File.dirname(__FILE__) $: << File.join(File.dirname(__FILE__), "..") -require 'OpenNebulaNetwork' +require 'vnmmad' template64 = ARGV[0] deploy_id = ARGV[1] begin - filter_driver = OpenNebulaNetwork.filter_driver(template64, deploy_id) + filter_driver = VNMMAD::VNMDriver.filter_driver(template64, deploy_id) filter_driver.activate rescue Exception => e OpenNebula.log_error(e.message) diff --git a/src/vnm_mad/remotes/lib/address.rb b/src/vnm_mad/remotes/lib/address.rb new file mode 100644 index 0000000000..c934126bf7 --- /dev/null +++ b/src/vnm_mad/remotes/lib/address.rb @@ -0,0 +1,77 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +module VNMNetwork + + # This methods translates an address range to a set of IPv4 networks + # in CIDR notation + # @param ip_start [String] First IP of the range in dot notation + # @param size [Fixnum] The number of IPs in the range + # + # @return [Array] The networks in CIDR + def self.to_nets(ip_start, size) + nets = Array.new + ip_i = IPv4.to_i(ip_start) + + # Find the largest address block (look for the first 1-bit) + lblock = 0 + + lblock += 1 while (ip_i[lblock] == 0 && lblock < 32 ) + + # Allocate whole blocks till the size fits + while ( size >= 2**lblock ) + nets << "#{IPv4.to_s(ip_i)}/#{32-lblock}" + + ip_i += 2**lblock + size -= 2**lblock + + lblock += 1 while (ip_i[lblock] == 0 && lblock < 32 ) + end + + # Fit remaining address blocks + 32.downto(0) { |i| + next if size[i] == 0 + + nets << "#{IPv4.to_s(ip_i)}/#{32-i}" + + ip_i += 2**i + } + + return nets + end + + # This implementation module includes IPv4 management functions + # It MUST NOT be used in other VNMAD classes + module IPv4 + # Returns the binary equivalent of a IP address + # @param ip [String] IP in dot notation + # @return [Fixnum] IP as an integer + def self.to_i(ip) + ip.split(".").inject(0) {|t,e| (t << 8) + e.to_i } + end + + # Returns the string equivalent of a IP address + # @param ip [Fixnum] IP as an integer + # @return [String] IP in dot notation + def self.to_s(ip) + ip = 3.downto(0).collect {|s| (ip >> 8*s) & 0xff }.join('.') + end + end +end + +end diff --git a/src/vnm_mad/remotes/lib/command.rb b/src/vnm_mad/remotes/lib/command.rb new file mode 100644 index 0000000000..aae4294984 --- /dev/null +++ b/src/vnm_mad/remotes/lib/command.rb @@ -0,0 +1,86 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +module VNMNetwork + + # This module include implementation specific functions. It MUST not be + # be used in other VNMAD classes. + module Configuration + # Return the command to talk to the Xen hypervisor xm or xl for + # Xen 3 and 4 + def self.get_xen_command + if system("ps axuww | grep -v grep | grep '\\bxen\\b'") + "sudo xm" + else + "sudo xl" + end + end + end + + # Command configuration for common network commands. This CAN be adjust + # to local installations. Any modification requires to sync the hosts with + # onehost sync command. + COMMANDS = { + :ebtables => "sudo ebtables", + :iptables => "sudo iptables", + :brctl => "sudo brctl", + :ip => "sudo ip", + :virsh => "virsh -c qemu:///system", + :xm => Configuration::get_xen_command, + :ovs_vsctl=> "sudo ovs-vsctl", + :ovs_ofctl=> "sudo ovs-ofctl", + :lsmod => "lsmod", + :ipset => "sudo ipset" + } + + # Represents an Array of commands to be executed by the networking drivers + # The commands + class Commands < Array + + # Adds a new command to the command array + # @param cmd [String] the command, it can be a key defined in COMMANDS + # @para args[Array] Arguments for the command + def add (cmd, *args) + if COMMANDS.keys.include?(cmd.to_sym) + cmd_str = "#{COMMANDS[cmd.to_sym]} #{args.join(' ')}" + else + cmd_str = "#{cmd} #{args.join(' ')}" + end + + self << cmd_str + end + + # Executes the commands array + # @return [String] the output of the commands + def run! + out = "" + + self.each{ |c| + out << `#{c}` + + raise StandardError, "Command Error: #{c}" if !$?.success? + } + + clear + + return out + end + end +end + +end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/fw_driver.rb b/src/vnm_mad/remotes/lib/fw_driver.rb new file mode 100644 index 0000000000..892f28944e --- /dev/null +++ b/src/vnm_mad/remotes/lib/fw_driver.rb @@ -0,0 +1,199 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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. # +#--------------------------------------------------------------------------- # + +module VNMMAD + + ############################################################################ + # Filter network driver based on simple iptables rules + ############################################################################ + class FWDriver < VNMDriver + # Driver name + DRIVER = "fw" + + # NICs filter. Select NICs with one or more of ICMP, WHITE_PORTS_* or + # BLACK_PORTS_* + XPATH_FILTER = "TEMPLATE/NIC[ICMP|WHITE_PORTS_TCP|WHITE_PORTS_UDP|" << + "BLACK_PORTS_TCP|BLACK_PORTS_UDP]" + + # Creates the driver object. It sets locking to prevent race conditions + # between concurrent deployments + def initialize(vm, deploy_id = nil, hypervisor = nil) + super(vm,XPATH_FILTER,deploy_id,hypervisor) + @locking = true + end + + # Function to activate the driver in the VM + def activate + lock + + vm_id = @vm['ID'] + + process do |nic| + #:white_ports_tcp => iptables_range + #:white_ports_udp => iptables_range + #:black_ports_tcp => iptables_range + #:black_ports_udp => iptables_range + #:icmp => 'DROP' or 'NO' + + nic_rules = Array.new + + chain = "one-#{vm_id}-#{nic[:network_id]}" + tap = nic[:tap] + + next if chain_exists?(chain) + + if tap + #TCP + if range = nic[:white_ports_tcp] + nic_rules << filter_established(chain, :tcp, :accept) + nic_rules << filter_ports(chain, :tcp, range, :accept) + nic_rules << filter_protocol(chain, :tcp, :drop) + elsif range = nic[:black_ports_tcp] + nic_rules << filter_ports(chain, :tcp, range, :drop) + end + + #UDP + if range = nic[:white_ports_udp] + nic_rules << filter_established(chain, :udp, :accept) + nic_rules << filter_ports(chain, :udp, range, :accept) + nic_rules << filter_protocol(chain, :udp, :drop) + elsif range = nic[:black_ports_udp] + nic_rules << filter_ports(chain, :udp, range, :drop) + end + + #ICMP + if nic[:icmp] + if %w(no drop).include? nic[:icmp].downcase + nic_rules << filter_established(chain, :icmp, :accept) + nic_rules << filter_protocol(chain, :icmp, :drop) + end + end + + process_chain(chain, tap, nic_rules) + end + end + + unlock + end + + # Method to clean iptables chains + def deactivate + lock + + vm_id = @vm['ID'] + process do |nic| + chain = "one-#{vm_id}-#{nic[:network_id]}" + iptables_out = `#{command(:iptables)} -n -v --line-numbers -L FORWARD` + if m = iptables_out.match(/.*#{chain}.*/) + rule_num = m[0].split(/\s+/)[0] + purge_chain(chain, rule_num) + end + end + + unlock + end + + ######################################################################## + # Methods to deal with iptables rules + ######################################################################## + private + + def purge_chain(chain, rule_num) + rules = Array.new + rules << rule("-D FORWARD #{rule_num}") + rules << rule("-F #{chain}") + rules << rule("-X #{chain}") + run_rules rules + end + + def process_chain(chain, tap, nic_rules) + rules = Array.new + if !nic_rules.empty? + # new chain + rules << new_chain(chain) + # move tap traffic to chain + rules << tap_to_chain(tap, chain) + + rules << nic_rules + end + run_rules rules + end + + def filter_established(chain, protocol, policy) + policy = policy.to_s.upcase + rule "-A #{chain} -p #{protocol} -m state --state ESTABLISHED -j #{policy}" + end + + def run_rules(rules) + rules.flatten.each do |rule| + OpenNebula.exec_and_log(rule) + end + end + + def range?(range) + range.match(/^(?:(?:\d+|\d+:\d+),)*(?:\d+|\d+:\d+)$/) + end + + def filter_protocol(chain, protocol, policy) + policy = policy.to_s.upcase + rule "-A #{chain} -p #{protocol} -j #{policy}" + end + + def filter_ports(chain, protocol, range, policy) + policy = policy.to_s.upcase + range.gsub!(/\s+/,"") + + if range? range + rule "-A #{chain} -p #{protocol} -m multiport --dports #{range} -j #{policy}" + end + end + + def tap_to_chain(tap, chain) + iptables_out = `#{command(:iptables)} -n -v --line-numbers -L FORWARD` + + # Insert the rule on top of the 'opennebula' chain if it exists, so it + # doesn't conflict with the security groups driver + index = nil + iptables_out.lines.each do |line| + fields = line.split + if fields.include?("opennebula") && fields.include?("--physdev-is-bridged") + index = fields[0] + break + end + end + + if index + rule "-I FORWARD #{index} -m physdev --physdev-out #{tap} -j #{chain}" + else + rule "-A FORWARD -m physdev --physdev-out #{tap} -j #{chain}" + end + end + + def new_chain(chain) + rule "-N #{chain}" + end + + def chain_exists?(chain) + iptables_nl =`#{command(:iptables)} -nL` + chains = iptables_nl.scan(/(one-.*?) .*references/).flatten + chains.include? chain + end + + def rule(rule) + "#{command(:iptables)} #{rule}" + end + end +end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/nic.rb b/src/vnm_mad/remotes/lib/nic.rb new file mode 100644 index 0000000000..9c85841e7d --- /dev/null +++ b/src/vnm_mad/remotes/lib/nic.rb @@ -0,0 +1,154 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +module VNMNetwork + + # This class represents the NICS of a VM, it provides a factory method + # to create VMs of the given hyprtvisor + class Nics < Array + def initialize(hypervisor) + case hypervisor + when "kvm" + @nicClass = NicKVM + when "xen" + @nicClass = NicXen + when "vmware" + @nicClass = NicVMware + end + end + + def new_nic + @nicClass.new + end + end + + ############################################################################ + # Hypervisor specific implementation of network interfaces. Each class + # implements the following interface: + # - get_info to populste the VM.vm_info Hash + # - get_tap to set the [:tap] attribute with the associated NIC + ############################################################################ + + # A NIC using KVM. This class implements functions to get the physical + # interface that the NIC is using, based on the MAC address + class NicKVM < Hash + def initialize + super(nil) + end + + # Get the VM information with virsh dumpxml + def get_info(vm) + if vm.deploy_id + deploy_id = vm.deploy_id + else + deploy_id = vm['DEPLOY_ID'] + end + + if deploy_id and vm.vm_info[:dumpxml].nil? + vm.vm_info[:dumpxml] = `#{VNMNetwork::COMMANDS[:virsh]} dumpxml #{deploy_id} 2>/dev/null` + + vm.vm_info.each_key do |k| + vm.vm_info[k] = nil if vm.vm_info[k].to_s.strip.empty? + end + end + end + + # Look for the tap in + # devices/interface[@type='bridge']/mac[@address='']/../target" + def get_tap(vm) + dumpxml = vm.vm_info[:dumpxml] + + if dumpxml + dumpxml_root = REXML::Document.new(dumpxml).root + + xpath = "devices/interface[@type='bridge']/" \ + "mac[@address='#{self[:mac]}']/../target" + + tap = dumpxml_root.elements[xpath] + + self[:tap] = tap.attributes['dev'] if tap + end + + self + end + end + + + # A NIC using Xen. This class implements functions to get the physical interface + # that the NIC is using + class NicXen < Hash + def initialize + super(nil) + end + + def get_info(vm) + if vm.deploy_id + deploy_id = vm.deploy_id + else + deploy_id = vm['DEPLOY_ID'] + end + + if deploy_id and (vm.vm_info[:domid].nil? or vm.vm_info[:networks].nil?) + vm.vm_info[:domid] =`#{VNMNetwork::COMMANDS[:xm]} domid #{deploy_id}`.strip + vm.vm_info[:networks] =`#{VNMNetwork::COMMANDS[:xm]} network-list #{deploy_id}` + + vm.vm_info.each_key do |k| + vm.vm_info[k] = nil if vm.vm_info[k].to_s.strip.empty? + end + end + end + + def get_tap(vm) + domid = vm.vm_info[:domid] + + if domid + networks = vm.vm_info[:networks].split("\n")[1..-1] + networks.each do |net| + n = net.split + + iface_id = n[0] + iface_mac = n[2] + + if iface_mac == self[:mac] + self[:tap] = "vif#{domid}.#{iface_id}" + break + end + end + end + self + end + end + + # A NIC using VMware. This class implements functions to get the physical interface + # that the NIC is using + class NicVMware < Hash + def initialize + super(nil) + end + + def get_info(vm) + end + + def get_tap(vm) + self + end + end + +end + +end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/security_groups.rb b/src/vnm_mad/remotes/lib/security_groups.rb new file mode 100644 index 0000000000..e8e3b92539 --- /dev/null +++ b/src/vnm_mad/remotes/lib/security_groups.rb @@ -0,0 +1,222 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +# This module includes provides the abstractions to implement SecurityGroups +module VNMNetwork + + ############################################################################ + # Rule supports these (final and relevant) attributes: + # + # PROTOCOL (mandatory) + # - Specifies the protocol of the rule + # - values: ['ALL', 'TCP', 'UDP', 'ICMP', 'IPSEC'] + # + # RULE_TYPE (mandatory) + # - Specifies the direction of application of the rule + # - values: ['INBOUND', 'OUTBOUND'] + # + # RANGE (optional) + # - only works for protocols ['TCP', 'UDP'] + # - uses the iptables multiports syntax + # + # ICMP_TYPE (optional) + # - Only works for protocol 'ICMP' + # - Is either in the form of '' or '/', where both + # '' and '' are integers. This class has a helper method + # tgat expands '' into all the '/' subtypes. + # + # IP and SIZE (optional but must be specified together) + # - Can be applied to any protocol + # - IP is the first valid IP and SIZE is the number of consecutive IPs + ############################################################################ + class Rule + # Rule type. + TYPES = [ + :protocol, # Type 1: block the whole protocol + :portrange, # Type 2a: block a port range within a protocol + :icmp_type, # Type 2b: block selected icmp types + :net, # Type 3: block a whole protocol for a network + :net_portrange, # Type 4a: block a port range from a network + :net_icmp_type # Type 4b: block selected icmp types from a network + ] + + # Initialize a new rule. + def initialize(rule) + @rule = rule + @protocol = @rule[:protocol].downcase.to_sym + @protocol = :esp if @protocol == :ipsec + + @rule_type = @rule[:rule_type].downcase.to_sym + @icmp_type = @rule[:icmp_type] + + @range = @rule[:range] + @ip = @rule[:ip] + @size = @rule[:size] + @type = set_type + end + + # Process the rule and generates the associated commands of the rule + # @param [Commands] cmd to add the rule commands to + # @param [Hash] vars iptables attributes for the rule + def process(cmds, vars) + case @type + when :protocol + process_protocol(cmds, vars) + + when :portrange + process_portrange(cmds, vars) + + when :icmp_type + process_icmp_type(cmds, vars) + + when :net + process_net(cmds, vars) + + when :net_portrange + process_net_portrange(cmds, vars) + + when :net_icmp_type + process_net_icmp_type(cmds, vars) + end + end + + # Return the network blocks associated to the rule + # @return [Array] each network block in CIDR. + def net + return [] if @ip.nil? || @size.nil? + + VNMNetwork::to_nets(@ip, @size.to_i) + end + + # Expand the ICMP type with associated codes if any + # @return [Array] expanded ICMP types to include all codes + def icmp_type_expand + if (codes = ICMP_TYPES_EXPANDED[@icmp_type.to_i]) + codes.collect{|e| "#{@icmp_type}/#{e}"} + else + ["#{@icmp_type}/0"] + end + end + + private + + # ICMP Codes for each ICMP type + ICMP_TYPES_EXPANDED = { + 3 => [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15], + 5 => [0, 1, 2, 3], + 11 => [0, 1], + 12 => [0, 1] + } + + # Depending on the combination of the rule attributes derive the + # rule type: + # + # @protocol + @rule_type => Type 1: 'protocol' + # @protocol + @rule_type + @range => Type 2A: 'portrange' + # @protocol + @rule_type + @icmp_type => Type 2B: 'icmp_type' + # @protocol + @rule_type + @ip + @size => Type 3: 'net' + # @protocol + @rule_type + @ip + @size + @range => Type 4A: 'net_portrange' + # @protocol + @rule_type + @ip + @size + @icmp_type => Type 4B: 'net_icmp_type' + # + # @return [Symbol] The rule type + def set_type + if @ip.nil? && @size.nil? + return :icmp_type if !@icmp_type.nil? + return :portrange if !@range.nil? + return :protocol + else + return :net_icmp_type if !@icmp_type.nil? + return :net_portrange if !@range.nil? + return :net + end + end + + ######################################################################## + # Dummy process methods for each rule type. These MUST be overriden + # in derived classes + # @param cmds [Commands] commands to implement the rule + # @param vars [Hash] with specific rule implementation variables + ######################################################################## + def process_protocol(cmds, vars) + end + + def process_portrange(cmds, vars) + end + + def process_icmp_type(cmds, vars) + end + + def process_net(cmds, vars) + end + + def process_net_portrange(cmds, vars) + end + + def process_net_icmp_type(cmds, vars) + end + end + + ############################################################################ + # Base class for security groups. This class SHOULD NOT be used directly + ############################################################################ + class SecurityGroup + # Creates a new security group + # @param vm [VNMMAD::VM] a VM object + # @param nic [VNMMAD::NIC] the network interface + # @param sg_id [Fixnum] the security group ID + # @param rules [Array] to be applied to the NIC + def initialize(vm, nic, sg_id, rules) + @vm = vm + @nic = nic + @sg_id = sg_id + + @rules = [] + @vars = {} + + @commands = VNMNetwork::Commands.new + + rules.each do |rule| + @rules << new_rule(rule) + end if rules + end + + # Default factory method for the SecurityGroup class. It MUST be + # overriden in derived classes + def new_rule(rule) + Rule.new(rule) + end + + # Generates the iptables/ipset commands to implement this security group + def process_rules + @rules.each do |rule| + rule.process(@commands, @vars) + end + + @commands.uniq! + end + + # Execute the implementation commands, process_rules MUST be called + # before this method + def run! + @commands.run! + end + end + +end + +end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/security_groups_iptables.rb b/src/vnm_mad/remotes/lib/security_groups_iptables.rb new file mode 100644 index 0000000000..2c9bf3027a --- /dev/null +++ b/src/vnm_mad/remotes/lib/security_groups_iptables.rb @@ -0,0 +1,341 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +# This module implements the SecurityGroup abstraction on top of iptables +module SGIPTables + + ############################################################################ + # A Rule implemented with the iptables/ipset Linux kernel facilities + ############################################################################ + class RuleIPTables < VNMNetwork::Rule + ######################################################################## + # Implementation of each rule type + ######################################################################## + private + + # Implements the :protocol rule. Example: + # iptables -A one-3-0-i -p tcp -j RETURN + def process_protocol(cmds, vars) + chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] + + cmds.add :iptables, "-A #{chain} -p #{@protocol} -j RETURN" + end + + # Implements the :portrange rule. Example: + # iptables -A one-3-0-o -p udp -m multiport --dports 80,22 -j RETURN + def process_portrange(cmds, vars) + chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] + + cmds.add :iptables, "-A #{chain} -p #{@protocol} -m multiport" \ + " --dports #{@range} -j RETURN" + end + + # Implements the :icmp_type rule. Example: + # iptables -A one-3-0-o -p icmp --icmp-type 8 -j RETURN + def process_icmp_type(cmds, vars) + chain = @rule_type == :inbound ? vars[:chain_in] : vars[:chain_out] + + cmds.add :iptables, "-A #{chain} -p icmp --icmp-type #{@icmp_type}" \ + " -j RETURN" + end + + # Implements the :net rule. Example: + # ipset create one-3-0-1-i-tcp-n hash:net + # iptables -A one-3-0-i -p tcp -m set --match-set one-3-0-1-i src -j RETURN + # ipset add -exist one-3-0-1-i-tcp-n 10.0.0.0/24 + def process_net(cmds, vars) + if @rule_type == :inbound + chain = vars[:chain_in] + set = "#{vars[:set_sg_in]}-#{@protocol}-n" + dir = "src" + else + chain = vars[:chain_out] + set = "#{vars[:set_sg_out]}-#{@protocol}-n" + dir = "dst" + end + + cmds.add :ipset, "create #{set} hash:net" + cmds.add :iptables, "-A #{chain} -p #{@protocol} -m set" \ + " --match-set #{set} #{dir} -j RETURN" + + net.each do |n| + cmds.add :ipset, "add -exist #{set} #{n}" + end + end + + # Implements the :net_portrange rule. Example: + # ipset create one-3-0-1-i-nr hash:net,port + # iptables -A one-3-0-i -m set --match-set one-3-0-1-i-nr src,dst -j RETURN + # ipset add -exist one-3-0-1-i-nr 10.0.0.0/24,tcp:80 + def process_net_portrange(cmds, vars) + if @rule_type == :inbound + chain = vars[:chain_in] + set = "#{vars[:set_sg_in]}-nr" + dir = "src,dst" + else + chain = vars[:chain_out] + set = "#{vars[:set_sg_out]}-nr" + dir = "dst,dst" + end + + cmds.add :ipset, "create #{set} hash:net,port" + cmds.add :iptables, "-A #{chain} -m set --match-set" \ + " #{set} #{dir} -j RETURN" + + net.each do |n| + @range.split(",").each do |r| + r.gsub!(":","-") + net_range = "#{n},#{@protocol}:#{r}" + cmds.add :ipset, "add -exist #{set} #{net_range}" + end + end + end + + # Implements the :net_icmp_type rule. Example: + # ipset create one-3-0-1-i-ni hash:net,port + # iptables -A one-3-0-i -m set --match-set one-3-0-1-i-nr src,dst -j RETURN + # ipset add -exist one-3-0-1-i-ni 10.0.0.0/24,icmp:8/0 + def process_net_icmp_type(cmds, vars) + if @rule_type == :inbound + chain = vars[:chain_in] + set = "#{vars[:set_sg_in]}-ni" + dir = "src,dst" + else + chain = vars[:chain_out] + set = "#{vars[:set_sg_out]}-ni" + dir = "dst,dst" + end + + cmds.add :ipset, "create #{set} hash:net,port" + cmds.add :iptables, "-A #{chain} -m set --match-set #{set} #{dir} -j RETURN" + + net.each do |n| + icmp_type_expand.each do |type_code| + cmds.add :ipset, "add -exist #{set} #{n},icmp:#{type_code}" + end + end + end + end + + ############################################################################ + # This class represents a SecurityGroup implemented with iptables/ipset + # Kernel facilities. + ############################################################################ + class SecurityGroupIPTables < VNMNetwork::SecurityGroup + def initialize(vm, nic, sg_id, rules) + super + + @vars = SGIPTables.vars(@vm, @nic, @sg_id) + end + + def new_rule(rule) + RuleIPTables.new(rule) + end + end + + ############################################################################ + # Methods to configure the hypervisor iptables rules. All the rules are + # added to the GLOBAL_CHAIN chain. By default this chain is "opennebula" + ############################################################################ + + GLOBAL_CHAIN = "opennebula" + + # Get information from the current iptables rules and chains + # @return [Hash] with the following keys: + # - :iptables_forwards + # - :iptables_s + # - :ipset_list + def self.info + commands = VNMNetwork::Commands.new + + commands.add :iptables, "-S" + iptables_s = commands.run! + + iptables_forwards = "" + + if iptables_s.match(/^-N #{GLOBAL_CHAIN}$/) + commands.add :iptables, "-L #{GLOBAL_CHAIN} --line-numbers" + iptables_forwards = commands.run! + end + + commands.add :ipset, "list -name" + ipset_list = commands.run! + + { + :iptables_forwards => iptables_forwards, + :iptables_s => iptables_s, + :ipset_list => ipset_list + } + end + + # Bootstrap the OpenNebula chains and rules. This method: + # 1.- Creates the GLOBAL_CHAIN chain + # 2.- Forwards the bridge traffic to the GLOBAL_CHAIN + # 3.- By default ACCEPT all traffic + def self.global_bootstrap + info = SGIPTables.info + + return if info[:iptables_s].split("\n").include? "-N #{GLOBAL_CHAIN}" + + commands = VNMNetwork::Commands.new + + commands.add :iptables, "-N #{GLOBAL_CHAIN}" + commands.add :iptables, "-A FORWARD -m physdev --physdev-is-bridged -j #{GLOBAL_CHAIN}" + commands.add :iptables, "-A #{GLOBAL_CHAIN} -j ACCEPT" + + commands.run! + end + + # Returns the base chain and ipset names for the VM + # @param vm [VM] the virtual machine + # @param nic [Nic] of the VM + # @param sg_id [Fixnum] ID of the SecurityGroup if any + # + # @return [Hash] with the :chain, :chain_in, :chain_out chain names, and + # :set_sg_in and :set_seg_out ipset names. + def self.vars(vm, nic, sg_id = nil) + vm_id = vm['ID'] + nic_id = nic[:nic_id] + + vars = {} + + vars[:vm_id] = vm_id, + vars[:nic_id] = nic_id, + vars[:chain] = "one-#{vm_id}-#{nic_id}", + vars[:chain_in] = "#{vars[:chain]}-i", + vars[:chain_out] = "#{vars[:chain]}-o" + + if sg_id + vars[:set_sg_in] = "#{vars[:chain]}-#{sg_id}-i" + vars[:set_sg_out] = "#{vars[:chain]}-#{sg_id}-o" + end + + vars + end + + # Bootstrap NIC rules for the interface. It creates the :chain_in and + # :chain_out and sets up FORWARD rules to these chains for inbound and + # outbound traffic. + # + # This method also sets mac_spoofing, and ip_spoofing rules + # + # Example, for VM 3 and NIC 0 + # iptables -N one-3-0-i + # iptables -N one-3-0-o + # iptables -I opennebula -m physdev --physdev-out vnet0 --physdev-is-bridged -j one-3-0-i" + # iptables -I opennebula -m physdev --physdev-in vnet0 --physdev-is-bridged -j one-3-0-o" + # iptables -A one-3-0-i -m state --state ESTABLISHED,RELATED -j ACCEPT + # iptables -A one-3-0-o -m state --state ESTABLISHED,RELATED -j ACCEPT + # + # Mac spoofing (no output traffic from a different MAC) + # iptables -A one-3-0-o -m mac ! --mac-source 02:00:00:00:00:01 -j DROP + # + # IP spoofing + # iptables -A one-3-0-o ! --source 10.0.0.1 -j DROP + def self.nic_pre(vm, nic) + commands = VNMNetwork::Commands.new + + vars = SGIPTables.vars(vm, nic) + + chain = vars[:chain] + chain_in = vars[:chain_in] + chain_out = vars[:chain_out] + + # create chains + commands.add :iptables, "-N #{chain_in}" # inbound + commands.add :iptables, "-N #{chain_out}" # outbound + + # Send traffic to the NIC chains + commands.add :iptables, "-I #{GLOBAL_CHAIN} -m physdev --physdev-out #{nic[:tap]} --physdev-is-bridged -j #{chain_in}" + commands.add :iptables, "-I #{GLOBAL_CHAIN} -m physdev --physdev-in #{nic[:tap]} --physdev-is-bridged -j #{chain_out}" + + # Mac-spofing + if nic[:filter_mac_spoofing] == "YES" + commands.add :iptables, "-A #{chain_out} -m mac ! --mac-source #{nic[:mac]} -j DROP" + end + + # IP-spofing + if nic[:filter_ip_spoofing] == "YES" + commands.add :iptables, "-A #{chain_out} ! --source #{nic[:ip]} -j DROP" + end + + # Related, Established + commands.add :iptables, "-A #{chain_in} -m state --state ESTABLISHED,RELATED -j ACCEPT" + commands.add :iptables, "-A #{chain_out} -m state --state ESTABLISHED,RELATED -j ACCEPT" + + commands.run! + end + + # Sets the default policy to DROP for the NIC rules. Example + # iptables -A one-3-0-i -j DROP + # iptables -A one-3-0-o -j DROP + def self.nic_post(vm, nic) + vars = SGIPTables.vars(vm, nic) + chain_in = vars[:chain_in] + chain_out = vars[:chain_out] + + commands = VNMNetwork::Commands.new + commands.add :iptables, "-A #{chain_in} -j DROP" + commands.add :iptables, "-A #{chain_out} -j DROP" + + commands.run! + end + + # Removes all the rules associated to a VM and NIC + def self.nic_deactivate(vm, nic) + vars = SGIPTables.vars(vm, nic) + chain = vars[:chain] + chain_in = vars[:chain_in] + chain_out = vars[:chain_out] + + info = self.info + iptables_forwards = info[:iptables_forwards] + iptables_s = info[:iptables_s] + ipset_list = info[:ipset_list] + + commands = VNMNetwork::Commands.new + + iptables_forwards.lines.reverse_each do |line| + fields = line.split + if [chain_in, chain_out].include?(fields[1]) + n = fields[0] + commands.add :iptables, "-D #{GLOBAL_CHAIN} #{n}" + end + end + + remove_chains = [] + iptables_s.lines.each do |line| + if line.match(/^-N #{chain}/) + remove_chains << line.split[1] + end + end + remove_chains.each {|c| commands.add :iptables, "-F #{c}" } + remove_chains.each {|c| commands.add :iptables, "-X #{c}" } + + ipset_list.lines.each do |line| + if line.match(/^#{chain}/) + set = line.strip + commands.add :ipset, "destroy #{set}" + end + end + + commands.run! + end +end + +end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/sg_driver.rb b/src/vnm_mad/remotes/lib/sg_driver.rb new file mode 100644 index 0000000000..8390e7d948 --- /dev/null +++ b/src/vnm_mad/remotes/lib/sg_driver.rb @@ -0,0 +1,105 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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. # +#--------------------------------------------------------------------------- # + +module VNMMAD + + ############################################################################ + # OpenNebula Firewall with Security Groups Based on IPTables (KVM and Xen) + ############################################################################ + class SGDriver < VNMDriver + + DRIVER = "sg" + XPATH_FILTER = "TEMPLATE/NIC" + + # Creates a new SG driver and scans SG Rules + def initialize(vm, deploy_id = nil, hypervisor = nil) + super(vm, XPATH_FILTER, deploy_id, hypervisor) + @locking = true + @commands = VNMNetwork::Commands.new + + rules = {} + @vm.vm_root.elements.each('TEMPLATE/SECURITY_GROUP_RULE') do |r| + security_group_rule = {} + + r.elements.each do |e| + key = e.name.downcase.to_sym + security_group_rule[key] = e.text + end + + id = security_group_rule[:security_group_id] + + rules[id] = [] if rules[id].nil? + rules[id] << security_group_rule + end + + @security_group_rules = rules + end + + # Activate the rules, bootstrap iptables chains and set filter rules for + # each VM NIC + def activate + deactivate + lock + + # Global Bootstrap + SGIPTables.global_bootstrap + + # Process the rules + @vm.nics.each do |nic| + next if nic[:security_groups].nil? + + SGIPTables.nic_pre(@vm, nic) + + sg_ids = nic[:security_groups].split(",") + + sg_ids.each do |sg_id| + rules = @security_group_rules[sg_id] + + sg = SGIPTables::SecurityGroupIPTables.new(@vm, nic, sg_id, + rules) + + begin + sg.process_rules + sg.run! + rescue Exception => e + unlock + deactivate + raise e + end + end + + SGIPTables.nic_post(@vm, nic) + end + + unlock + end + + # Clean iptables rules and chains + def deactivate + lock + + begin + @vm.nics.each do |nic| + SGIPTables.nic_deactivate(@vm, nic) + end + rescue Exception => e + raise e + ensure + unlock + end + end + end +end diff --git a/src/vnm_mad/remotes/lib/vm.rb b/src/vnm_mad/remotes/lib/vm.rb new file mode 100644 index 0000000000..a09511389d --- /dev/null +++ b/src/vnm_mad/remotes/lib/vm.rb @@ -0,0 +1,110 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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. # +#--------------------------------------------------------------------------- # + +module VNMMAD + +module VNMNetwork + + ############################################################################ + # This class represents the VM abstraction. It provides basic methods + # to interact with its network interfaces. + ############################################################################ + class VM + attr_accessor :nics, :vm_info, :deploy_id, :vm_root + + + # Creates a new VM object, and bootstrap the NICs array + # @param vm_root [REXML] XML document representing the VM + # @param xpath_filer [String] to get the VM NICs + # @param deploy_id [String] refers to the VM in the hypervisor + # @param hypervisor [String] + def initialize(vm_root, xpath_filter, deploy_id, hypervisor) + @vm_root = vm_root + @xpath_filter = xpath_filter + @deploy_id = deploy_id + @hypervisor = hypervisor + @vm_info = Hash.new + + @deploy_id = nil if deploy_id == "-" + + nics = VNMNetwork::Nics.new(@hypervisor) + + @vm_root.elements.each(@xpath_filter) do |nic_element| + nic = nics.new_nic + + nic_build_hash(nic_element,nic) + + nic.get_info(self) + nic.get_tap(self) + + nics << nic + end + + @nics = nics + end + + # Iterator on each NIC of the VM + def each_nic(block) + if @nics != nil + @nics.each do |the_nic| + block.call(the_nic) + end + end + end + + # Access an XML Element of the VM + # @param element [String] element name + # @return [String] valule of the element or nil if not found + def [](element) + if @vm_root + val = @vm_root.elements[element] + return val.text if !val.nil? && val.text + end + + nil + end + + private + + # Method to build the associated Hash from a NIC + # @param nic_element [REXML] for the NIC + # @param nic [Nic] class representation + def nic_build_hash(nic_element,nic) + nic_element.elements.each('*') do |nic_attribute| + key = nic_attribute.name.downcase.to_sym + + if nic_attribute.has_elements? + data = {} + nic_build_hash(nic_attribute,data) + else + data = nic_attribute.text + end + + if nic[key] + if nic[key].instance_of?(Array) + nic[key] << data + else + nic[key] = [nic[key], data] + end + else + nic[key] = data + end + end + end + end +end + +end \ No newline at end of file diff --git a/src/vnm_mad/remotes/lib/vnm_driver.rb b/src/vnm_mad/remotes/lib/vnm_driver.rb new file mode 100644 index 0000000000..f62df56e06 --- /dev/null +++ b/src/vnm_mad/remotes/lib/vnm_driver.rb @@ -0,0 +1,158 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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. # +#--------------------------------------------------------------------------- # + +################################################################################ +# The VNMMAD module provides the basic abstraction to implement custom +# virtual network drivers. The VNMAD module includes: +# - VNMNetwork with base classes and main functionality to manage Virtual Nets +# - SGIPTables a module with a SG implementation based in iptables/ipset +################################################################################ +module VNMMAD + + ############################################################################ + # Base driver class to implement a Network driver. It relays on two filter + # drivers FirewallDriver and SGDriver. + ############################################################################ + class VNMDriver + attr_reader :hypervisor, :vm + + # Creates new driver using: + # @param vm_tpl [String] XML String from oned + # @param xpath_filter [String] to get relevant NICs for the driver + # @param deploy_id [String] + # @param hypervisor [String] + def initialize(vm_tpl, xpath_filter, deploy_id = nil, hypervisor = nil) + @locking = false + + if !hypervisor + @hypervisor = detect_hypervisor + else + @hypervisor = hypervisor + end + + @vm = VNMNetwork::VM.new(REXML::Document.new(vm_tpl).root, + xpath_filter, deploy_id, @hypervisor) + end + + # Creates a new VNDriver using: + # @param vm_64 [String] Base64 encoded XML String from oned + # @param deploy_id [String] + # @param hypervisor [String] + def self.from_base64(vm_64, deploy_id = nil, hypervisor = nil) + vm_xml = Base64::decode64(vm_64) + self.new(vm_xml, deploy_id, hypervisor) + end + + # Locking function to serialized driver operations if needed. Similar + # to flock. File is created as /tmp/onevnm--lock + def lock + if @locking + driver_name = self.class.name.downcase + @locking_file = File.open("/tmp/onevnm-#{driver_name}-lock","w") + @locking_file.flock(File::LOCK_EX) + end + end + + # Unlock driver execution mutex + def unlock + if @locking + @locking_file.close + end + end + + # Executes the given block on each NIC + def process(&block) + @vm.each_nic(block) + end + + # Return a string for the hypervisor + # @return [String] "kvm", "xen" or nil + def detect_hypervisor + lsmod = `#{VNMNetwork::COMMANDS[:lsmod]}` + xen_file = "/proc/xen/capabilities" + + if File.exists?(xen_file) + "xen" + elsif lsmod.match(/kvm/) + "kvm" + else + nil + end + end + + # Get hypervisor bridges + # @return [Hash] with the bridge names + def get_interfaces + bridges = Hash.new + brctl_exit =`#{VNMNetwork::COMMANDS[:brctl]} show` + + cur_bridge = "" + + brctl_exit.split("\n")[1..-1].each do |l| + l = l.split + + if l.length > 1 + cur_bridge = l[0] + + bridges[cur_bridge] = Array.new + bridges[cur_bridge] << l[3] if l[3] + else + bridges[cur_bridge] << l[0] + end + end + + bridges + end + + # Returns true if the template contains the deprecated firewall attributes: + # - ICMP + # - WHITE_PORTS_TCP + # - WHITE_PORTS_UDP + # - BLACK_PORTS_TCP + # - BLACK_PORTS_UDP + # + # @return Boolean + def self.has_fw_attrs?(vm_xml) + vm_root = REXML::Document.new(vm_xml).root + !vm_root.elements[FWDriver::XPATH_FILTER].nil? + end + + # Returns a filter object based on the contents of the template + # + # @return FWDriver or SGDriver object + def self.filter_driver(vm_64, deploy_id = nil, hypervisor = nil) + vm_xml = Base64::decode64(vm_64) + + if self.has_fw_attrs?(vm_xml) + FWDriver.new(vm_xml, deploy_id, hypervisor) + else + SGDriver.new(vm_xml, deploy_id, hypervisor) + end + end + + # Returns the associated command including sudo and other configuration + # attributes + def command(cmd) + if VNMNetwork::COMMANDS.keys.include?(cmd.to_sym) + cmd_str = "#{VNMNetwork::COMMANDS[cmd.to_sym]}" + else + cmd_str = "#{cmd}" + end + + return cmd_str + end + end +end diff --git a/src/vnm_mad/remotes/lib/vnmmad.rb b/src/vnm_mad/remotes/lib/vnmmad.rb new file mode 100644 index 0000000000..dad4386931 --- /dev/null +++ b/src/vnm_mad/remotes/lib/vnmmad.rb @@ -0,0 +1,49 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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 'rexml/document' +require 'base64' +require 'yaml' + +require 'command' +require 'vm' +require 'nic' +require 'address' +require 'security_groups' +require 'security_groups_iptables' +require 'vnm_driver' +require 'fw_driver' +require 'sg_driver' + +require 'scripts_common' + +include OpenNebula + +begin + CONF = YAML.load_file( + File.join(File.dirname(__FILE__), "OpenNebulaNetwork.conf") + ) +rescue + CONF = { + :start_vlan => 2 + } +end + +# Set PATH +ENV['PATH'] = "#{ENV['PATH']}:/bin:/sbin:/usr/bin" diff --git a/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb b/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb index cec5fe6c38..b6160ecc22 100644 --- a/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb +++ b/src/vnm_mad/remotes/ovswitch/OpenvSwitch.rb @@ -14,9 +14,9 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -require 'OpenNebulaNetwork' +require 'vnmmad' -class OpenvSwitchVLAN < OpenNebulaNetwork +class OpenvSwitchVLAN < VNMMAD::VNMDriver DRIVER = "ovswitch" FIREWALL_PARAMS = [:black_ports_tcp, @@ -91,7 +91,7 @@ class OpenvSwitchVLAN < OpenNebulaNetwork end def tag_vlan - cmd = "#{COMMANDS[:ovs_vsctl]} set Port #{@nic[:tap]} " + cmd = "#{command(:ovs_vsctl)} set Port #{@nic[:tap]} " cmd << "tag=#{vlan}" run cmd @@ -100,7 +100,7 @@ class OpenvSwitchVLAN < OpenNebulaNetwork def tag_trunk_vlans range = @nic[:vlan_tagged_id] if range? range - ovs_vsctl_cmd = "#{COMMANDS[:ovs_vsctl]} set Port #{@nic[:tap]}" + ovs_vsctl_cmd = "#{command(:ovs_vsctl)} set Port #{@nic[:tap]}" cmd = "#{ovs_vsctl_cmd} trunks=#{range}" run cmd @@ -159,7 +159,7 @@ class OpenvSwitchVLAN < OpenNebulaNetwork def del_flows in_port = "" - dump_flows = "#{COMMANDS[:ovs_ofctl]} dump-flows #{@nic[:bridge]}" + dump_flows = "#{command(:ovs_ofctl)} dump-flows #{@nic[:bridge]}" `#{dump_flows}`.lines do |flow| next unless flow.match("#{@nic[:mac]}") flow = flow.split.select{|e| e.match(@nic[:mac])}.first @@ -175,13 +175,13 @@ class OpenvSwitchVLAN < OpenNebulaNetwork def add_flow(filter,action,priority=nil) priority = (priority.to_s.empty? ? "" : "priority=#{priority},") - run "#{COMMANDS[:ovs_ofctl]} add-flow " << + run "#{command(:ovs_ofctl)} add-flow " << "#{@nic[:bridge]} #{filter},#{priority}actions=#{action}" end def del_flow(filter) filter.gsub!(/priority=(\d+)/,"") - run "#{COMMANDS[:ovs_ofctl]} del-flows " << + run "#{command(:ovs_ofctl)} del-flows " << "#{@nic[:bridge]} #{filter}" end @@ -192,7 +192,7 @@ class OpenvSwitchVLAN < OpenNebulaNetwork def port return @nic[:port] if @nic[:port] - dump_ports = `#{COMMANDS[:ovs_ofctl]} \ + dump_ports = `#{command(:ovs_ofctl)} \ dump-ports #{@nic[:bridge]} #{@nic[:tap]}` @nic[:port] = dump_ports.scan(/^\s*port\s*(\d+):/).flatten.first diff --git a/src/vnm_mad/remotes/test/sg_test_pre.rb b/src/vnm_mad/remotes/test/sg_test_pre.rb new file mode 100755 index 0000000000..9d32ec9f26 --- /dev/null +++ b/src/vnm_mad/remotes/test/sg_test_pre.rb @@ -0,0 +1,146 @@ +#!/usr/bin/env ruby + +# -------------------------------------------------------------------------- # +# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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.dirname(__FILE__) + '/../lib' +$: << File.dirname(__FILE__) + '/../../../mad/ruby' + +require 'vnmmad' + +module VNMMAD +module VNMNetwork + class Nics < Array + def initialize(hypervisor) + @nicClass = NicTest + end + end + + class NicTest < Hash + def initialize + super(nil) + end + + def get_info(vm) + end + + def get_tap(vm) + self[:tap] = "vnet0" + self + end + end + + class Commands < Array + def run! + self.each{ |c| puts "#{c}"} + clear + return "" + end + end +end +end + +vm_xml=< + 3 + + +EOF + +one_sg = VNMMAD::SGDriver.new(vm_xml, "one-0", "test") +one_sg.activate diff --git a/src/vnm_mad/remotes/vmware/VMware.rb b/src/vnm_mad/remotes/vmware/VMware.rb index 53d38f3e76..722a502890 100644 --- a/src/vnm_mad/remotes/vmware/VMware.rb +++ b/src/vnm_mad/remotes/vmware/VMware.rb @@ -34,9 +34,9 @@ $: << RUBY_LIB_LOCATION require 'yaml' require 'CommandManager' -require 'OpenNebulaNetwork' +require 'vnmmad' -class OpenNebulaVMware < OpenNebulaNetwork +class OpenNebulaVMware < VNMMAD::VNMDriver DRIVER = "vmware" XPATH_FILTER = "TEMPLATE/NIC"