You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltacloud.apache.org by mf...@apache.org on 2011/07/26 16:25:32 UTC

svn commit: r1151112 - in /incubator/deltacloud/trunk/server: config/ config/drivers/ lib/deltacloud/drivers/condor/ lib/deltacloud/drivers/condor/ip_agents/

Author: mfojtik
Date: Tue Jul 26 14:25:30 2011
New Revision: 1151112

URL: http://svn.apache.org/viewvc?rev=1151112&view=rev
Log:
Add condor-cloud driver to deltacloud.

This patch adds a condor-cloud driver to deltacloud.  This is a little
different from most drivers in that it directly drives condor and is
part of the control logic for the cloud itself.

This deals with most of the things David pointed out except for the
base_models.rb issue.  I also left the encoding of uuid and config
server because they need to go into the instance itself and really this
is the best place to do that.

    Ian

Signed-off-by: Michal fojtik <mf...@redhat.com>

Removed base_model, reogranized file layout of driver

Signed-off-by: Michal fojtik <mf...@redhat.com>

Added quite a few recent fixes.  Bridge is br0 as per docs, run submit
command as 'condor' user, renamed some stuff etc.

Signed-off-by: Ian Main <im...@redhat.com>

Added:
    incubator/deltacloud/trunk/server/config/addresses.xml
    incubator/deltacloud/trunk/server/config/condor.yaml
    incubator/deltacloud/trunk/server/config/drivers/condor.yaml
    incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/
    incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/condor_client.rb
    incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/condor_driver.rb
    incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/ip_agents/
    incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/ip_agents/confserver.rb
    incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/ip_agents/default.rb

Added: incubator/deltacloud/trunk/server/config/addresses.xml
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/config/addresses.xml?rev=1151112&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/config/addresses.xml (added)
+++ incubator/deltacloud/trunk/server/config/addresses.xml Tue Jul 26 14:25:30 2011
@@ -0,0 +1,14 @@
+<addresses>
+  <address mac="00:1A:4A:22:20:01">172.31.0.101</address>
+  <address mac="00:1A:4A:22:20:02">172.31.0.102</address>
+  <address mac="00:1A:4A:22:20:03">172.31.0.103</address>
+  <address mac="00:1A:4A:22:20:04">172.31.0.104</address>
+  <address mac="00:1A:4A:22:20:05">172.31.0.105</address>
+  <address mac="00:1A:4A:22:20:06">172.31.0.106</address>
+  <address mac="00:1A:4A:22:20:07">172.31.0.107</address>
+  <address mac="00:1A:4A:22:20:08">172.31.0.108</address>
+  <address mac="00:1A:4A:22:20:09">172.31.0.109</address>
+  <address mac="00:1A:4A:22:20:10">172.31.0.110</address>
+  <address mac="00:1A:4A:22:20:11">172.31.0.111</address>
+  <address mac="00:1A:4A:22:20:12">172.31.0.112</address>
+</addresses>

Added: incubator/deltacloud/trunk/server/config/condor.yaml
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/config/condor.yaml?rev=1151112&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/config/condor.yaml (added)
+++ incubator/deltacloud/trunk/server/config/condor.yaml Tue Jul 26 14:25:30 2011
@@ -0,0 +1,30 @@
+---
+
+# The network bridge used for VMs.
+:default_bridge: br0
+:username: condor
+:password: deltacloud
+
+# The location of your images.  These would be regular KVM formatted images.
+# Note that this variable also exists in the condor cloud configuration in
+# /usr/libexec/condor/cloud_functions.  This directory must also be shared
+# accross all nodes so that they all have access to the images.
+#
+# *** If you change this check /usr/libexec/condor/cloud_functions ***
+:image_storage: /var/lib/condor-cloud/shared_images
+
+# There are currently 2 ways to configure the IP addresses of instances,
+# one is to use the ip server agent in each image, the other is a simple
+# mapping between MAC and IP.  The latter is the default.
+#
+# For the simple mac/ip mapping, see config/addresses.xml for the mapping list.
+# You must then configure your DHCPD server to hand out those addresses
+# for the given macs.
+:default_ip_agent: DefaultIPAgent
+
+#:default_ip_agent: ConfServerIPAgent
+#:ip_agent_address: '10.34.32.181:4444'
+#:ip_agent_version: '0.0.1'
+
+:vnc_listen_port: '5900'
+:vnc_listen_ip: '0.0.0.0'

