Hi,

I was playing a bit with adding DataMapper to Deltacloud to support storing certain CIMI attributes that cannot be stored on the provider side (like 'description', etc.). We were discussing earlier that for that attributes we will need to create some persistent storage on Deltacloud side. So I put together a small PoC of how complex (or not complex) this will be:

So let say you want to create a new Machine. Currently if you specify 'description' in JSON body, we ignore this property because there is now way how to store it on the backend side (using the driver method).

So you have this JSON:

 ~/code/core/server › cat machine.json
{
  "resourceURI": "http://schemas.dmtf.org/cimi/1/MachineCreate";,
  "name": "myMachine1",
  "description": "My very first machine",
  "machineTemplate": {
"machineConfig": { "href": "http://localhost:3001/cimi/machine_configurations/m1-small"; }, "machineImage": { "href": "http://localhost:3001/cimi/machine_images/img1"; }
  }
}

And you create Machine using this curl command:

curl -v --user "mockuser:mockpassword" -X POST \
        http://localhost:3001/cimi/machines \
        -H "Content-Type: application/json" -d @machine.json


With the patch I attached, you will see something like this in Deltacloud API log:

<snip>
~ (0.000047) SELECT "id", "name", "uuid", "entity" FROM "deltacloud_database_entity_properties" WHERE ("uuid" = 'f6d93dfa8eaca129d7412a8d58980a5948361ae8' AND "entity" = 'Instance:inst7' AND "name" = 'description') ORDER BY "id" LIMIT 1 ~ (0.017038) INSERT INTO "deltacloud_database_entity_properties" ("name", "uuid", "entity") VALUES ('description', 'f6d93dfa8eaca129d7412a8d58980a5948361ae8', 'Instance:inst7') ~ (0.014218) UPDATE "deltacloud_database_entity_properties" SET "value" = 'My very first machine' WHERE "id" = 4 ~ (0.000104) SELECT "id", "name", "uuid", "entity" FROM "deltacloud_database_entity_properties" WHERE ("uuid" = 'f6d93dfa8eaca129d7412a8d58980a5948361ae8' AND "entity" = 'Instance:inst7' AND "name" = 'description') ORDER BY "id" LIMIT 1 ~ (0.000045) SELECT "id", "value" FROM "deltacloud_database_entity_properties" WHERE "id" = 4 ORDER BY "id" 127.0.0.1 - - [13/Nov/2012 12:45:03] "POST /cimi/machines HTTP/1.1" 201 862 0.2704
</snip>

As you can see DataMapper will store the 'description' property for the 'Instance' model with id 'inst7' (the newly created instance).

The horrible looking 'uuid' field is SHA1 hash composed from credentials, driver name and provider string, so different clients will not override their properties.

Once this property is stored, I used the 'get_property_value' method, that require just instance of the Deltacloud model and property name to get the property value.

Let me know what you think and if we want to go this way or we need something more complex.

The patch is also uploaded in Tracker:

http://tracker.deltacloud.org/set/123


-- Michal


--

Michal Fojtik <[email protected]>
Deltacloud API, CloudForms
>From 97174fd47be1fee9c030c44494b393654af06e23 Mon Sep 17 00:00:00 2001
From: Michal Fojtik <[email protected]>
Date: Tue, 13 Nov 2012 11:59:06 +0100
Subject: [PATCH core] CIMI: Added support for DataMapper and entity_property

Added 'EntityProperty' DataMapper class that will store
certain properties for CIMI that are not supported on the backend
cloud (like 'description').

Signed-off-by: Michal fojtik <[email protected]>
---
 server/config.ru                           |  1 +
 server/deltacloud-core.gemspec             |  3 +++
 server/lib/cimi/collections/base.rb        |  1 +
 server/lib/cimi/helpers.rb                 |  1 +
 server/lib/cimi/models/machine.rb          |  5 +++-
 server/lib/db.rb                           | 41 ++++++++++++++++++++++++++++++
 server/lib/db/entity_property.rb           | 24 +++++++++++++++++
 server/lib/deltacloud/models/base_model.rb |  4 +++
 8 files changed, 79 insertions(+), 1 deletion(-)
 create mode 100644 server/lib/db.rb
 create mode 100644 server/lib/db/entity_property.rb

diff --git a/server/config.ru b/server/config.ru
index 8ef20e5..f842e10 100644
--- a/server/config.ru
+++ b/server/config.ru
@@ -54,6 +54,7 @@ end
 #       different root_url's
 #
 frontends.each do |frontend|
+  frontend = frontend.strip
   if Deltacloud[frontend.to_sym].nil?
     puts "ERROR: Unknown frontend (#{frontend}). Valid values are 'deltacloud,cimi,ec2'"
     exit(1)
diff --git a/server/deltacloud-core.gemspec b/server/deltacloud-core.gemspec
index cb9a8c8..0b60396 100644
--- a/server/deltacloud-core.gemspec
+++ b/server/deltacloud-core.gemspec
@@ -79,6 +79,9 @@ Gem::Specification.new do |s|
   s.add_dependency('nokogiri', '>= 1.4.3')
   s.add_dependency('require_relative') if RUBY_VERSION < '1.9'
 
+  s.add_dependency('data_mapper')
+  s.add_dependency('dm-sqlite-adapter')
+
   # dependencies for various cloud providers:
 
   # RHEV-M and oVirt
diff --git a/server/lib/cimi/collections/base.rb b/server/lib/cimi/collections/base.rb
index a78b78a..30c595b 100644
--- a/server/lib/cimi/collections/base.rb
+++ b/server/lib/cimi/collections/base.rb
@@ -23,6 +23,7 @@ module CIMI::Collections
     include CIMI::Model
 
     helpers Deltacloud::Helpers::Drivers
