wakame-vdc-dcmgr 10.12.0 → 11.06.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/LICENSE +164 -201
  2. data/Rakefile +6 -11
  3. data/bin/collector +10 -24
  4. data/config/dcmgr.conf.example +18 -6
  5. data/config/initializers/isono.rb +7 -23
  6. data/config/initializers/sequel.rb +11 -2
  7. data/lib/dcmgr.rb +70 -11
  8. data/lib/dcmgr/cli/base.rb +74 -0
  9. data/lib/dcmgr/cli/errors.rb +59 -0
  10. data/lib/dcmgr/cli/group.rb +101 -0
  11. data/lib/dcmgr/cli/host.rb +101 -0
  12. data/lib/dcmgr/cli/image.rb +108 -0
  13. data/lib/dcmgr/cli/keypair.rb +72 -0
  14. data/lib/dcmgr/cli/network.rb +198 -0
  15. data/lib/dcmgr/cli/quota.rb +28 -0
  16. data/lib/dcmgr/cli/spec.rb +82 -0
  17. data/lib/dcmgr/cli/storage.rb +88 -0
  18. data/lib/dcmgr/cli/tag.rb +81 -0
  19. data/lib/dcmgr/cli/vlan.rb +53 -0
  20. data/lib/dcmgr/drivers/hypervisor.rb +33 -0
  21. data/lib/dcmgr/drivers/iijgio_storage.rb +37 -0
  22. data/lib/dcmgr/drivers/kvm.rb +118 -0
  23. data/lib/dcmgr/drivers/lxc.rb +167 -0
  24. data/lib/dcmgr/drivers/s3_storage.rb +39 -0
  25. data/lib/dcmgr/drivers/snapshot_storage.rb +51 -0
  26. data/lib/dcmgr/endpoints/core_api.rb +188 -324
  27. data/lib/dcmgr/endpoints/core_api_mock.rb +52 -3
  28. data/lib/dcmgr/endpoints/errors.rb +73 -32
  29. data/lib/dcmgr/endpoints/metadata.rb +163 -16
  30. data/lib/dcmgr/helpers/cli_helper.rb +1 -1
  31. data/lib/dcmgr/helpers/nic_helper.rb +35 -0
  32. data/lib/dcmgr/logger.rb +5 -1
  33. data/lib/dcmgr/messaging_client.rb +117 -0
  34. data/lib/dcmgr/models/account.rb +27 -3
  35. data/lib/dcmgr/models/base_new.rb +21 -7
  36. data/lib/dcmgr/models/host_pool.rb +27 -7
  37. data/lib/dcmgr/models/image.rb +31 -3
  38. data/lib/dcmgr/models/instance.rb +72 -23
  39. data/lib/dcmgr/models/instance_nic.rb +12 -2
  40. data/lib/dcmgr/models/instance_spec.rb +16 -0
  41. data/lib/dcmgr/models/ip_lease.rb +37 -1
  42. data/lib/dcmgr/models/netfilter_group.rb +7 -7
  43. data/lib/dcmgr/models/network.rb +42 -3
  44. data/lib/dcmgr/models/quota.rb +25 -0
  45. data/lib/dcmgr/models/request_log.rb +26 -11
  46. data/lib/dcmgr/models/ssh_key_pair.rb +14 -1
  47. data/lib/dcmgr/models/storage_pool.rb +19 -72
  48. data/lib/dcmgr/models/tag.rb +5 -0
  49. data/lib/dcmgr/models/vlan_lease.rb +8 -0
  50. data/lib/dcmgr/models/volume.rb +26 -8
  51. data/lib/dcmgr/models/volume_snapshot.rb +37 -0
  52. data/lib/dcmgr/node_modules/hva_collector.rb +56 -36
  53. data/lib/dcmgr/node_modules/instance_ha.rb +1 -1
  54. data/lib/dcmgr/node_modules/instance_monitor.rb +70 -0
  55. data/lib/dcmgr/node_modules/service_netfilter.rb +914 -0
  56. data/lib/dcmgr/node_modules/sta_collector.rb +7 -30
  57. data/lib/dcmgr/rack/request_logger.rb +60 -0
  58. data/lib/dcmgr/rack/run_initializer.rb +42 -0
  59. data/lib/dcmgr/rpc/hva_handler.rb +388 -0
  60. data/lib/dcmgr/rubygems.rb +7 -0
  61. data/lib/dcmgr/storage_service.rb +98 -0
  62. data/lib/dcmgr/tags.rb +2 -2
  63. data/lib/dcmgr/version.rb +8 -0
  64. data/lib/ext/time.rb +8 -0
  65. data/lib/sinatra/respond_to.rb +3 -0
  66. data/lib/sinatra/sequel_transaction.rb +20 -5
  67. data/web/api/config.ru +9 -13
  68. data/web/metadata/config.ru +10 -13
  69. metadata +162 -120
  70. data/lib/dcmgr/models/physical_host.rb +0 -67
  71. data/lib/dcmgr/web/base.rb +0 -21