Added: incubator/deltacloud/trunk/server/config/drivers/condor.yaml
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/config/drivers/condor.yaml?rev=1151112&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/config/drivers/condor.yaml (added)
+++ incubator/deltacloud/trunk/server/config/drivers/condor.yaml Tue Jul 26 14:25:30 2011
@@ -0,0 +1,3 @@
+---
+:condor:
+  :name: Condor

Added: incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/condor_client.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/condor_client.rb?rev=1151112&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/condor_client.rb (added)
+++ incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/condor_client.rb Tue Jul 26 14:25:30 2011
@@ -0,0 +1,276 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+require 'pp'
+require 'nokogiri'
+require 'etc'
+require 'tempfile'
+require 'yaml'
+require 'time'
+require 'deltacloud/drivers/condor/ip_agents/default'
+require 'deltacloud/drivers/condor/ip_agents/confserver'
+
+module CondorCloud
+
+  class CondorAddress
+    attr_accessor :ip
+    attr_accessor :mac
+
+    def initialize(opts={})
+      @ip, @mac = opts[:ip], opts[:mac]
+    end
+  end
+
+
+  class DefaultExecutor
+
+    CONDOR_Q_CMD = ENV['CONDOR_Q_CMD'] || "condor_q"
+    CONDOR_RM_CMD = ENV['CONDOR_RM_CMD'] || "condor_rm"
+
+    # deltacloudd runs as root from the init script so we have to use su to move to
+    # 'condor' user.
+    CONDOR_SUBMIT_CMD = ENV['CONDOR_SUBMIT_CMD'] || 'su condor -m -c condor_submit'
+
+    # This directory needs to be readable for user running Deltacloud API
+    CONDOR_CONFIG = ENV['CONDOR_CONFIG'] || 'config/condor.yaml'
+
+    attr_accessor :ip_agent
+    attr_reader :config
+
+    # You can use your own IP agent using :ip_agent option.
+    # IPAgent should have parent class set to 'IPAgent' and implement all
+    # methods from this class. You can pass options to ip_agent using
+    # :ip_agent_args hash.
+    #
+    def initialize(opts={}, &block)
+      load_config!
+      if opts[:ip_agent]
+        @ip_agent = opts[:ip_agent]
+      else
+        default_ip_agent = CondorCloud::const_get(@config[:default_ip_agent])
+        @ip_agent = default_ip_agent.new(:config => @config)
+      end
+      yield self if block_given?
+      self
+    end
+
+    def load_config!
+      @config = YAML::load(File.open(CONDOR_CONFIG))
+    end
+
+    # List instances using ENV['CONDOR_Q_CMD'] command.
+    # Retrieve XML from this command and parse it using Nokogiri. Then this XML
+    # is converted to CondorCloud::Instance class
+    #
+    # @opts - This Hash can be used for filtering instances using :id => 'instance_id'
+    #
+    def instances(opts={})
+      bare_xml = Nokogiri::XML(`#{CONDOR_Q_CMD} -xml`)
+      parse_condor_q_output(bare_xml, opts)
+    end
+
+    # List all files in ENV['STORAGE_DIRECTORY'] or fallback to '/home/cloud/images'
+    # Convert files to CondorCloud::Image class
+    #
+    # @opts - This Hash can be used for filtering images using :id => 'SHA1 of
+    # name'
+    #
+    def images(opts={})
+      Dir["#{@config[:image_storage]}/*"].collect do |file|
+        next unless File::file?(file)
+        next unless File::readable?(file)
+        image = Image.new(
+          :name => File::basename(file).downcase.tr('.', '-'),
+          :owner_id => Etc.getpwuid(File.stat(file).uid).name,
+          :description => file
+        )
+        next if opts[:id] and opts[:id]!=image.id
+        image
+      end.compact
+    end
+
+    # Launch a new instance in Condor cloud using ENV['CONDOR_SUBMIT_CMD'].
+    # Return CondorCloud::Instance.
+    #
+    # @image  - Expecting CondorCloud::Image here
+    # @hardware_profile - Expecting CondorCloud::HardwareProfile here
+    #
+    # @opts - You can specify additional parameters like :name here
+    #         You can set additional parameters for libvirt using :user_data
+    #         specified in JSON format.
+    #
+    #         Parameters are:
+    #
+    #         { 'bridge_dev' : 'br0' }
+    #         { 'smbios' : 'sysinfo' }
+    #         { 'vnc_port' : '5900' }
+    #         { 'vnc_ip' : '0.0.0.0' }
+    #         { 'features' : ['acpi', 'apic', 'pae'] }
+    #         { 'sysinfo' : { 'bios_vendor' : 'Lenovo', 'system_manufacturer' : 'Virt', 'system_vendor' : 'IBM' } }
+    #
+    #         Of course you can combine them as you want, like (:user_data => "{ 'bridge_dev' : 'br0', 'vnc_ip' : 127.0.0.1 }")
+    #
+    def launch_instance(image, hardware_profile, opts={})
+      raise "Image object must be not nil" unless image
+      raise "HardwareProfile object must be not nil" unless hardware_profile
+      opts[:name] ||= "i-#{Time.now.to_i}"
+
+      # This needs to be determined by the mac/ip translation stuff.
+      # We need to call into it and have it return these variables, or at least the MAC if not the IP.
+      mac_addr = @ip_agent.find_free_mac
+      ip_addr = @ip_agent.find_ip_by_mac(mac_addr) if mac_addr && !mac_addr.empty?
+
+      libvirt_xml = "+VM_XML=\"<domain type='kvm'>
+        <name>{NAME}</name>
+        <memory>#{hardware_profile.memory.value.to_i * 1024}</memory>
+        <vcpu>#{hardware_profile.cpu.value}</vcpu>
+        <os>
+          <type arch='x86_64'>hvm</type>
+          <boot dev='hd'/>
+          <smbios mode='sysinfo'/>
+        </os>
+        <sysinfo type='smbios'>
+          <system>
+            <entry name='manufacturer'>#{opts[:config_server_address]}</entry>
+            <entry name='product'>#{opts[:uuid]}</entry>
+            <entry name='serial'>#{opts[:otp]}</entry>
+          </system>
+        </sysinfo>
+        <features>
+          <acpi/><apic/><pae/>
+        </features>
+        <clock offset='utc'/>
+        <on_poweroff>destroy</on_poweroff>
+        <on_reboot>restart</on_reboot>
+        <on_crash>restart</on_crash>
+        <devices>
+          <disk type='file' device='disk'>
+            <source file='{DISK}'/>
+            <target dev='vda' bus='virtio'/>
+            <driver name='qemu' type='qcow2'/>
+          </disk>
+          <interface type='bridge'>
+            #{"<mac address='" + mac_addr + "'/>" if mac_addr && !mac_addr.empty?}
+            <source bridge='#{@config[:default_bridge]}'/>
+          </interface>
+          <graphics type='vnc' port='#{@config[:vnc_listen_port]}' autoport='yes' keymap='en-us' listen='#{@config[:vnc_listen_ip]}'/>
+        </devices>
+      </domain>\"".gsub(/(\s{2,})/, ' ').gsub(/\>\s\</, '><')
+
+      # I use the 2>&1 to get stderr and stdout together because popen3 does not support
+      # the ability to get the exit value of the command in ruby 1.8.
+      pipe = IO.popen("#{CONDOR_SUBMIT_CMD} 2>&1", "w+")
+      pipe.puts "universe=vm"
+      pipe.puts "vm_type=kvm"
+      pipe.puts "vm_memory=#{hardware_profile.memory.value}"
+      pipe.puts "request_cpus=#{hardware_profile.cpu.value}"
+      pipe.puts "vm_disk=#{image.description}:null:null"
+      pipe.puts "executable=#{image.description}"
+      pipe.puts "vm_macaddr=#{mac_addr}"
+
+      # Only set the ip if it is available, and this should depend on the IP mapping used.
+      # With the fixed mapping method we know the IP address right away before we start the
+      # instance, so fill it in here.  If it is not set I think we should set it to an empty
+      # string and we'll fill it in later using a condor tool to update the job.
+      pipe.puts "+vm_ipaddr=\"#{ip_addr}\""
+      pipe.puts '+HookKeyword="CLOUD"'
+      pipe.puts "+Cmd=\"#{opts[:name]}\""
+      # Really the image should not be a full path to begin with I think..
+      pipe.puts "+cloud_image=\"#{File.basename(image.description)}\""
+      pipe.puts libvirt_xml
+      pipe.puts "queue"
+      pipe.puts ""
+      pipe.close_write
+      out = pipe.read
+      pipe.close
+
+      if $? != 0
+        raise "Error starting VM in condor_submit: #{out}"
+      end
+
+      bare_xml = Nokogiri::XML(`#{CONDOR_Q_CMD} -xml`)
+      parse_condor_q_output(bare_xml, :name => opts[:name])
+    end
+
+    def destroy_instance(instance_id)
+      bare_xml = Nokogiri::XML(`#{CONDOR_Q_CMD} -xml`)
+      cluster_id = (bare_xml/'/classads/c/a[@n="GlobalJobId"]/s').collect do |id|
+        id.text.split('#')[1] if id.text.split('#').last==instance_id
+      end.compact.first
+      `#{CONDOR_RM_CMD} #{cluster_id}`
+    end
+
+    # List hardware profiles available for Condor.
+    # Basically those profiles are static 'small', 'medium' and 'large'
+    #
+    # Defined as:
+    #
+    #    when { :memory => '512', :cpus => '1' } then 'small'
+    #    when { :memory => '1024', :cpus => '2' } then 'medium'
+    #    when { :memory => '2047', :cpus => '4' } then 'large'
+    #
+    # @opts - You can filter hardware_profiles using :id
+    #
+    def hardware_profiles(opts={})
+      return [
+        { :name => 'small', :cpus => 1, :memory => 512 },
+        { :name => 'medium', :cpus => 2, :memory => 1024 },
+        { :name => 'large', :cpus => 4, :memory => 2048 }
+      ]
+    end
+
+    private
+
+    def convert_image_name_to_id(name)
+      Digest::SHA1.hexdigest(name).to_s
+    end
+
+    def parse_condor_q_output(bare_xml, opts={})
+      inst_array = []
+      (bare_xml/"/classads/c").each do |c|
+        unless opts[:id].nil?
+          next unless (c/'a[@n="GlobalJobId"]/s').text.strip.split('#').last==opts[:id]
+        end
+        unless opts[:name].nil?
+          next unless (c/'a[@n="Cmd"]/s').text.strip==opts[:name]
+        end
+        # Even with the checks above this can still fail because there may be other condor jobs
+        # in the queue formatted in ways we don't know.
+        begin
+          inst_array << Instance.new(
+            :id => (c/'a[@n="GlobalJobId"]/s').text.strip.split('#').last,
+            :name => (c/'a[@n="Cmd"]/s').text.strip,
+            :state => Instance::convert_condor_state((c/'a[@n="JobStatus"]/i').text.to_i),
+            :public_addresses => [
+              CondorAddress.new(:mac => (c/'a[@n="JobVM_MACADDR"]/s').text, :ip => (c/'a[@n="vm_ipaddr"]/s').text)
+            ],
+            :instance_profile => HardwareProfile.new(:hwp_memory => (c/'a[@n="JobVMMemory"]/i').text, :hwp_cpu => (c/'a[@n="JobVM_VCPUS"]/i').text),
+            :owner_id => (c/'a[@n="User"]/s').text,
+            :image_id => convert_image_name_to_id(File::basename((c/'a[@n="VMPARAM_vm_Disk"]/s').text.split(':').first).downcase.tr('.', '-')),
+            :realm => Realm.new(:id => (c/'a[@n="JobVMType"]/s').text),
+            :launch_time => Time.at((c/'a[@n="JobStartDate"]/i').text.to_i)
+          )
+        rescue Exception => e
+          puts "Caught exception (may be safe to ignore if other jobs present): #{e}"
+          puts e.message
+          puts e.backtrace
+          # Be nice to log something here in case we start getting silent failures.
+        end
+      end
+      inst_array
+    end
+
+  end
+end

