deltacloud-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lut...@redhat.com
Subject [PATCH] CIMI: split models into model and service objects
Date Fri, 01 Mar 2013 00:15:27 GMT
From: David Lutterkort <lutter@redhat.com>

The current CIMI::Model classes address two concerns:

  * serialization/deserialization of CIMI objects
  * interaction with the current driver and the DB

This patch splits these two concerns into two separate class hierarchies:
CIMI::Model for (de)serialization and CIMI::Service for interacting with
drivers/db

Besides cleaning up the code, this will make it possible to reuse
CIMI::Model classes as the basis for client code.
---
 server/lib/cimi/collections/base.rb  |   4 +-
 server/lib/cimi/models/collection.rb |  27 ++----
 server/lib/cimi/models/machine.rb    | 125 --------------------------
 server/lib/cimi/service.rb           |  21 +++++
 server/lib/cimi/service/base.rb      | 169 +++++++++++++++++++++++++++++++++++
 server/lib/cimi/service/machine.rb   | 147 ++++++++++++++++++++++++++++++
 6 files changed, 347 insertions(+), 146 deletions(-)
 create mode 100644 server/lib/cimi/service.rb
 create mode 100644 server/lib/cimi/service/base.rb
 create mode 100644 server/lib/cimi/service/machine.rb

diff --git a/server/lib/cimi/collections/base.rb b/server/lib/cimi/collections/base.rb
index cf92143..f268080 100644
--- a/server/lib/cimi/collections/base.rb
+++ b/server/lib/cimi/collections/base.rb
@@ -13,14 +13,14 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-require_relative '../models'
+require_relative '../service'
 
 module CIMI::Collections
   class Base < Sinatra::Base
 
     include Sinatra::Rabbit
     include Sinatra::Rabbit::Features
-    include CIMI::Model
+    include CIMI::Service
 
     helpers Deltacloud::Helpers::Drivers
     helpers Deltacloud::Helpers::Database
diff --git a/server/lib/cimi/models/collection.rb b/server/lib/cimi/models/collection.rb
index f36c081..cc95ab7 100644
--- a/server/lib/cimi/models/collection.rb
+++ b/server/lib/cimi/models/collection.rb
@@ -120,26 +120,15 @@ module CIMI::Model
     end
 
     # Return a collection of entities
-    def list(context)
-      entries = find(:all, context)
-      desc = "#{self.name.split("::").last} Collection for the #{context.driver.name.capitalize}
driver"
-      acts_as_root_entity unless collection_class
-      id = context.send("#{collection_class.entry_name}_url")
-      ops = []
-      cimi_entity = collection_class.entry_name.to_s.singularize
-      cimi_create = "create_#{cimi_entity}_url"
-      dcloud_create = context.deltacloud_create_method_for(cimi_entity)
-      if(context.respond_to?(cimi_create) &&
-         context.driver.respond_to?(dcloud_create)) ||
-      provides?(cimi_entity)
-        url = context.send(cimi_create)
-        ops << { :rel => "add", :href => url }
+    def list(id, entries, params = {})
+      params[:id] = id
+      params[:entries] = entries
+      params[:count] = params[:entries].size
+      if params[:add_url]
+        params[:operations] ||= []
+        params[:operations] << { :rel => "add", :href => params.delete(:add_url)
}
       end
-      collection_class.new(:id => id,
-                           :count => entries.size,
-                           :entries => entries,
-                           :operations => ops,
-                           :description => desc)
+      collection_class.new(params)
     end
 
     def all(context)
diff --git a/server/lib/cimi/models/machine.rb b/server/lib/cimi/models/machine.rb
index 3beb2f7..6990fb9 100644
--- a/server/lib/cimi/models/machine.rb
+++ b/server/lib/cimi/models/machine.rb
@@ -38,129 +38,4 @@ class CIMI::Model::Machine < CIMI::Model::Base
     scalar :rel, :href
   end
 
