The first step in providing GemStone/S 64 Bit as a Cloud Foundry service is having it installed with Cloud Foundry. The new “experimental” vcap_dev_setup script uses Chef to install various components and we will install GemStone/S this way in our private cloud (created here). These steps will all take place in a shell on the server.

Edit ~/cloudfoundry/vcap/dev_setup/lib/job_manager.rb to add “gemstone” to the list of services in line 19.

SERVICES = ["gemstone", "redis", "mysql", "mongodb", "neo4j"]

Create ~/cloudfoundry/vcap/dev_setup/roles/gemstone_gateway.json with the following:

{
  "name": "gemstone_gateway",
  "override_attributes": {},
  "json_class": "Chef::Role",
  "description": "GemStone/S services gateway",
  "chef_type": "role",
  "run_list" : ["recipe[deployment]",
  "recipe[essentials]",
  "recipe[ruby]",
  "recipe[gemstone::gateway]"]
}

Create ~/cloudfoundry/vcap/dev_setup/roles/gemstone_node.json with the following:

{
  "name": "gemstone",
  "override_attributes": {},
  "json_class": "Chef::Role",
  "description": "GemStone/S database for apps",
  "chef_type": "role",
  "run_list" : ["recipe[deployment]",
  "recipe[essentials]",
  "recipe[ruby]",
  "recipe[gemstone]",
  "recipe[gemstone::node]"]
}

We will create the following directories and files:

  •  ~/cloudfoundry/vcap/dev_setup/cookbooks/gemstone/
    • attributes/
      • default.rb
    • recipes/
      • default.rb
      • gateway.rb
      • node.rb
    • templates/
      • default/
        • datacurator.tpz.erb
        • gemstone_gateway.yml.erb
        • gemstone_node.yml.erb
        • gs64stone.conf.erb
        • gslist.erb
        • reset_passwords.tpz.erb
        • startstone.erb
        • stopstone.erb
        • topaz.conf.erb
        • topaz_dc.erb
        • topaz.erb
        • .topazini.erb

Create the Chef cookbook directories:

mkdir ~/cloudfoundry/vcap/dev_setup/cookbooks/gemstone/
cd ~/cloudfoundry/vcap/dev_setup/cookbooks/gemstone/
mkdir attributes recipes templates templates/default

Create attributes/default.rb with the following (with changes to the last line to give a good password):

default[:gemstone][:version] = "3.0.1"
default[:gemstone][:gs64ldi] = "50377"
default[:gemstone][:path] = File.join(node[:deployment][:home],
  "deploy", "gemstone")
default[:gemstone][:service_dir] = "/var/vcap/services/gemstone"
# the following provide a max of 512 MB (okay for a 1 GB machine)
default[:gemstone][:shmmax] = "536870912"
default[:gemstone][:shmall] = "131072"
default[:gemstone_node][:available_memory] = "4096"
default[:gemstone_node][:index] = "0"
default[:gemstone_node][:max_memory] = "128"
default[:gemstone_node][:token] = "changegemstonetoken"
default[:gemstone_node][:spc_size_kb] = "131072"
default[:gemstone_node][:tempobj_cache_size] = "131072"
default[:gemstone_node][:stonename] = "gs64stone"
# generate a good replacement for the following
#  (e.g., http://www.random.org/passwords/)
default[:gemstone_node][:password] = "swordfish"

Create recipes/default.rb with the following:

#
# Cookbook Name:: gemstone
# Recipe:: default
#
# Copyright 2012, VMware
#
#
case node['platform']
when "ubuntu"
  gs64ldi = node[:gemstone][:gs64ldi]
  bash "Add gs64ldi to /etc/services" do
    user "root"
    code <<-EOH
      echo "gs64ldi #{gs64ldi}/tcp # GemStone/S" >> /etc/services
    EOH
    not_if do 
      string = `grep -w gs64ldi /etc/services | xargs echo | \
        cut -d" " -f2 | cut -d"/" -f1`.strip
      !string.empty? && Integer(string) == Integer(gs64ldi)
    end
  end
