components/ruby/puppet/files/solaris/lib/puppet/provider/evs_properties/solaris.rb
author Sungmin Lee <sungmin.lee@oracle.com>
Tue, 18 Aug 2015 15:07:30 -0700
changeset 4794 be62c55aa235
child 4801 c249904bb056
permissions -rw-r--r--
PSARC/2015/218 Add EVS support to Puppet 18043918 Add EVS support to Puppet

#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
#


Puppet::Type.type(:evs_properties).provide(:evs_properties) do
    desc "Provider for managing Oracle Solaris EVS properties"
    confine :operatingsystem => [:solaris]
    defaultfor :osfamily => :solaris, :kernelrelease => ["5.11", "5.12"]
    commands :evsadm => "/usr/sbin/evsadm"

    mk_resource_methods

    def initialize(value={})
        super(value)
        @property_flush = {}
    end

    def self.get_client_property
        client_property = {}
        begin
            p = evsadm("show-prop", "-c", "-o", "property,value")
        rescue Puppet::ExecutionFailure => e
            raise Puppet::Error, "Failed to prefetch client property: \n" \
                "#{e.inspect}"
        end

        property, value = p.strip.split(":", 2)
        value.gsub! "\\:", ":" 
        client_property[:name] = "client_property"
        client_property[:controller] = value
        
        Puppet.debug "Client Property: #{client_property.inspect}"
        client_property

    end

    def self.get_control_properties
        control_props = {}
        uplink_ports = []
        uri_templates = []
        vxlan_addrs = []
        begin
            evsadm("show-controlprop", "-c", "-o", 
                "property,value,vlan_range,vxlan_range,host,flat").\
                split("\n").collect do |each_prop|
                each_prop.gsub! "\\:", "\\"
                property, value, vlan_range, vxlan_range, host, flat = \
                    each_prop.strip().split(":")
                case property
                when "l2-type"
                    control_props[:l2_type] = value
                when "uplink-port"
                    next if value.empty?
                    host = "" if host == nil
                    val = "#{value};#{vlan_range};#{vxlan_range};#{host};"\
                        "#{flat}"
                    uplink_ports << val
                when "uri-template"
                    value.gsub! "\\", ":"
                    host = "" if host == nil
                    val = "#{value};#{host}"
                    uri_templates << val
                when "vlan-range"
                    control_props[:vlan_range] = value
                when "vxlan-addr"
                    host = "" if host == nil
                    val = "#{value};#{vxlan_range};#{host}"
                    vxlan_addrs << val
                when "vxlan-ipvers"
                    control_props[:vxlan_ipvers] = value
                when "vxlan-mgroup"
                    control_props[:vxlan_mgroup] = value
                when "vxlan-range"
                    control_props[:vxlan_range] = value
                end
            end
        rescue Puppet::ExecutionFailure => e
            # EVS controller is not set or valid
            # Handle the exception at upper level
            raise
        end
        control_props[:name] = "controller_property"
        control_props[:uplink_port] = uplink_ports
        control_props[:uri_template] = uri_templates
        control_props[:vxlan_addr] = vxlan_addrs
        Puppet.debug "Control Properties: #{control_props.inspect}"
        control_props
    end

    def self.instances
        prop_instance = []
        client_property = get_client_property
        prop_instance << new(client_property)
        
        begin
            controller_property = get_control_properties
        rescue Puppet::ExecutionFailure => e
            # EVS controller is not set or invalid
            prop_instance << new({:name => "controller_property"})
        else
            # controller_property values are fetched
            prop_instance << new(controller_property)
        end
        prop_instance
    end

    def self.prefetch(resources)
        instances.each do |inst|
            if resource = resources[inst.name]
                resource.provider = inst
            end
        end
    end
    
    ### Define Setters ###
    ## Controller side property setup ##
    
    def l2_type=(value)
        @property_flush[:l2_type] = value
    end
    
    def uplink_port=(value)
        @property_flush[:uplink_port] = value
    end
    
    def uri_template=(value)
        @property_flush[:uri_template] = value
    end

    def vlan_range=(value)
        @property_flush[:vlan_range] = value
    end
    
    def vxlan_addr=(value)
        @property_flush[:vxlan_addr] = value
    end

    def vxlan_ipvers=(value)
        @property_flush[:vxlan_ipvers] = value
    end
    
    def vxlan_mgroup=(value)
        @property_flush[:vxlan_mgroup] = value
    end

    def vxlan_range=(value)
        @property_flush[:vxlan_range] = value
    end
    
    ## Client side property setup: the pointer to the EVS controller ##
    def controller=(value)
        @property_flush[:controller] = value
    end

    def set_controller_property(host, property)
        begin
            evsadm("set-controlprop", host, property)
        rescue Puppet::ExecutionFailure => e
            # Pass up the exception to upper level
            raise
        end
    end

    def set_client_property(property)
        begin
            evsadm("set-prop", "-p", property)
        rescue Puppet::ExecutionFailure => e
            # Pass up the exception to upper level
            raise
        end
    end

    def flush
        case @resource[:name]
        when "controller_property"
            if @property_flush.has_key?(:controller)
                puts "foo"
                raise Puppet::Error, "controller_property does not have "\
                    "'controller' property. Try client_property"
                puts "bar"
            end
            
            props = []
            @property_flush.each do |key, value|
                # Change symbol to string
                k = key.to_s.gsub! "_", "-"
                case k
                # uplink-port property takes up to five values:
                # link, [vlan-range], [vxlan-range], [host] and [flat]
                when "uplink-port"
                    link, vlan, vxlan, host, flat = \
                        value.strip().split(";", -1)

                    # store host parameter if exists
                    host = host != "" ? ["-h", host] : []

                    # Concatenate the parameters of uplink-port
                    link = "uplink-port=#{link}"
                    vlan = vlan != "" ? ",vlan-range=#{vlan}" : ""
                    vxlan = vxlan != "" ? ",vxlan-range=#{vxlan}" : ""
                    flat = flat != "" ? ",flat=#{flat}" : ""

                    p = ["-p", "#{link}#{vlan}#{vxlan}#{flat}"]

                    props << [host, p]

                # uri-template property takes up to two values:
                # uri and [host]
                when "uri-template"
                    uri, host = value.strip().split(";", -1)
                    
                    # store host parameter if exists
                    host = host != "" ? ["-h", host] : []
                    uri = ["-p", "uri-template=#{uri}"]
                    
                    props << [host, uri]

                # vxlan_addr property takes up to three values:
                # vxlan-addr, [vxlan-range] and [host]
                when "vxlan-addr"
                    addr, range, host = value.strip().split(";", -1)
                    
                    # store host parameter if exists
                    host = host != "" ? ["-h", host] : []
                    addr = "vxlan-addr=#{addr}"
                    range = range != "" ? ",vxlan-range=#{range}" : ""
                    
                    p = ["-p", "#{addr}#{range}"]
                    
                    props << [host, p]

                # l2-type, vlan-range, vxlan-range, vxlan-ipvers
                # and vxlan-mgroup properties just need values
                else
                    host = []
                    p = ["-p", "#{k}=#{value}"]
                    props << [host, p]
                end
            end

            # Reverse the array so that l2-type, vlan-range, vxlan-range
            # vxlan-ipvers and vxlan-mgroups are set before the others
            props.reverse!
            # iteratively set controller property
            props.each do |p|
                begin
                    set_controller_property(p[0], p[1])
                # Do not terminate the script even if there is an error
                # Just continue to next script
                rescue Puppet::ExecutionFailure => e
                    Puppet.err "Cannot apply the property: \n#{e.inspect}"
                end
            end

        when "client_property"
            unless @property_flush.has_key?(:controller)
                raise Puppet::Error,
                    "'controller' property must be specified"
            end
            prop = "controller=#{@property_flush[:controller]}"
            begin
                set_client_property(prop) 
            rescue Puppet::ExecutionFailure => e
                raise Puppet::Error, "Cannot apply the property:\n #{e.inspect}"
            end
        end
        
        # Synchronize all the SHOULD values to IS values
        @property_hash = resource.to_hash
    end
end