-  def self.find(id, context)
-    instances = []
-    if id == :all
-      instances = context.driver.instances(context.credentials)
-      instances.map { |instance| from_instance(instance, context) }.compact
-    else
-      instance = context.driver.instance(context.credentials, :id => id)
-      raise CIMI::Model::NotFound unless instance
-      from_instance(instance, context)
-    end
-  end
-
-  def perform(action, context, &block)
-    begin
-      if context.driver.send(:"#{action.name}_instance", context.credentials, self.id.split("/").last)
-        block.callback :success
-      else
-        raise "Operation failed to execute on given Machine"
-      end
-    rescue => e
-      block.callback :failure, e.message
-    end
-  end
-
-  def self.delete!(id, context)
-    context.driver.destroy_instance(context.credentials, id)
-    new(:id => id).destroy
-  end
-
-  #returns the newly attach machine_volume
-  def self.attach_volume(volume, location, context)
-    context.driver.attach_storage_volume(context.credentials,
-     {:id=>volume, :instance_id=>context.params[:id], :device=>location})
-    CIMI::Model::MachineVolume.find(context.params[:id], context, volume)
-  end
-
-  #returns the machine_volume_collection for the given machine
-  def self.detach_volume(volume, location, context)
-    context.driver.detach_storage_volume(context.credentials,
-     {:id=>volume, :instance_id=>context.params[:id], :device=>location})
-    CIMI::Model::MachineVolume.collection_for_instance(context.params[:id], context)
-  end
-
-  def self.from_instance(instance, context)
-    cpu =  memory = (instance.instance_profile.id == "opaque")? "n/a" : nil
-    machine_conf = CIMI::Model::MachineConfiguration.find(instance.instance_profile.name,
context)
-    machine_spec = {
-      :name => instance.name,
-      :created => instance.launch_time.nil? ? Time.now.xmlschema : Time.parse(instance.launch_time.to_s).xmlschema,
-      :description => "No description set for Machine #{instance.name}",
-      :id => context.machine_url(instance.id),
-      :state => convert_instance_state(instance.state),
-      :cpu => cpu || convert_instance_cpu(instance.instance_profile, context),
-      :memory => memory || convert_instance_memory(instance.instance_profile, context),
-      :disks => { :href => context.machine_url(instance.id)+"/disks"},
-      :volumes => { :href=>context.machine_url(instance.id)+"/volumes"},
-      :operations => convert_instance_actions(instance, context)
-    }
-    if context.expand? :disks
-      machine_spec[:disks] = CIMI::Model::Disk.find(instance, machine_conf, context, :all)
-    end
-    if context.expand? :volumes
-      machine_spec[:volumes] = CIMI::Model::MachineVolume.find(instance.id, context, :all)
-    end
-    machine_spec[:realm] = instance.realm_id if instance.realm_id
-    machine_spec[:machine_image] = { :href => context.machine_image_url(instance.image_id)
} if instance.image_id
-    self.new(machine_spec)
-  end
-
-  # FIXME: This will convert 'RUNNING' state to 'STARTED'
-  # which is defined in CIMI (p65)
-  #
-  def self.convert_instance_state(state)
-    case state
-      when "RUNNING" then "STARTED"
-      when "PENDING" then "CREATING" #aruba only exception... could be "STARTING" here
-      else state
-    end
-  end
-
-  def self.convert_instance_cpu(profile, context)
-    cpu_override = profile.overrides.find { |p, v| p == :cpu }
-    if cpu_override.nil?
-      CIMI::Model::MachineConfiguration.find(profile.id, context).cpu
-    else
-      cpu_override[1]
-    end
-  end
-
-  def self.convert_instance_memory(profile, context)
-    machine_conf = CIMI::Model::MachineConfiguration.find(profile.name, context)
-    memory_override = profile.overrides.find { |p, v| p == :memory }
-    memory_override.nil? ? machine_conf.memory.to_i : context.to_kibibyte(memory_override[1].to_i,"MB")
-  end
-
-  def self.convert_instance_addresses(instance)
-    (instance.public_addresses + instance.private_addresses).collect do |address|
-      {
-        :hostname => address.is_hostname? ? address : nil,
-        :mac_address => address.is_mac? ? address : nil,
-        :state => 'Active',
-        :protocol => 'IPv4',
-        :address => address.is_ipv4? ? address : nil,
-        :allocation => 'Static'
-      }
-    end
-  end
-
-  def self.convert_instance_actions(instance, context)
-    actions = instance.actions.collect do |action|
-      action = :restart if action == :reboot
-      name = action
-      name = :delete if action == :destroy # In CIMI destroy operation become delete
-      { :href => context.send(:"#{action}_machine_url", instance.id), :rel => "http://schemas.dmtf.org/cimi/1/action/#{name}"
}
-    end
-    actions <<  { :href => context.send(:"machine_images_url"), :rel => "http://schemas.dmtf.org/cimi/1/action/capture"
} if instance.can_create_image?
-    actions
-  end
-
-  def self.convert_storage_volumes(instance, context)
-    instance.storage_volumes ||= [] #deal with nilpointers
-    instance.storage_volumes.map{|vol| {:href=>context.volume_url(vol.keys.first),
-                                       :initial_location=>vol.values.first} }
-  end
-
 end