#
  # Shared Memory
  shmmax = node[:gemstone][:shmmax]
  shmall = node[:gemstone][:shmall]
  bash "Set kernel parameters for shared memory for GemStone/S 64 Bit" do
    user "root"
    code <<-EOH
      sysctl -w kernel.shmmax=#{shmmax}
      sysctl -w kernel.shamll=#{shmall}
      echo "kernel.shmmax = #{shmmax}" >> /etc/sysctl.conf
      echo "kernel.shmall = #{shmall}" >> /etc/sysctl.conf
    EOH
    not_if do 
      Integer(shmmax) <= Integer(`sysctl kernel.shmmax | cut -d" " -f3`)
    end
  end
#
  # Download product tree
  dir_name = "GemStone64Bit#{node[:gemstone][:version]}-x86_64.Linux"
  file_name = dir_name + ".zip"
  bash "Download GemStone/S 64 Bit" do
    cwd File.join("", "tmp")
    user node[:deployment][:user]
    code <<-EOH
      ftp -inv ftp.gemstone.com <<-FTP_END
        user anonymous swordfish
        cd /pub/GemStone64/#{node[:gemstone][:version]}/
        binary
        get #{file_name}
        bye
      FTP_END
    EOH
    not_if do
      ::File.exists?(File.join("", "tmp", file_name))
    end
  end
#
  bash "Install GemStone/S 64 Bit" do
    user node[:deployment][:user]
    group node[:deployment][:user]
    cwd File.join("", "tmp")
    code <<-EOH
      unzip -q #{file_name} -d #{node[:gemstone][:path]}
      ln -s #{node[:gemstone][:path] + "/" + dir_name} \
        #{node[:gemstone][:path]}/product
    EOH
    not_if do
      ::File.exists?(File.join(node[:gemstone][:path], dir_name , \
        "bin", "startstone"))
    end
  end
#
  ["", "bin", "data", "etc", "locks", "log"].each do | dir |
    directory File.join(node[:gemstone][:service_dir], dir) do
      owner node[:deployment][:user]
      group node[:deployment][:user]
      mode "0755"
    end
  end
#
  %w(gs64stone topaz).each do | name |
    template File.join(node[:gemstone][:service_dir], "etc","#{name}.conf") do
      source "#{name}.conf.erb"
      mode 0666
      owner node[:deployment][:user]
      group node[:deployment][:user]
    end
  end
#
  %w(startstone stopstone gslist).each do | name |
    template File.join(node[:gemstone][:service_dir], "bin", name) do
      source "#{name}.erb"
      mode 0500
      owner node[:deployment][:user]
      group node[:deployment][:user]
    end
  end
#
  %w(topaz).each do | name |
    template File.join(node[:gemstone][:service_dir], "bin", name) do
      source "#{name}.erb"
      mode 0555
      owner node[:deployment][:user]
      group node[:deployment][:user]
    end
  end
#
  template File.join(node[:gemstone][:service_dir], "bin", ".topazini") do
    source ".topazini.erb"
    mode 0444
    owner node[:deployment][:user]
    group node[:deployment][:user]
  end
#
  template File.join(node[:gemstone][:service_dir], "bin", "topaz_dc") do
    source "topaz_dc.erb"
    mode 0500
    owner node[:deployment][:user]
    group node[:deployment][:user]
  end
#
  %w(datacurator.tpz reset_passwords.tpz).each do | name |
    template File.join(node[:gemstone][:service_dir], "bin", name) do
      source "#{name}.erb"
      mode 0400
      owner node[:deployment][:user]
      group node[:deployment][:user]
    end
  end
#
  bash "Copy keyfile and extent" do
    user node[:deployment][:user]
    group node[:deployment][:user]
    cwd node[:gemstone][:service_dir]
    code <<-EOH
      cp #{node[:gemstone][:path]}/product/seaside/etc/gemstone.key etc
      cp #{node[:gemstone][:path]}/product/bin/extent0.dbf data
      chmod 644 data/extent0.dbf
      cp #{node[:gemstone][:path]}/product/data/system.conf etc
    EOH
    not_if do
      ::File.exists?(File.join(node[:gemstone][:service_dir], "data", \
        "extent0.dbf"))
    end
  end
