diff --git a/install.sh b/install.sh index 2e42c13b10..e123cfd476 100755 --- a/install.sh +++ b/install.sh @@ -157,6 +157,7 @@ ETC_DIRS="$ETC_LOCATION/im_kvm \ $ETC_LOCATION/tm_dummy \ $ETC_LOCATION/tm_lvm \ $ETC_LOCATION/hm \ + $ETC_LOCATION/auth \ $ETC_LOCATION/ec2query_templates \ $ETC_LOCATION/occi_templates" @@ -240,10 +241,11 @@ INSTALL_ETC_FILES[10]="TM_SSH_ETC_FILES:$ETC_LOCATION/tm_ssh" INSTALL_ETC_FILES[11]="TM_DUMMY_ETC_FILES:$ETC_LOCATION/tm_dummy" INSTALL_ETC_FILES[12]="TM_LVM_ETC_FILES:$ETC_LOCATION/tm_lvm" INSTALL_ETC_FILES[13]="HM_ETC_FILES:$ETC_LOCATION/hm" -INSTALL_ETC_FILES[14]="ECO_ETC_FILES:$ETC_LOCATION" -INSTALL_ETC_FILES[15]="ECO_ETC_TEMPLATE_FILES:$ETC_LOCATION/ec2query_templates" -INSTALL_ETC_FILES[16]="OCCI_ETC_FILES:$ETC_LOCATION" -INSTALL_ETC_FILES[17]="OCCI_ETC_TEMPLATE_FILES:$ETC_LOCATION/occi_templates" +INSTALL_ETC_FILES[14]="AUTH_ETC_FILES:$ETC_LOCATION/auth" +INSTALL_ETC_FILES[15]="ECO_ETC_FILES:$ETC_LOCATION" +INSTALL_ETC_FILES[16]="ECO_ETC_TEMPLATE_FILES:$ETC_LOCATION/ec2query_templates" +INSTALL_ETC_FILES[17]="OCCI_ETC_FILES:$ETC_LOCATION" +INSTALL_ETC_FILES[18]="OCCI_ETC_TEMPLATE_FILES:$ETC_LOCATION/occi_templates" #------------------------------------------------------------------------------- # Binary files, to be installed under $BIN_LOCATION @@ -281,7 +283,10 @@ RUBY_LIB_FILES="src/mad/ruby/one_mad.rb \ src/cli/client_utilities.rb \ src/cli/command_parse.rb \ src/oca/ruby/OpenNebula.rb \ - src/tm_mad/TMScript.rb" + src/tm_mad/TMScript.rb \ + src/authm_mad/one_usage.rb \ + src/authm_mad/quota.rb \ + src/authm_mad/simple_auth.rb" RUBY_OPENNEBULA_LIB_FILES="src/oca/ruby/OpenNebula/Host.rb \ src/oca/ruby/OpenNebula/HostPool.rb \ @@ -318,7 +323,9 @@ MADS_LIB_FILES="src/mad/sh/madcommon.sh \ src/tm_mad/one_tm \ src/tm_mad/one_tm.rb \ src/hm_mad/one_hm.rb \ - src/hm_mad/one_hm" + src/hm_mad/one_hm \ + src/authm_mad/one_auth_mad.rb \ + src/authm_mad/one_auth_mad" #------------------------------------------------------------------------------- # Information Manager Probes, to be installed under $LIB_LOCATION/im_probes @@ -436,6 +443,12 @@ TM_LVM_ETC_FILES="src/tm_mad/lvm/tm_lvm.conf \ HM_ETC_FILES="src/hm_mad/hmrc" +#------------------------------------------------------------------------------- +# Hook Manager driver config. files, to be installed under $ETC_LOCATION/hm +#------------------------------------------------------------------------------- + +AUTH_ETC_FILES="src/authm_mad/auth_mad" + #------------------------------------------------------------------------------- # Sample files, to be installed under $SHARE_LOCATION/examples #------------------------------------------------------------------------------- diff --git a/src/authm_mad/auth_mad b/src/authm_mad/auth_mad new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/authm_mad/auth_mad @@ -0,0 +1 @@ + diff --git a/src/authm_mad/one_auth_mad b/src/authm_mad/one_auth_mad new file mode 100755 index 0000000000..58df732596 --- /dev/null +++ b/src/authm_mad/one_auth_mad @@ -0,0 +1,39 @@ +#!/bin/bash + +# -------------------------------------------------------------------------- # +# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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. # +#--------------------------------------------------------------------------- # + +if [ -z "${ONE_LOCATION}" ]; then + DRIVERRC=/etc/one/auth/authrc + MADCOMMON=/usr/lib/one/mads/madcommon.sh + VAR_LOCATION=/var/lib/one +else + DRIVERRC=$ONE_LOCATION/etc/auth/authrc + MADCOMMON=$ONE_LOCATION/lib/mads/madcommon.sh + VAR_LOCATION=$ONE_LOCATION/var +fi + +. $MADCOMMON + +# Export the vmm_mad specific rc +export_rc_vars $DRIVERRC + +# Go to ONE_LOCATION +cd $VAR_LOCATION + +# Execute the actual MAD +execute_mad $* + diff --git a/src/authm_mad/one_auth_mad.rb b/src/authm_mad/one_auth_mad.rb new file mode 100755 index 0000000000..4da410a75a --- /dev/null +++ b/src/authm_mad/one_auth_mad.rb @@ -0,0 +1,65 @@ +#!/usr/bin/env ruby + +# -------------------------------------------------------------------------- # +# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" + ETC_LOCATION="/etc/one/" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" + ETC_LOCATION=ONE_LOCATION+"/etc/" +end + +$: << RUBY_LIB_LOCATION + +require 'pp' + +require 'OpenNebulaDriver' +require 'simple_auth' + +class AuthorizationManager < OpenNebulaDriver + def initialize + super(1, false) + + @authenticate=SimpleAuth.new + + register_action(:AUTHENTICATE, method('action_authenticate')) + register_action(:AUTHORIZE, method('action_authorize')) + end + + def action_authenticate(request_id, user_id, user, password, token) + auth=@authenticate.auth(user_id, user, password, token) + if auth==true + send_message('AUTHENTICATE', RESULT[:success], + request_id, 'Successfully authenticated') + else + send_message('AUTHENTICATE', RESULT[:failure], + request_id, auth) + end + end + + def action_authorize(request_id, user_id, *tokens) + send_message('AUTHORIZE', RESULT[:success], request_id, 'success') + end +end + + +am=AuthorizationManager.new +am.start_driver + diff --git a/src/authm_mad/one_usage.rb b/src/authm_mad/one_usage.rb new file mode 100644 index 0000000000..49c8ee9e28 --- /dev/null +++ b/src/authm_mad/one_usage.rb @@ -0,0 +1,86 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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 'OpenNebula' + +# This class holds usage information for a virtual machine or +# total usage for a user. Variables inside are cpu and memory +# consumption +class VmUsage + attr_accessor :cpu, :memory + def initialize(cpu, memory) + @cpu=cpu + @memory=memory + end +end + +# This class retrieves and caches vms and its consuption grouped +# by users. 'update_user' method should be called to fill data for +# a user before any calculation is made +class OneUsage + # 'client' is an OpenNebula::Client object used to connect + # to OpenNebula daemon. Ideally it should connect as user 0 + def initialize(client) + @client=client + @users=Hash.new + end + + # Gets information about VMs defined for a user. It caches new + # VMs and takes out from the cache deleted VMs + def update_user(user) + @users[user]=Hash.new if !@users[user] + + vmpool=OpenNebula::VirtualMachinePool.new(@client, user) + vmpool.info + + one_ids=vmpool.map {|vm| vm.id } + vms=@users[user] + user_ids=vms.keys + + deleted_vms=user_ids-one_ids + added_vms=one_ids-user_ids + + deleted_vms.each {|vmid| vms.delete(vmid) } + added_vms.each do |vmid| + vm=OpenNebula::VirtualMachine.new( + OpenNebula::VirtualMachine.build_xml(vmid), @client) + vm.info + hash=vm.to_hash['VM']['TEMPLATE'] + usage=VmUsage.new(hash['CPU'].to_f, hash['MEMORY'].to_i) + vms[vmid.to_i]=usage + end + end + + # Returns the cache of defined VMs for a user. It is a hash with + # VM id as key and VmUsage as value + def vms(user) + vms=@users[user] + @users[user]=vms=Hash.new if !vms + vms + end + + # Returns total consumption by a user into a VmUsage object + def total(user) + usage=VmUsage.new(0.0, 0) + + @users[user].each do |id, vm| + usage.cpu+=vm.cpu + usage.memory+=vm.memory + end + + usage + end +end diff --git a/src/authm_mad/quota.rb b/src/authm_mad/quota.rb new file mode 100644 index 0000000000..36a0a588d3 --- /dev/null +++ b/src/authm_mad/quota.rb @@ -0,0 +1,84 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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. # +#--------------------------------------------------------------------------- # + +# Quota functionality for auth driver. Stores in database limits for each +# user and using OneUsage is able to retrieve resource usage from +# OpenNebula daemon and check if it is below limits +class Quota + TABLE_NAME=:quotas + + # 'db' is a Sequel database where to store user limits and client + # is OpenNebula::Client used to connect to OpenNebula daemon + def initialize(db, client) + @db=db + @client=client + + @usage=OneUsage.new(@client) + + create_table + @table=@db[TABLE_NAME] + end + + # Creates database quota table if it does not exist + def create_table + @db.create_table?(TABLE_NAME) do + primary_key :id + Integer :uid + Float :cpu + Integer :memory + Integer :num_vms + index :uid + end + end + + # Adds new user limits + def add(uid, cpu, memory, num_vms) + @table.insert( + :uid => uid, + :cpu => cpu, + :memory => memory, + :num_vms => num_vms + ) + end + + # Gets user limits + def get(uid) + @table.filter(:uid => uid).first + end + + # Checks if the user is below resource limits. If new_vm is defined + # checks if its requirements fit in limits + def check(user, new_vm=nil) + usage=@usage.total(user) + user_quota=get(user) + if new_vm + usage.cpu+=new_vm.cpu + usage.memory+=new_vm.memory + end + usage.cpu<=user_quota[:cpu] && usage.memory<=user_quota[:memory] + end + + # Updates user resource consuption + def update(user) + @usage.update_user(user) + end + + # Get cache for the user + def get_user(user) + @usage.vms(user) + end +end + diff --git a/src/authm_mad/simple_auth.rb b/src/authm_mad/simple_auth.rb new file mode 100644 index 0000000000..793890c6c2 --- /dev/null +++ b/src/authm_mad/simple_auth.rb @@ -0,0 +1,33 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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 SimpleAuth + def initialize + end + + def auth(user_id, user, password, token) + STDERR.puts [user_id, user, password, token].inspect + STDERR.flush + t_user, t_password=token.split(':') + #auth=(user==t_user && password==t_password) + auth=(password==token) + auth="Invalid credentials" if auth!=true + auth + end +end + + diff --git a/src/authm_mad/spec/client_mock.rb b/src/authm_mad/spec/client_mock.rb new file mode 100644 index 0000000000..fed78630bd --- /dev/null +++ b/src/authm_mad/spec/client_mock.rb @@ -0,0 +1,55 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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 ClientMock + def initialize(mock_data) + @mock_data=mock_data + end + + def search_method(methods, args) + keys=methods.keys + + if keys.include? args[0] + value=methods[args[0]] + if value.class != Hash + return value + elsif args.length>1 + return search_method(value, args[1..-1]) + else + nil + end + else + nil + end + end + + def call(action, *args) + value=search_method(@mock_data, [action]+args.flatten) + if value + if value.class==Proc + value.call(action, args.flatten) + else + value + end + else + message="Action '#{action}(#{args.join(',')})' not defined" + STDERR.puts message + OpenNebula::Error.new(message) + end + end +end + + diff --git a/src/authm_mad/spec/db_helpers.rb b/src/authm_mad/spec/db_helpers.rb new file mode 100644 index 0000000000..ea3ca3f7a5 --- /dev/null +++ b/src/authm_mad/spec/db_helpers.rb @@ -0,0 +1,25 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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 'rubygems' +require 'sequel' + +def check_column(schema, column) + schema.find {|c| c[0] == column }.should_not == nil +end + + + diff --git a/src/authm_mad/spec/oca_vms.yaml b/src/authm_mad/spec/oca_vms.yaml new file mode 100644 index 0000000000..b69377b77a --- /dev/null +++ b/src/authm_mad/spec/oca_vms.yaml @@ -0,0 +1,551 @@ + +vmpool.info: + 0: " + + + 0 + + + 0 + + + jfontan + + + test + + + 0 + + + 1 + + + 0 + + + 1278433840 + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 0 + + + jfontan + + + test + + + 0 + + + 1 + + + 0 + + + 1278433841 + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + " + 1: " + + + 2 + + + 1 + + + lero + + + test + + + 0 + + + 1 + + + 0 + + + 1278433868 + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 3 + + + 1 + + + lero + + + test + + + 0 + + + 1 + + + 0 + + + 1278433869 + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + " +vm.info: + 0: " + + 0 + + + 0 + + + test + + + 0 + + + 1 + + + 0 + + + 1278433840 + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + " + 1: " + + 1 + + + 0 + + + test + + + 0 + + + 1 + + + 0 + + + 1278433841 + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + " + 2: " + + 2 + + + 1 + + + test + + + 0 + + + 1 + + + 0 + + + 1278433868 + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + " + 3: " + + 3 + + + 1 + + + test + + + 0 + + + 1 + + + 0 + + + 1278433869 + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + " \ No newline at end of file diff --git a/src/authm_mad/spec/one_usage_spec.rb b/src/authm_mad/spec/one_usage_spec.rb new file mode 100644 index 0000000000..f20b6d111d --- /dev/null +++ b/src/authm_mad/spec/one_usage_spec.rb @@ -0,0 +1,75 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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 'spec_common' + +require 'client_mock' +require 'one_usage' + +describe "OneUsage" do + before(:all) do + mock_data=YAML::load(File.read('spec/oca_vms.yaml')) + client=ClientMock.new(mock_data) + @one_usage=OneUsage.new(client) + end + + it "should get information about vm users" do + @one_usage.update_user(0) + vms=@one_usage.vms(0) + + vms.size.should == 2 + vms[0].cpu.should == 0.5 + vms[0].memory.should == 64 + vms[1].cpu.should == 1.0 + vms[1].memory.should == 256 + + usage=@one_usage.total(0) + usage.cpu.should == 1.5 + usage.memory.should == 64+256 + + @one_usage.update_user(1) + vms=@one_usage.vms(1) + + vms.size.should == 2 + vms[2].cpu.should == 1.5 + vms[2].memory.should == 512 + vms[3].cpu.should == 2.0 + vms[3].memory.should == 1024 + + usage=@one_usage.total(1) + usage.cpu.should == 3.5 + usage.memory.should == 512+1024 + end + + it "should update information" do + vms=@one_usage.vms(1) + vms[4]=VmUsage.new(4.0, 2048) + + usage=@one_usage.total(1) + usage.cpu.should == 1.5 + 2.0 + 4.0 + usage.memory.should == 512+1024+2048 + + vms.delete(3) + usage=@one_usage.total(1) + usage.cpu.should == 1.5 + 4.0 + usage.memory.should == 512+2048 + + @one_usage.update_user(1) + usage=@one_usage.total(1) + usage.cpu.should == 1.5 + 2.0 + usage.memory.should == 512+1024 + end +end diff --git a/src/authm_mad/spec/quota_spec.rb b/src/authm_mad/spec/quota_spec.rb new file mode 100644 index 0000000000..0e8e352f64 --- /dev/null +++ b/src/authm_mad/spec/quota_spec.rb @@ -0,0 +1,82 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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 'spec_common' + +require 'quota' + +def check_quota(uid, cpu, memory, num_vms) + quota=@quota.get(uid) + quota.should_not == nil + quota[:cpu].should == cpu + quota[:memory].should == memory + quota[:num_vms].should == num_vms +end + +describe 'Quota' do + before(:all) do + @db=Sequel.sqlite + mock_data=YAML::load(File.read('spec/oca_vms.yaml')) + client=ClientMock.new(mock_data) + @quota=Quota.new(@db, client) + end + + it 'should create table' do + @db.table_exists?(:quotas).should == true + + schema=@db.schema(:quotas) + check_column(schema, :uid) + check_column(schema, :cpu) + check_column(schema, :memory) + check_column(schema, :num_vms) + end + + it 'should let add and retrieve quotas' do + @quota.add(0, 10.0, 1024, 10) + @quota.add(1, 20.0, 2048, 20) + @quota.add(2, 40.0, 4096, 40) + + check_quota(0, 10.0, 1024, 10) + check_quota(1, 20.0, 2048, 20) + check_quota(2, 40.0, 4096, 40) + + @quota.get(3).should == nil + end + + it 'should check for quotas' do + @quota.update(0) + @quota.update(1) + @quota.check(0).should == true + @quota.check(1).should == true + + vms=@quota.get_user(0) + vms[5]=VmUsage.new(40.0, 8192) + + vms=@quota.get_user(1) + vms[6]=VmUsage.new(40.0, 8192) + + @quota.check(0).should == false + @quota.check(1).should == false + + @quota.update(0) + @quota.update(1) + @quota.check(0).should == true + @quota.check(1).should == true + end + +end + + diff --git a/src/authm_mad/spec/spec_common.rb b/src/authm_mad/spec/spec_common.rb new file mode 100644 index 0000000000..aad9fa1fc1 --- /dev/null +++ b/src/authm_mad/spec/spec_common.rb @@ -0,0 +1,25 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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 'pp' + +require 'db_helpers' + +$: << '../oca/ruby' + + + +