data/lib/dcmgr/logger.rb CHANGED
@@ -4,7 +4,11 @@ require 'logger'
4
4
  module Dcmgr
5
5
  module Logger
6
6
 
7
- @logdev = ::Logger::LogDevice.new(STDOUT)
7
+ # for passenger, messages in STDOUT are not appeared in
8
+ # error.log. $> is changed in initializers/logger.rb as per the
9
+ # server environment. so that here also refers $> instead of STDOUT or
10
+ # STDERR constant.
11
+ @logdev = ::Logger::LogDevice.new($>)
8
12
 
9
13
  def self.default_logdev
10
14
  @logdev
@@ -0,0 +1,117 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'isono'
4
+ require 'eventmachine'
5
+
6
+ module Dcmgr
7
+ # @example Sync RPC call with object method.
8
+ # mc = MessagingClient.start
9
+ # puts mc.request('endpoint', 'func1', xxxx, xxxx)
10
+ # puts mc.request('endpoint', 'func2', xxx, xxx)
11
+ #
12
+ # @example Sync RPC call using delegated object
13
+ # mc = MessagingClient.start
14
+ # endpoint = mc.sync_rpc('endpoint')
15
+ # endpoint.func1(xxxx, xxxx)
16
+ # endpoint.func2(xxx, xxx)
17
+ #
18
+ class MessagingClient < Isono::Node
19
+ include Logger
20
+ include Isono
21
+
22
+ def self.start(amqp_uri, manifest=nil, &blk)
23
+ node = self.new(manifest, &blk)
24
+
25
+ if EventMachine.reactor_thread?
26
+ EventMachine.schedule {
27
+ node.connect(amqp_uri)
28
+ }
29
+ else
30
+ q = ::Queue.new
31
+ EventMachine.schedule {
32
+ node.connect(amqp_uri) { |type|
33
+ q << type
34
+ }
35
+ }
36
+ case q.deq
37
+ when :success
38
+ when :error
39
+ raise "Connection failed: #{amqp_uri}"
40
+ end
41
+ end
42
+
43
+ node
44
+ end
45
+
46
+ def stop
47
+ if connected?
48
+ close {
49
+ EventMachine.schedule {
50
+ EventMachine.stop
51
+ }
52
+ }
53
+ end
54
+ end
55
+
56
+ def initialize(m=nil, &blk)
57
+ m ||= Manifest.new(Dir.pwd) {
58
+ node_name 'dcmgr'
59
+ node_instance_id Util.gen_id
60
+
61
+ load_module NodeModules::EventChannel
62
+ load_module NodeModules::RpcChannel
63
+ load_module NodeModules::JobChannel
64
+ }
65
+ m.instance_eval(&blk) if blk
66
+ super(m)
67
+ end
68
+
69
+ class RpcSyncDelegator
70
+ attr_reader :endpoint
71
+
72
+ def initialize(rpc, endpoint, opts={})
73
+ @rpc = rpc
74
+ @endpoint = endpoint
75
+ @opts = {:timeout=>0.0, :oneshot=>false}.merge(opts)
76
+ end
77
+
78
+ private
79
+ def method_missing(m, *args)
80
+ if @opts[:oneshot]
81
+ oneshot_request(m, *args)
82
+ else
83
+ normal_request(m, *args)
84
+ end
85
+ end
86
+
87
+ def oneshot_request(m, *args)
88
+ @rpc.request(@endpoint, m, *args) { |req|
89
+ req.oneshot = true
90
+ }
91
+ end
92
+
93
+ def normal_request(m, *args)
94
+ @rpc.request(@endpoint, m, *args)
95
+ end
96
+ end
97
+
98
+ def sync_rpc(endpoint, opts={})
99
+ rpc = NodeModules::RpcChannel.new(self)
100
+ RpcSyncDelegator.new(rpc, endpoint, opts)
101
+ end
102
+
103
+ def request(endpoint, key, *args, &blk)
104
+ rpc = NodeModules::RpcChannel.new(self)
105
+ rpc.request(endpoint, key, *args, &blk)
106
+ end
107
+
108
+ def submit(job_endpoint, key, *args)
109
+ NodeModules::JobChannel.new(self).submit(job_endpoint, key, *args)
110
+ end
111
+
112
+ def event_publish(evname, opts={})
113
+ NodeModules::EventChannel.new(self).publish(evname, opts)
114
+ end
115
+
116
+ end
117
+ end
@@ -16,6 +16,7 @@ module Dcmgr::Models
16
16
  with_timestamps