Added: incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/condor_driver.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/condor_driver.rb?rev=1151112&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/condor_driver.rb (added)
+++ incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/condor_driver.rb Tue Jul 26 14:25:30 2011
@@ -0,0 +1,236 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require 'deltacloud/base_driver'
+
+
+class Instance
+  def self.convert_condor_state(state_id)
+    case state_id
+      when 0,1,5 then 'PENDING'
+      when 2     then 'RUNNING'
+      when 3,4   then 'SHUTTING_DOWN'
+      else raise "Unknown Condor state (#{state_id})"
+    end
+  end
+end
+
+require 'deltacloud/drivers/condor/condor_client'
+
+module Deltacloud
+
+  module Drivers
+    module Condor
+
+      require 'base64'
+      require 'uuid'
+      require 'fileutils'
+
+      class CondorDriver < Deltacloud::BaseDriver
+
+        feature :instances, :user_data
+        feature :instances, :authentication_password
+
+        def supported_collections
+          DEFAULT_COLLECTIONS - [ :storage_volumes, :storage_snapshots ]
+        end
+
+        CONDOR_MAPPER_DIR = ENV['CONDOR_MAPPER_DIR'] || '/var/tmp'
+
+        def hardware_profiles(credentials, opts={})
+          results = []
+          new_client(credentials) do |condor|
+            results = condor.hardware_profiles.collect do |hwp|
+              HardwareProfile::new(hwp[:name]) do
+                architecture 'x86_64'
+                memory  hwp[:memory]
+                cpu     hwp[:cpus]
+                storage 100
+              end
+            end
+          end
+          filter_hardware_profiles(results, opts)
+        end
+
+        def realms(credentials, opts={})
+          [
+            Realm::new(
+              :id => 'default',
+              :name => 'Default Condor Realm',
+              :limit => :unlimited,
+              :state => 'AVAILABLE'
+            )
+          ]
+        end
+
+        def images(credentials, opts={})
+          results = []
+          new_client(credentials) do |condor|
+            results = condor.images.collect do |image|
+              Image::new(
+                :id => Digest::SHA1.hexdigest(image.name).to_s,
+                :name => image.name.split(':').first,
+                :state => image.state || 'AVAILABLE',
+                :architecture => 'x86_64',
+                :owner_id => image.owner_id || 'unknown',
+                :description => image.description
+              )
+            end
+          end
+          filter_on( results, :id, opts )
+        end
+
+        def instances(credentials, opts={})
+          results = []
+          new_client(credentials) do |condor|
+            results = condor.instances.collect do |instance|
+              vm_uuid = get_value(:uuid, instance.id)
+              ip_address = condor.ip_agent.find_ip_by_mac(vm_uuid)
+              Instance::new(
+                :id => instance.id,
+                :name => instance.name,
+                :realm_id => 'default',
+                :instance_profile => InstanceProfile::new(instance.instance_profile.name),
+                :image_id => instance.image_id,
+                :public_addresses => [ ip_address ],
+                :owner_id => instance.owner_id,
+                :description => instance.name,
+                :architecture => 'x86_64',
+                :actions => instance_actions_for(instance.state),
+                :launch_time => instance.launch_time,
+                :username => 'root',
+                :password => opts[:password],
+                :state => instance.state
+              )
+            end
+          end
+          results = filter_on( results, :state, opts )
+          filter_on( results, :id, opts )
+        end
+
+        def create_instance(credentials, image_id, opts={})
+          # User data should contain this Base64 encoded configuration:
+          #
+          # $config_server_ip:[$uuid]
+          #
+          # $config_server - IP address of Configuration Server to use (eg. 192.168.1.1)
+          # $uuid          - UUID to use for instance (will be used for ConfServer <-> DC
+          #                  API communication)
+          # $otp           - One-time-password
+          #
+          user_data = opts[:user_data] ? Base64.decode64(opts[:user_data]) : nil
+          if user_data
+            config_server_address, vm_uuid, vm_otp = opts[:user_data].strip.split(';')
+            if vm_uuid.nil? and vm_otp.nil?
+              vm_uuid = config_server_address
+              config_server_address = nil
+            end
+          end
+          vm_uuid ||= UUID::new.generate
+          vm_otp ||= vm_uuid[0..7]
+          new_client(credentials) do |condor|
+            config_server_address ||= condor.ip_agent.address
+            image = images(credentials, :id => image_id).first
+            hardware_profile = hardware_profiles(credentials, :id => opts[:hwp_id] || 'small').first
+            instance = condor.launch_instance(image, hardware_profile, {
+              :name => opts[:name] || "i-#{Time.now.to_i}",
+              :config_server_address => config_server_address,
+              :uuid => vm_uuid,
+              :otp => vm_otp,
+            }).first
+            store(:uuid, vm_uuid, instance.id)
+            raise "Error: VM not launched" unless instance
+            instance(credentials, { :id => instance.id, :password => vm_otp })
+          end
+        end
+
+        def destroy_instance(credentials, instance_id)
+          old_instance = instance(credentials, :id => instance_id)
+          new_client(credentials) do |condor|
+            condor.destroy_instance(instance_id)
+            remove_key(:uuid, instance_id)
+            remove_key(:mac, instance_id)
+          end
+          old_instance.state = 'PENDING'
+          old_instance.actions = instance_actions_for(old_instance.state),
+          old_instance
+        end
+
+        define_instance_states do
+          start.to( :pending )          .automatically
+          pending.to( :running )        .automatically
+          pending.to( :finish )         .on(:destroy)
+          running.to( :running )        .on( :reboot )
+          running.to( :shutting_down )  .on( :destroy )
+          pending.to( :finish )         .automatically
+        end
+
+        def valid_credentials?(credentials)
+          if ( credentials.user != @config[:username] ) or ( credentials.password != @config[:password] )
+            return false
+          end
+          return true
+        end
+
+        exceptions do
+          on /AuthException/ do
+            status 401
+          end
+          on /ERROR/ do
+            status 502
+          end
+        end
+
+        private
+
+        def new_client(credentials)
+          if ( credentials.user != 'condor' ) or ( credentials.password != 'deltacloud' )
+            raise Deltacloud::ExceptionHandler::AuthenticationFailure.new
+          end
+          safely do
+            yield CondorCloud::DefaultExecutor.new
+          end
+        end
+
+        def store(item, key, value)
+          FileUtils.mkdir_p(File.join(CONDOR_MAPPER_DIR, item.to_s))
+          File.open(File.join(CONDOR_MAPPER_DIR, item.to_s, key), 'w') do |f|
+            f.puts(value)
+          end
+        end
+
+        def get_value(key, id)
+          begin
+            File.open(File.join(CONDOR_MAPPER_DIR, key.to_s, id)).read.strip
+          rescue Errno::ENOENT
+            puts "Warning: Could not find entry for #{key} #{id} (#{File.join(CONDOR_MAPPER_DIR, key.to_s, id)})"
+            nil
+          end
+        end
+
+        def remove_key(key, id)
+          begin
+            FileUtils::rm(File.join(CONDOR_MAPPER_DIR, key.to_s, id))
+          rescue
+            # We should probably check for specific error conditions here.  Some we will want to log or throw an error for.
+            puts "Warning: Cannot remove #{key} mapping for instance #{id} (#{File.join(CONDOR_MAPPER_DIR, key.to_s, id)})"
+            nil
+          end
+        end
+      end
+    end
+  end
+end