diff --git a/server/lib/cimi/service.rb b/server/lib/cimi/service.rb
new file mode 100644
index 0000000..c9b662d
--- /dev/null
+++ b/server/lib/cimi/service.rb
@@ -0,0 +1,21 @@
+# 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 require_relatived 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 CIMI::Service; end
+
+require_relative './models'
+require_relative './service/base'
+require_relative './service/machine'
diff --git a/server/lib/cimi/service/base.rb b/server/lib/cimi/service/base.rb
new file mode 100644
index 0000000..9c281b7
--- /dev/null
+++ b/server/lib/cimi/service/base.rb
@@ -0,0 +1,169 @@
+# 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 'xmlsimple'
+
+require_relative '../helpers/database_helper'
+
+# Service objects implement the server functionality of CIMI resources; in
+# particular, these objects are responsible for interacting with the
+# current driver. They use the CIMI::Model objects for (de)serialization
+module CIMI::Service
+
+  class Base
+
+    # Extend the base model with database methods
+    extend Deltacloud::Helpers::Database
+
+    attr_reader :model, :context
+
+    class << self
+      def model_class
+        CIMI::Model.const_get(name.split('::').last)
+      end
+
+      def model_name
+        name.split('::').last.underscore.to_sym
+      end
+
+      def collection_name
+        name.split('::').last.underscore.pluralize.to_sym
+      end
+
+      def inherited(subclass)
+        # Decorate all the attributes of the model class
+        schema = subclass.model_class.schema
+        schema.attribute_names.each do |name|
+          define_method(name) { self[name] }
+          define_method(:"#{name}=") { |newval| self[name] = newval }
+        end
+      end
+    end
+
+    def initialize(context, opts)
+      if opts[:values]
+        @model = model_class.new(opts[:values])
+      elsif opts[:model]
+        @model = opts[:model]
+      else
+        @model = model_class.new({})
+      end
+      @context = context
+      retrieve_entity
+    end
+
+    def model_class
+      self.class.model_class
+    end
+
+    # Decorate some model methods
+    def []=(a, v)
+      v = (@model[a] = v)
+      retrieve_entity if a == :id
+      v
+    end
+
+    def [](a)
+      @model[a]
+    end
+
+    def to_xml
+      @model.to_xml
+    end
+
+    def to_json
+      @model.to_json
+    end
+
+    def select_attributes(attr_list)
+      @model.select_attributes(attr_list)
+    end
+
+    def self.list(ctx)
+      id = ctx.send("#{collection_name}_url")
+      entries = find(:all, ctx)
+      params = {}
+      params[:desc] = "#{self.name.split("::").last} Collection for the #{ctx.driver.name.capitalize}
driver"
+      params[:add_url] = create_url(ctx)
+      model_class.list(id, entries, params)
+    end
+
+    def self.create_url(ctx)
+      cimi_create = "create_#{model_name}_url"
+      dcloud_create = ctx.deltacloud_create_method_for(model_name)
+      if(ctx.respond_to?(cimi_create) &&
+         ctx.respond_to?(dcloud_create)) || provides?(model_name)
+        ctx.send(cimi_create)
+      end
+    end
+
+    # Save the common attributes name, description, and properties to the
+    # database
+    def save
+      if @entity
+        @entity.name = @model.name
+        @entity.description = @model.description
+        @entity.properties = @model.property
+        @entity.save
+      end
+      self
+    end
+
+    # Destroy the database attributes for this model
+    def destroy
+      @entity.destroy
+      self
+    end
+
+    # FIXME: Kludge around the fact that we do not have proper *Create
+    # objects that deserialize properties by themselves
+    def extract_properties!(data)
+      h = {}
+      if data['property']
+        # Data came from XML
+        h = data['property'].inject({}) do |r,v|
+          r[v['key']] = v['content']
+          r
+        end
+      elsif data['properties']
+        h = data['properties']
+      end
+      property ||= {}
+      property.merge!(h)
+    end
+
+    def ref_id(ref_url)
+      ref_url.split('/').last if ref_url
+    end
+
+    private
+
+    # Load an existing database entity for this object, or create a new one
+    def retrieve_entity
+      if self.id
+        @entity = Deltacloud::Database::Entity::retrieve(self)
+        if @entity.exists?
+          @model.name = @entity.name
+          @model.description = @entity.description
+          @model.property ||= {}
+          @model.property.merge!(@entity.properties)
+        end
+      else
+        @entity = nil
+      end
+    end
+
+  end
+end
diff --git a/server/lib/cimi/service/machine.rb b/server/lib/cimi/service/machine.rb
new file mode 100644
index 0000000..dedb0ad
--- /dev/null
+++ b/server/lib/cimi/service/machine.rb
@@ -0,0 +1,147 @@
+# 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.
+
+class CIMI::Service::Machine < CIMI::Service::Base
+
+  def initialize(ctx, opts)
+    super
+  end
+
+  def self.find(id, context)
+    instances = []
+    if id == :all
+      instances = context.driver.instances(context.credentials)
+      instances.map { |instance| from_instance(instance, context) }.compact
+    else
+      instance = context.driver.instance(context.credentials, :id => id)
+      raise CIMI::Model::NotFound unless instance
+      from_instance(instance, context)
+    end
+  end
+
+  def perform(action, context, &block)
+    begin
+      if context.driver.send(:"#{action.name}_instance", context.credentials, self.id.split("/").last)
+        block.callback :success
+      else
+        raise "Operation failed to execute on given Machine"
+      end
+    rescue => e
+      block.callback :failure, e.message
+    end
+  end
+
+  def self.delete!(id, context)
+    context.driver.destroy_instance(context.credentials, id)
+    new(:id => id).destroy
+  end
+
+  #returns the newly attach machine_volume
+  def self.attach_volume(volume, location, context)
+    context.driver.attach_storage_volume(context.credentials,
+     {:id=>volume, :instance_id=>context.params[:id], :device=>location})
+    CIMI::Model::MachineVolume.find(context.params[:id], context, volume)
+  end
+
+  #returns the machine_volume_collection for the given machine
+  def self.detach_volume(volume, location, context)
+    context.driver.detach_storage_volume(context.credentials,
+     {:id=>volume, :instance_id=>context.params[:id], :device=>location})
+    CIMI::Model::MachineVolume.collection_for_instance(context.params[:id], context)
+  end
+
+  def self.from_instance(instance, context)
+    cpu =  memory = (instance.instance_profile.id == "opaque")? "n/a" : nil
+    machine_conf = CIMI::Model::MachineConfiguration.find(instance.instance_profile.name,
context)
+    machine_spec = {
+      :name => instance.name,
+      :created => instance.launch_time.nil? ? Time.now.xmlschema : Time.parse(instance.launch_time.to_s).xmlschema,
+      :description => "No description set for Machine #{instance.name}",
+      :id => context.machine_url(instance.id),
+      :state => convert_instance_state(instance.state),
+      :cpu => cpu || convert_instance_cpu(instance.instance_profile, context),
+      :memory => memory || convert_instance_memory(instance.instance_profile, context),
+      :disks => { :href => context.machine_url(instance.id)+"/disks"},
+      :volumes => { :href=>context.machine_url(instance.id)+"/volumes"},
+      :operations => convert_instance_actions(instance, context)
+    }
+    if context.expand? :disks
+      machine_spec[:disks] = CIMI::Model::Disk.find(instance, machine_conf, context, :all)
+    end
+    if context.expand? :volumes
+      machine_spec[:volumes] = CIMI::Model::MachineVolume.find(instance.id, context, :all)
+    end
+    machine_spec[:realm] = instance.realm_id if instance.realm_id
+    machine_spec[:machine_image] = { :href => context.machine_image_url(instance.image_id)
} if instance.image_id
+    self.new(context, :values => machine_spec)
+  end
+
+  # FIXME: This will convert 'RUNNING' state to 'STARTED'
+  # which is defined in CIMI (p65)
+  #
+  def self.convert_instance_state(state)
+    case state
+      when "RUNNING" then "STARTED"
+      when "PENDING" then "CREATING" #aruba only exception... could be "STARTING" here
+      else state
+    end
+  end
+
+  def self.convert_instance_cpu(profile, context)
+    cpu_override = profile.overrides.find { |p, v| p == :cpu }
+    if cpu_override.nil?
+      CIMI::Model::MachineConfiguration.find(profile.id, context).cpu
+    else
+      cpu_override[1]
+    end
+  end
+
+  def self.convert_instance_memory(profile, context)
+    machine_conf = CIMI::Model::MachineConfiguration.find(profile.name, context)
+    memory_override = profile.overrides.find { |p, v| p == :memory }
+    memory_override.nil? ? machine_conf.memory.to_i : context.to_kibibyte(memory_override[1].to_i,"MB")
+  end
+
+  def self.convert_instance_addresses(instance)
+    (instance.public_addresses + instance.private_addresses).collect do |address|
+      {
+        :hostname => address.is_hostname? ? address : nil,
+        :mac_address => address.is_mac? ? address : nil,
+        :state => 'Active',
+        :protocol => 'IPv4',
+        :address => address.is_ipv4? ? address : nil,
+        :allocation => 'Static'
+      }
+    end
+  end
+
+  def self.convert_instance_actions(instance, context)
+    actions = instance.actions.collect do |action|
+      action = :restart if action == :reboot
+      name = action
+      name = :delete if action == :destroy # In CIMI destroy operation become delete
+      { :href => context.send(:"#{action}_machine_url", instance.id), :rel => "http://schemas.dmtf.org/cimi/1/action/#{name}"
}
+    end
+    actions <<  { :href => context.send(:"machine_images_url"), :rel => "http://schemas.dmtf.org/cimi/1/action/capture"
} if instance.can_create_image?
+    actions
+  end
+
+  def self.convert_storage_volumes(instance, context)
+    instance.storage_volumes ||= [] #deal with nilpointers
+    instance.storage_volumes.map{|vol| {:href=>context.volume_url(vol.keys.first),
+                                       :initial_location=>vol.values.first} }
+  end
+
+end
-- 
1.8.1.2


Mime
View raw message