17
17
 
18
18
  one_to_many :tags, :dataset=>lambda { Tag.filter(:account_id=>self.canonical_uuid); }
19
+ one_to_one :quota, :class=>Quota, :key=>:account_id
19
20
 
20
21
  # sti plugin has to be loaded at lower position.
21
22
  plugin :subclasses
@@ -30,6 +31,17 @@ module Dcmgr::Models
30
31
  self.enabled == ENABLED
31
32
  end
32
33
 
34
+ def after_create
35
+ self.quota = Quota.create
36
+ super
37
+ end
38
+
39
+ def before_destroy
40
+ self.quota.destroy unless self.quota.nil?
41
+ super
42
+ end
43
+
44
+
33
45
  # STI class variable setter, getter methods.
34
46
  class << self
35
47
  def default_values
@@ -46,12 +58,13 @@ module Dcmgr::Models
46
58
  def uuid(uuid=nil)
47
59
  if uuid.is_a?(String)
48
60
  uuid = uuid.downcase
49
- if uuid !~ /^[a-z0-9]{8}$/
61
+ unless self.check_trimmed_uuid_format(uuid)
50
62
  raise "Invalid syntax of uuid: #{uuid}"
51
63
  end
52
64
  default_values[:uuid] = uuid
53
65
  end
54
- default_values[:uuid] || raise("#{self}.uuid is unset. Set the unique number")
66
+ raise("#{self}.uuid is unset. Set the unique number") unless default_values[:uuid]
67
+ "#{uuid_prefix}-#{default_values[:uuid]}"
55
68
  end
56
69
 
57
70
  def description(description=nil)
@@ -80,8 +93,19 @@ module Dcmgr::Models
80
93
  Account.subclasses.each { |m|
81
94
  Account.create(m.default_values.dup)
82
95
  }
83
- end
84
96
 
97
+ # create shared resource pool tags
98
+ Dcmgr::Tags::HostPool.create(:account_id=>SystemAccount::SharedPoolAccount.uuid,
99
+ :uuid=>'shhost',
100
+ :name=>"default_shared_hosts")
101
+ Dcmgr::Tags::NetworkPool.create(:account_id=>SystemAccount::SharedPoolAccount.uuid,
102
+ :uuid=>'shnet',
103
+ :name=>"default_shared_networks")
104
+ Dcmgr::Tags::StoragePool.create(:account_id=>SystemAccount::SharedPoolAccount.uuid,
105
+ :uuid=>'shstor',
106
+ :name=>"default_shared_storages")
107
+ end
108
+
85
109
  SystemAccount.define_account(:DatacenterAccount) do
86
110
  pk 100
87
111
  uuid '00000000'
@@ -4,9 +4,6 @@ require 'sequel/model'
4
4
 
5
5
 
6
6
  module Dcmgr::Models
7
- class InvalidUUIDError < StandardError; end
8
- class UUIDPrefixDuplication < StandardError; end
9
-
10
7
  # Sequal::Model plugin to inject the Taggable feature to the model
11
8
  # class.
12
9
  #
@@ -187,6 +184,11 @@ module Dcmgr::Models
187
184
  raise InvalidUUIDError, "Invalid uuid or unsupported uuid: #{p_uuid} in #{self}"
188
185
  end
189
186
 
187
+ # Checks the general uuid syntax
188
+ def check_trimmed_uuid_format(uuid)
189
+ uuid.match(/^[a-z0-9 ]*$/) && uuid.length <= 8
190
+ end
191
+
190
192
  # Checks the uuid syntax if it is for the Taggable class.
191
193
  def check_uuid_format(uuid)
192
194
  uuid =~ /^#{self.uuid_prefix}-/