Added: incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/ip_agents/confserver.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/ip_agents/confserver.rb?rev=1151112&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/ip_agents/confserver.rb (added)
+++ incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/ip_agents/confserver.rb Tue Jul 26 14:25:30 2011
@@ -0,0 +1,76 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+
+module CondorCloud
+
+  require 'nokogiri'
+  require 'rest-client'
+
+  class ConfServerIPAgent < IPAgent
+
+    def initialize(opts={})
+      @config = opts[:config]
+      self.address = ENV['CONFIG_SERVER_ADDRESS'] || @config[:ip_agent_address] || "10.34.32.181:4444"
+      @version = @config[:ip_agent_version] || '0.0.1'
+      @client = RestClient::Resource::new(self.address)
+      # TODO: Manage MAC addresses through ConfServer
+      @mappings = Nokogiri::XML(File.open(opts[:file] || File.join('config', 'addresses.xml')))
+    end
+
+    def find_ip_by_mac(uuid)
+      begin
+        @client["/ip/%s/%s" % [@version, uuid]].get(:accept => 'text/plain').body.strip
+      rescue RestClient::ResourceNotFound
+        '127.0.0.1'
+      rescue
+        puts 'ERROR: Could not query ConfServer for an IP'
+      end
+    end
+
+    def find_mac_by_ip(ip)
+    end
+
+    def find_free_mac
+      addr_hash = {}
+      DefaultExecutor::new do |executor|
+        addresses = (@mappings/'/addresses/address').collect { |a| Address.new(:ip => a.text.strip, :mac => a[:mac]) }
+
+        # Make an address hash to speed up the inner loop.
+        addresses.each do |address|
+          addr_hash[address.mac] = address.ip
+        end
+
+        executor.instances.each do |instance|
+          instance.public_addresses.each do |public_address|
+            if addr_hash.key?(public_address.mac)
+              addr_hash.delete(public_address.mac)
+            end
+          end
+        end
+      end
+
+      raise "No available MACs to assign to instance." if addr_hash.empty?
+
+      return addr_hash.keys.first
+    end
+
+    def addresses
+      (@mappings/'/addresses/address').collect { |a| Address.new(:ip => a.text.strip, :mac => a[:mac]) }
+    end
+
+  end
+end