#
  bash "Reset passwords" do
    user node[:deployment][:user]
    group node[:deployment][:user]
    cwd File.join(node[:gemstone][:service_dir], "bin")
    code <<-EOH
      ./startstone
      ./topaz < reset_passwords.tpz
      ./stopstone
    EOH
    not_if do
      ::File.exists?(File.join(node[:gemstone][:service_dir], "data", \
        "tranlog1.dbf"))
    end
  end
#
else
  Chef::Log.error("Installation of GemStone/S 64 Bit not supported 
     on this platform.")
end

Create recipes/gateway.rb with the following:

#
# Cookbook Name:: gateway
# Recipe:: default
#
# Copyright 2012, VMware
#
cloudfoundry_service "gemstone" do
  components ["gemstone_gateway"]
end

Create recipes/node.rb with the following:

#
# Cookbook Name:: node
# Recipe:: default
#
# Copyright 2012, VMware
#
cloudfoundry_service "gemstone" do
  components ["gemstone_node"]
end

Create templates/default/datacurator.tpz.erb to login into Topaz as DataCurator:

level 1
iferr 1 stk
iferr 2 stack
iferr 3 exit
set user DataCurator pass <%= node[:gemstone_node][:password] %>
set gems <%= node[:gemstone_node][:stonename] %>
login

Create templates/default/gemstone_gateway.yml.erb to configure the gateway:

---
cloud_controller_uri: <%= node[:cloud_controller][:service_api_uri] %>
service:
  name: gemstone
  version: "3.0"
  description: 'GemStone/S 64 Bit database service'
  plans: ['free']
  tags: ['gemstone', 'object']
host: localhost
index: <%= node[:gemstone_node][:index] %>
token: <%= node[:gemstone_node][:token] %>
mbus: nats://<%= \
     node[:nats_server][:user] %>:<%= \
     node[:nats_server][:password] %>@<%= \
     node[:nats_server][:host] %>:<%= \
     node[:nats_server][:port] %>/
pid: /var/vcap/sys/run/gemstone_service.pid
node_timeout: 2
logging:
  level: debug

Create templates/default/gemstone_node.yml.erb to configure the service node:

---
local_db: sqlite3:/var/vcap/services/gemstone/gemstone_node.db
mbus: nats://<%= \
     node[:nats_server][:user] %>:<%= \
     node[:nats_server][:password] %>@<%= \
     node[:nats_server][:host] %>:<%= \
     node[:nats_server][:port] %>/
index: <%= node[:gemstone_node][:index] %>
base_dir: /var/vcap/services/gemstone/
pid: /var/vcap/sys/run/gemstone_node.pid
available_memory: <%= node[:gemstone_node][:available_memory] %>
node_id: <%= "gemstone_node_#{node[:gemstone_node][:index]}" %>
max_memory: <%= node[:gemstone_node][:max_memory] %>
migration_nfs: /mnt/migration
logging:
  level: debug

Create templates/default/gs64stone.conf.erb for the stone configuration file:

KEYFILE = <%= node[:gemstone][:service_dir] %>/etc/gemstone.key;
DBF_EXTENT_NAMES = <%= node[:gemstone][:service_dir] %>/data/extent0.dbf;
STN_TRAN_LOG_DIRECTORIES = <%= node[:gemstone][:service_dir] %>/data/, 
     <%= node[:gemstone][:service_dir] %>/data/;
STN_TRAN_LOG_SIZES = 100, 100;
SHR_PAGE_CACHE_SIZE_KB = <%= node[:gemstone_node][:spc_size_kb] %>;
STN_TRAN_FULL_LOGGING = TRUE;

Create templates/default/gslist.erb to list the GemStone/S processes:

#!/bin/bash
export GEMSTONE=<%= node[:gemstone][:path] %>/product
export PATH=$GEMSTONE/bin:$PATH
export GEMSTONE_GLOBAL_DIR=<%= node[:gemstone][:service_dir] %>
gslist -cvl

Create templates/default/reset_passwords.tpz.erb change the passwords for the build-in users as part of the install process:

set gems <%= node[:gemstone_node][:stonename] %>
set user SystemUser pass swordfish
login
run
(AllUsers userWithId: 'SystemUser') 
     password: '<%= node[:gemstone_node][:password] %>'.
(AllUsers userWithId: 'DataCurator') 
     password: '<%= node[:gemstone_node][:password] %>'.
(AllUsers userWithId: 'GcUser') 
     password: '<%= node[:gemstone_node][:password] %>'.
(AllUsers userWithId: 'SymbolUser') 
     password: '<%= node[:gemstone_node][:password] %>'.
(AllUsers userWithId: 'Nameless') 
     password: '<%= node[:gemstone_node][:password] %>'.
System commitTransaction
%
logout
exit

Create templates/default/startstone.erb to start the GemStone/S database service:

#!/bin/bash
export GEMSTONE=<%= node[:gemstone][:path] %>/product
export PATH=$GEMSTONE/bin:$PATH
export GEMSTONE_GLOBAL_DIR=<%= node[:gemstone][:service_dir] %>
startstone -l <%= node[:gemstone][:service_dir] %>/log/gs64stone.log \
  -e <%= node[:gemstone][:service_dir] %>/etc/gs64stone.conf \
  -z <%= node[:gemstone][:service_dir] %>/etc/system.conf

Create templates/default/stopstone.erb to stop the GemStone/S database service:

#!/bin/bash
export GEMSTONE=<%= node[:gemstone][:path] %>/product
export PATH=$GEMSTONE/bin:$PATH
export GEMSTONE_GLOBAL_DIR=<%= node[:gemstone][:service_dir] %>
stopstone -i <%= node[:gemstone_node][:stonename] %> \
     DataCurator <%= node[:gemstone_node][:password] %>

Create templates/default/topaz.conf.erb as a configuration file for the Topaz processes:

GEM_TEMPOBJ_CACHE_SIZE = <%= node[:gemstone_node][:tempobj_cache_size] %>;

Create templates/default/topaz_dc.erb to start a Topaz process as DataCurator:

#!/bin/bash
export GEMSTONE=<%= node[:gemstone][:path] %>/product
export PATH=$GEMSTONE/bin:$PATH
export GEMSTONE_GLOBAL_DIR=<%= node[:gemstone][:service_dir] %>
topaz -l \
  -I <%= node[:gemstone][:service_dir] %>/bin/datacurator.tpz \
  -e <%= node[:gemstone][:service_dir] %>/etc/topaz.conf \
  -z <%= node[:gemstone][:service_dir] %>/etc/system.conf

Create templates/default/topaz.erb to start a non-DataCurator Topaz process:

#!/bin/bash
export GEMSTONE=<%= node[:gemstone][:path] %>/product
export PATH=$GEMSTONE/bin:$PATH
export GEMSTONE_GLOBAL_DIR=<%= node[:gemstone][:service_dir] %>
topaz -l \
  -I <%= node[:gemstone][:service_dir] %>/bin/.topazini \
  -e <%= node[:gemstone][:service_dir] %>/etc/topaz.conf \
  -z <%= node[:gemstone][:service_dir] %>/etc/system.conf

Create templates/default/.topazini.erb to provide a general Topaz initialization:

level 1
iferr 1 stk
iferr 2 stack
iferr 3 exit
set gems <%= node[:gemstone_node][:stonename] %>

When these files have been created, you can rerun the deployment script:

~/cloudfoundry/vcap/dev_setup/bin/vcap_dev_setup -d ~/cloudfoundry

This will download and install the GemStone/S product at ~/cloudfoundry/.deployments/devbox/deploy/gemstone/product. It will also create a place to hold the database at:

  • /var/vcap/services/gemstone/
    • bin/
      • datacurator.tpz
      • gslist
      • reset_passwords.tpz
      • startstone
      • stopstone
      • topaz
      • topaz_dc
      • .topazini
    • data/
      • extent0.dbf
    • etc/
      • gemstone.key
      • gs64stone.conf
      • system.conf
      • topaz.conf
    • locks/
    • log/

From the bin directory you can start the database and login as DataCurator and execute various Topaz commands.

Later we will look at presenting GemStone/S 64 Bit as a service in Cloud Foundry.