@@ -397,11 +399,18 @@ module Dcmgr::Models
397
399
  class BaseNew < Sequel::Model
398
400
 
399
401
  LOCK_TABLES_KEY='__locked_tables'
402
+
403
+ def self.default_row_lock_mode=(mode)
404
+ raise ArgumentError unless [nil, :share, :update].member?(mode)
405
+ @default_row_lock_mode = mode
406
+ end
400
407
 
401
- def self.lock!
408
+ def self.lock!(mode=nil)
409
+ raise ArgumentError unless [nil, :share, :update].member?(mode)
410
+ mode ||= @default_row_lock_mode
402
411
  locktbls = Thread.current[LOCK_TABLES_KEY]
403
412
  if locktbls
404
- locktbls[self.db.uri.to_s + @dataset.first_source_alias.to_s]=1
413
+ locktbls[self.db.uri.to_s + @dataset.first_source_alias.to_s]=mode
405
414
  end
406
415
  end
407
416
 
@@ -414,8 +423,9 @@ module Dcmgr::Models
414
423
 
415
424
  def self.dataset
416
425
  locktbls = Thread.current[LOCK_TABLES_KEY]
417
- if locktbls && locktbls[self.db.uri.to_s + @dataset.first_source_alias.to_s]
418
- @dataset.opts = @dataset.opts.merge({:lock=>:update})
426
+ if locktbls && (mode = locktbls[self.db.uri.to_s + @dataset.first_source_alias.to_s])
427
+ # lock mode: :share or :update
428
+ @dataset.opts = @dataset.opts.merge({:lock=>mode})
419
429
  else
420
430
  @dataset.opts = @dataset.opts.merge({:lock=>nil})
421
431
  end
@@ -439,6 +449,10 @@ module Dcmgr::Models
439
449
  s
440
450
  end
441
451
 
452
+ # Returns true if this Model has time stamps
453
+ def with_timestamps?
454
+ self.columns.include?(:created_at) && self.columns.include?(:updated_at)
455
+ end
442
456
 
443
457
  # Callback when the initial data is setup to the database.
444
458
  def self.install_data
@@ -1,18 +1,21 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ require 'isono'
4
+
3
5
  module Dcmgr::Models
4
6
  class HostPool < AccountResource
5
7
  taggable 'hp'
6
8
  with_timestamps
7
9
 
8
- HYPERVISOR_XEN_34=:'xen-3.4'
9
- HYPERVISOR_XEN_40=:'xen-4.0'
10
- HYPERVISOR_KVM=:'kvm'
10
+ HYPERVISOR_XEN_34='xen-3.4'
11
+ HYPERVISOR_XEN_40='xen-4.0'
12
+ HYPERVISOR_KVM='kvm'
11
13
 
12
14
  ARCH_X86=:x86.to_s
13
15
  ARCH_X86_64=:x86_64.to_s
14
16
 
15
17
  SUPPORTED_ARCH=[ARCH_X86, ARCH_X86_64]
18
+ SUPPORTED_HYPERVISOR=[HYPERVISOR_KVM]
16
19
 
17
20
  inheritable_schema do
18
21
  String :node_id, :size=>80, :null=>true
@@ -35,8 +38,13 @@ module Dcmgr::Models
35
38
 
36
39
  def validate
37
40
  super
38
- unless self.node_id =~ /^hva-/
39
- errors.add(:node_id, "hva node has to be associated: #{self.node_id}")
41
+ # for compatibility: hva.xxx or hva-xxxx
42
+ unless self.node_id =~ /^hva[-.]/
43
+ errors.add(:node_id, "is invalid ID: #{self.node_id}")
44
+ end
45
+
46
+ if (h = self.class.filter(:node_id=>self.node_id).first) && h.id != self.id
47
+ errors.add(:node_id, " #{self.node_id} is already been associated to #{h.canonical_uuid} ")
40
48
  end
41
49
 
42
50
  unless SUPPORTED_ARCH.member?(self.arch)
@@ -75,11 +83,18 @@ module Dcmgr::Models
75
83
  i.account_id = account.canonical_uuid
76
84
  i.image = image
77
85
  i.instance_spec = spec
86
+ i.cpu_cores = spec.cpu_cores
87
+ i.memory_size = spec.memory_size
88
+ i.quota_weight = spec.quota_weight
78
89
  i.host_pool = self
79
90
  i.save
80
91
 
81
92
  vnic = i.add_nic(network)