Added: incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/ip_agents/default.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/ip_agents/default.rb?rev=1151112&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/ip_agents/default.rb (added)
+++ incubator/deltacloud/trunk/server/lib/deltacloud/drivers/condor/ip_agents/default.rb Tue Jul 26 14:25:30 2011
@@ -0,0 +1,84 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require 'nokogiri'
+
+module CondorCloud
+
+  class IPAgent
+
+    attr_accessor :address
+
+    def find_mac_by_ip(ip); end
+    def find_ip_by_mac(mac); end
+    def find_free_mac
+      return nil
+    end
+
+    # This method must return an Array of 'CondorAddress' objects
+    # [ CondorAddress.new, CondorAddress.new ]
+    def addresses; end
+  end
+
+
+  # Default IP agent will lookup addresses from XML
+  # files stored in config directory.
+  # You can overide default directory using { :file => 'path' }
+  #
+  class DefaultIPAgent < IPAgent
+
+    def initialize(opts={})
+      @mappings = Nokogiri::XML(File.open(opts[:file] || File.join('config', 'addresses.xml')))
+    end
+
+    def find_free_mac
+      addr_hash = {}
+      DefaultExecutor::new do |executor|
+        addresses = (@mappings/'/addresses/address').collect { |a| CondorAddress.new(:ip => a.text.strip, :mac => a[:mac]) }
+
+        # Make an address hash to speed up the inner loop.
+        addresses.each do |address|
+          addr_hash[address.mac] = address.ip
+        end
+
+        executor.instances.each do |instance|
+          instance.public_addresses.each do |public_address|
+            if addr_hash.key?(public_address.mac)
+              addr_hash.delete(public_address.mac)
+            end
+          end
+        end
+      end
+
+      raise "No available MACs to assign to instance." if addr_hash.empty?
+
+      return addr_hash.keys.first
+    end
+
+    def find_ip_by_mac(mac)
+      t = (@mappings/"/addresses/address[@mac='#{mac}']").text
+      t.empty? ? nil : t
+    end
+
+    def find_mac_by_ip(ip)
+      (@mappings/"/addresses/address[.='#{ip}']").first[:mac] rescue nil
+    end
+
+    def addresses
+      (@mappings/'/addresses/address').collect { |a| CondorAddress.new(:ip => a.text.strip, :mac => a[:mac]) }
+    end
+  end
+end