+    helpers Deltacloud::Helpers::Database
     helpers Sinatra::AuthHelper
     helpers Sinatra::Rabbit::URLHelper
     helpers Deltacloud::Helpers::Application
diff --git a/server/lib/cimi/helpers.rb b/server/lib/cimi/helpers.rb
index 9d069b1..c2f2461 100644
--- a/server/lib/cimi/helpers.rb
+++ b/server/lib/cimi/helpers.rb
@@ -26,6 +26,7 @@ end
 # Declare namespace for CIMI models
 #
 
+require_relative '../db'
 require_relative '../deltacloud/drivers'
 require_relative '../deltacloud/models'
 require_relative '../deltacloud/helpers/driver_helper'
diff --git a/server/lib/cimi/models/machine.rb b/server/lib/cimi/models/machine.rb
index 648c395..db76b4a 100644
--- a/server/lib/cimi/models/machine.rb
+++ b/server/lib/cimi/models/machine.rb
@@ -58,6 +58,7 @@ class CIMI::Model::Machine < CIMI::Model::Base
     instance = context.driver.create_instance(context.credentials, image_id, {
       :hwp_id => hardware_profile_id
     }.merge(additional_params))
+    context.store_property(instance, 'description', json['description']) if json['description']
     from_instance(instance, context)
   end
 
@@ -74,6 +75,7 @@ class CIMI::Model::Machine < CIMI::Model::Base
     instance = context.driver.create_instance(context.credentials, image_id, {
       :hwp_id => hardware_profile_id
     }.merge(additional_params))
+    context.store_property(instance, 'description', xml['description']) if xml['description']
     from_instance(instance, context)
   end
 
@@ -90,6 +92,7 @@ class CIMI::Model::Machine < CIMI::Model::Base
   end
 
   def self.delete!(id, context)
+    context.destroy_properties(context, Instance.new(:id => id))
     context.driver.destroy_instance(context.credentials, id)
   end
 
@@ -124,7 +127,7 @@ class CIMI::Model::Machine < CIMI::Model::Base
     machine_conf = CIMI::Model::MachineConfiguration.find(instance.instance_profile.name, context)
     machine_spec = {
       :name => instance.name,
-      :description => "Instance #{instance.name}",
+      :description => context.get_property_value(instance, 'description') || "No description set for Machine #{instance.name}",
       :created => instance.launch_time.nil? ? Time.now.xmlschema : Time.parse(instance.launch_time).xmlschema,
       :id => context.machine_url(instance.id),
       :state => convert_instance_state(instance.state),
diff --git a/server/lib/db.rb b/server/lib/db.rb
new file mode 100644
index 0000000..0c7b272
--- /dev/null
+++ b/server/lib/db.rb
@@ -0,0 +1,41 @@
+module Deltacloud
+  require 'data_mapper'
+  require_relative './db/entity_property'
+
+  DATABASE_LOCATION = ENV['DATABASE_LOCATION'] || "/var/tmp/deltacloud-mock-#{ENV['USER']}/db.sqlite"
+
+  def self.initialize_database
+    DataMapper::Logger.new($stdout, :debug)
+    DataMapper::setup(:default, "sqlite://#{DATABASE_LOCATION}")
+    DataMapper::finalize
+    DataMapper::auto_upgrade!
+  end
+
+  module Helpers
+    module Database
+      include Deltacloud::Database
+
+      def store_property(model, name, value)
+        EntityProperty.first_or_create(:uuid => property_uuid, :entity => model.to_entity, :name => name).update(:value => value)
+      end
+
+      def get_property_value(model, name)
+        ep = EntityProperty.first(:uuid => property_uuid, :entity => model.to_entity, :name => name)
+        !ep.nil? ? ep.value : nil
+      end
+
+      def destroy_properties(model)
+        EntityProperty.all(:uuid => property_uuid, :entity => model.to_entity).destroy
+      end
+
+      def property_uuid
+        Digest::SHA1.hexdigest("#{credentials.user}:#{credentials.password}:#{driver_symbol}:"+
+                               "#{Thread.current[:provider] || ENV['API_PROVIDER'] || 'default'}")
+      end
+
+    end
+  end
+
+end
+
+Deltacloud::initialize_database
diff --git a/server/lib/db/entity_property.rb b/server/lib/db/entity_property.rb
new file mode 100644
index 0000000..b76b110
--- /dev/null
+++ b/server/lib/db/entity_property.rb
@@ -0,0 +1,24 @@
+module Deltacloud
+  module Database
+
+    require 'digest/sha1'
+
+    class EntityProperty
+      include DataMapper::Resource
+
+      property :id, Serial
+
+      # Property name and value (:description, :name, etc...)
+      property :name, String, :required => true
+      property :value, Text
+
+      # UUID is unique key that define cloud account
+      property :uuid, String, :required => true
+
+      # Entity is a Deltacloud model with an ID (Instance:inst123)
+      property :entity, String, :required => true
+
+    end
+
+  end
+end
diff --git a/server/lib/deltacloud/models/base_model.rb b/server/lib/deltacloud/models/base_model.rb
index 5c19ad5..bd76d25 100644
--- a/server/lib/deltacloud/models/base_model.rb
+++ b/server/lib/deltacloud/models/base_model.rb
@@ -44,4 +44,8 @@ class BaseModel
     @id
   end
 
+  def to_entity
+    "#{self.class.name}:#{@id}"
+  end
+
 end
-- 
1.8.0

Reply via email to