82
93
  IpLease.lease(vnic, network)
94
+
95
+ #Lease the nat ip in case there is an outside network mapped
96
+ nat_network = Network.find(:id => vnic[:nat_network_id])
97
+ IpLease.lease(vnic,nat_network) unless nat_network.nil?
83
98
  i
84
99
  end
85
100
 
@@ -93,10 +108,15 @@ module Dcmgr::Models
93
108
  raise TypeError unless spec.is_a?(InstanceSpec)
94
109
  inst_on_hp = self.instances_dataset.lives.all
95
110
 
96
- (self.offering_cpu_cores > inst_on_hp.inject(0) {|t, i| t += i.spec.cpu_cores } + spec.cpu_cores) &&
97
- (self.offering_memory_size > inst_on_hp.inject(0) {|t, i| t += i.spec.memory_size } + spec.memory_size)
111
+ (self.offering_cpu_cores >= inst_on_hp.inject(0) {|t, i| t += i.cpu_cores } + spec.cpu_cores) &&
112
+ (self.offering_memory_size >= inst_on_hp.inject(0) {|t, i| t += i.memory_size } + spec.memory_size)
98
113
  end
99
114
 
115
+ def to_api_document
116
+ h = to_hash
117
+ h.delete(:node_id)
118
+ h
119
+ end
100
120
 
101
121
  end
102
122
  end
@@ -14,17 +14,19 @@ module Dcmgr::Models
14
14
  Text :source, :null=>false
15
15
  String :arch, :size=>10, :null=>false
16
16
  Text :description
17
+ Boolean :is_public, :null=>false, :default=>false
17
18
  #Fixnum :parent_image_id
19
+ String :format, :null=>false, :default=>'raw'
18
20
 
19
21
  String :state, :size=>20, :null=>false, :default=>:init.to_s
22
+ index :is_public
20
23
  end
21
24
 
22
25
  # serialize plugin must be defined at the bottom of all class
23
26
  # method calls.
24
27
  # Possible source column data:
25
- # vdc volume:
26
- # {:type=>:vdcvol, :account_id=>'a-xxxxx', :snapshot_id=>'snap-xxxxxx'}
27
- # {:type=>:http, :uri=>'http://localhost/xxx/xxx'}
28
+ # {:snapshot_id=>'snap-xxxxxx'}
29
+ # {:uri=>'http://localhost/xxx/xxx'}
28
30
  plugin :serialization
29
31
  serialize_attributes :yaml, :source
30
32
 
@@ -36,11 +38,37 @@ module Dcmgr::Models
36
38
  unless HostPool::SUPPORTED_ARCH.member?(self.arch)
37
39
  errors.add(:arch, "Unsupported arch type: #{self.arch}")
38
40
  end
41
+
42
+ # validate source
43
+ md = self.source
44
+ case self.boot_dev_type
45
+ when BOOT_DEV_LOCAL
46
+ errors.add(:source, "Unknown image URI") if md[:uri].nil? || md[:uri] == ''
47
+ when BOOT_DEV_SAN
48
+ errors.add(:source, "Unknown snapshot ID") if md[:snapshot_id].nil? || md[:snapshot_id] == '' || VolumeSnapshot[md[:snapshot_id]].nil?
49
+ end
39
50
  end
40
51
 
41
52
  def to_hash
42
53
  super.merge({:source=>self.source.dup, :description=>description.to_s})
43
54
  end
55
+
56
+ # note on "lookup_account_id":
57
+ # the source column sometime contains the information which
58
+ # should not be shown to other accounts. so that the method takes
59
+ # an argument who is looking into then filters the data in source
60
+ # column accordingly.
61
+ def to_api_document(lookup_account_id)
62
+ h = to_hash
63
+ if self.account_id == lookup_account_id
64
+ else
65
+ if h[:source][:type] == :http
66
+ # do not show URI for non-owner accounts.
67
+ h[:source][:uri] = nil
68
+ end
69
+ end
70
+ h
71
+ end
44
72
 
45
73
  end
46
74
  end
@@ -17,11 +17,16 @@ module Dcmgr::Models
17
17
  String :state, :size=>20, :null=>false, :default=>:init.to_s
18
18
  String :status, :size=>20, :null=>false, :default=>:init.to_s
19
19
  String :hostname, :null=>false, :size=>32
20
+ # TODO: remove ssh_key_pair_id column
20
21
  String :ssh_key_pair_id
21
22
  Fixnum :ha_enabled, :null=>false, :default=>0
23
+ Float :quota_weight, :null=>false, :default=>0.0
24
+ Fixnum :cpu_cores, :null=>false, :unsigned=>true
25
+ Fixnum :memory_size, :null=>false, :unsigned=>true
22
26
 
23
27
  Text :user_data, :null=>false, :default=>''
24
28
  Text :runtime_config, :null=>false, :default=>''
29
+ Text :ssh_key_data, :null=>true
25
30
 
26
31
  Time :terminated_at
27
32
  index :state
@@ -36,13 +41,22 @@ module Dcmgr::Models
36
41
  one_to_many :volume
37
42
  one_to_many :instance_nic
38
43
  alias :nic :instance_nic
39
- one_to_many :instance_netfilter_groups
40
44
  many_to_many :netfilter_groups, :join_table=>:instance_netfilter_groups
45
+ # TODO: remove ssh_key_pair_id column
41
46
  many_to_one :ssh_key_pair
42
47
 
43
48
  plugin ArchiveChangedColumn, :histories
44
49
 
45
50
  subset(:lives, {:terminated_at => nil})
51
+
52
+ RECENT_TERMED_PERIOD=(60 * 15)
53
+ # lists the instances which alives and died within
54
+ # RECENT_TERMED_PERIOD sec.
55
+ # it was difficult for me to write exprs in virtual row syntax as
56
+ # per subset(). ;-(
57
+ def_dataset_method(:alives_and_recent_termed) {
58
+ filter("terminated_at IS NULL OR terminated_at >= ?", (Time.now.utc - RECENT_TERMED_PERIOD))
59
+ }
46
60
 
47
61
  # serialization plugin must be defined at the bottom of all class
48
62
  # method calls.
@@ -51,6 +65,8 @@ module Dcmgr::Models
51
65
  # {:vnc_port=>11, :telnet_port=>1111}
52
66
  plugin :serialization
53
67
  serialize_attributes :yaml, :runtime_config
68
+ # equal to SshKeyPair#to_hash
69
+ serialize_attributes :yaml, :ssh_key_data
54
70
 
55
71
  module ValidationMethods
56
72
  def self.hostname_uniqueness(account_id, hostname)
@@ -125,14 +141,20 @@ module Dcmgr::Models
125
141
  end
126
142
  @update_hostname = false
127
143
  end
128
-
144
+
145
+ # sum() returns nil if there is no instance rows.
146
+ lives_weight = self.class.filter(:account_id=>self.account_id).lives.sum(:quota_weight) || 0.0
147
+ unless lives_weight <= self.account.quota.instance_total_weight
148
+ raise "Out of quota limit: #{self.account_id}'s current weight capacity: #{lives_weight} (<= #{self.account.quota.instance_total_weight})"
149
+ end
150
+
129
151
  super
130
152
  end
131
153
 
132
154
  def before_destroy
133
155
  HostnameLease.filter(:account_id=>self.account_id, :hostname=>self.hostname).destroy
134
156
  self.instance_nic.each { |o| o.destroy }
135
- self.instance_netfilter_groups.each{|o| o.destroy }
157
+ self.remove_all_netfilter_groups
136
158
  self.volume.each { |v|
137
159
  v.instance_id = nil
138
160
  v.state = :available
@@ -154,14 +176,15 @@ module Dcmgr::Models
154
176
  # this is for internal use.
155
177
  def to_hash
156
178
  h = super
157
- h = h.merge({:user_data => user_data.to_s, # Sequel::BLOB -> String
179
+ h.merge!({:user_data => user_data.to_s, # Sequel::BLOB -> String
158
180
  :runtime_config => self.runtime_config, # yaml -> hash
159
181
  :image=>image.to_hash,
160
182
  :host_pool=>host_pool.to_hash,
161
183
  :instance_nics=>instance_nic.map {|n| n.to_hash },
162
- :instance_spec=>instance_spec.to_hash,
163
- :ips => instance_nic.map { |n| n.ip.ipv4 if n.ip },
164
- })
184
+ :ips => instance_nic.map { |n| n.ip.map {|i| unless i.is_natted? then i.ipv4 else nil end} if n.ip }.flatten.compact,
185
+ :nat_ips => instance_nic.map { |n| n.ip.map {|i| if i.is_natted? then i.ipv4 else nil end} if n.ip }.flatten.compact,
186
+ })
187
+ h.merge!({:instance_spec=>instance_spec.to_hash}) unless instance_spec.nil?
165
188
  h[:volume]={}
166
189
  if self.volume
167
190
  self.volume.each { |v|
@@ -186,12 +209,13 @@ module Dcmgr::Models
186
209
  # :created_at
187
210
  # :state
188
211
  # :status
212
+ # :vif => {'vif-xxxxx'=>{:ipv4=>{:address=>'8.8.8.8', :nat_address=>'9.9.9.9.9'}}}
189
213
  # }
190
214
  def to_api_document
191
215
  h = {
192
216
  :id => canonical_uuid,
193
- :cpu_cores => instance_spec.cpu_cores,
194
- :memory_size => instance_spec.memory_size,
217
+ :cpu_cores => cpu_cores,
218
+ :memory_size => memory_size,
195
219
  :image_id => image.canonical_uuid,
196
220
  :created_at => self.created_at,
197
221
  :state => self.state,
@@ -200,19 +224,41 @@ module Dcmgr::Models
200
224
  :network => [],
201
225
  :volume => [],
202
226
  :netfilter_group => [],
227
+ :vif => [],
203
228
  }
204
- if self.ssh_key_pair
205
- h[:ssh_key_pair] = self.ssh_key_pair.name
229
+ if self.ssh_key_data
230
+ h[:ssh_key_pair] = self.ssh_key_data[:name]
206
231
  end
207
232
 
208
233
  if instance_nic
209
234
  instance_nic.each { |n|
210
- if n.ip
211
- h[:network] << {
212
- :network_name => n.network.canonical_uuid,
213
- :ipaddr => n.ip.ipv4
235
+ direct_lease_ds = n.direct_ip_lease_dataset
236
+ next if direct_lease_ds.first.nil?
237
+ outside_lease_ds = n.nat_ip_lease_dataset
238
+
239
+ h[:network] << {
240
+ :network_name => n.network.canonical_uuid,
241
+ :ipaddr => direct_lease_ds.all.map {|lease| lease.ipv4 }.compact,
242
+ :nat_ipaddr => outside_lease_ds.all.map {|lease| lease.ipv4 }.compact
243
+ }
244
+ }
245
+ end
246
+
247
+ if instance_nic
248
+ instance_nic.each { |vif|
249
+ ent = {
250
+ :vif_id=>vif.canonical_uuid,
251
+ }
252
+ direct_lease = vif.direct_ip_lease.first
253
+ if direct_lease.nil?
254
+ else
255
+ outside_lease = direct_lease.nat_outside_lease
256
+ ent[:ipv4] = {
257
+ :address=> direct_lease.ipv4,
258
+ :nat_address => outside_lease.nil? ? nil : outside_lease.ipv4,
214
259
  }
215
260
  end
261
+ h[:vif] << ent
216
262
  }
217
263
  end
218
264
 
@@ -244,14 +290,6 @@ module Dcmgr::Models
244
290
  self.image.arch
245
291
  end
246
292
 
247
- def cpu_cores
248
- self.instance_spec.cpu_cores
249
- end
250
-
251
- def memory_size
252
- self.instance_spec.memory_size
253
- end
254
-
255
293
  def config
256
294
  self.instance_spec.config
257
295
  end
@@ -261,6 +299,7 @@ module Dcmgr::Models
261
299
  vendor_id ||= '00:ff:f1'
262
300
  nic = InstanceNic.new(:mac_addr=>vendor_id)
263
301
  nic.network = network
302
+ nic.nat_network = network.nat_network
264
303
  nic.instance = self
265
304
  nic.save
266
305
  end
@@ -335,10 +374,20 @@ module Dcmgr::Models
335
374
  Volume.lock!
336
375
  VolumeSnapshot.lock!
337
376
  IpLease.lock!
377
+ HostnameLease.lock!
378
+ Network.lock!
338
379
  end
339
380
 
340
381
  def live?
341
382
  self.terminated_at.nil?
342
383
  end
384
+
385
+ def set_ssh_key_pair(ssh_key_pair)
386
+ raise ArgumentError unless ssh_key_pair.is_a?(SshKeyPair)
387
+ self.ssh_key_data = ssh_key_pair.to_hash
388
+ # TODO: remove ssh_key_pair_id column
389
+ self.ssh_key_pair_id = ssh_key_pair.canonical_uuid
390
+ end
391
+
343
392
  end
344
393
  end