Class: Kytoon::Providers::Xenserver::ServerGroup

Inherits:
Object
  • Object
show all
Defined in:
lib/kytoon/providers/xenserver/server_group.rb

Overview

ip addr add 192.168.0.1/24 brd 192.168.0.255 scope global dev xenbr1

Constant Summary collapse

CONFIG_FILE =
KYTOON_PROJECT + File::SEPARATOR + "config" + File::SEPARATOR + "server_group.json"
@@data_dir =
File.join(KYTOON_PROJECT, "tmp", "xenserver")

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ ServerGroup

Returns a new instance of ServerGroup.

Raises:



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 53

def initialize(options={})
@id = options[:id] || Time.now.to_f
@name = options[:name]
@netmask = options[:netmask]
@gateway = options[:gateway]
@broadcast = options[:broadcast]
@network_type = options[:network_type]
@bridge = options[:bridge]
@public_ip_bridge = options[:public_ip_bridge]
@dns_nameserver = options[:dns_nameserver]
@cleanup_before_create = options[:cleanup_before_create]
@gateway_ip = options[:gateway_ip]
@gateway_ip = ENV['GATEWAY_IP'] if @gateway_ip.nil?
raise ConfigException, "Please specify a GATEWAY_IP" if @gateway_ip.nil?

@servers=[]
end

Instance Attribute Details

#bridgeObject

Returns the value of attribute bridge.



48
49
50
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 48

def bridge
  @bridge
end

#broadcastObject

Returns the value of attribute broadcast.



46
47
48
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 46

def broadcast
  @broadcast
end

#cleanup_before_createObject

Returns the value of attribute cleanup_before_create.



51
52
53
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 51

def cleanup_before_create
  @cleanup_before_create
end

#dns_nameserverObject

Returns the value of attribute dns_nameserver.



50
51
52
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 50

def dns_nameserver
  @dns_nameserver
end

#gatewayObject

Returns the value of attribute gateway.



45
46
47
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 45

def gateway
  @gateway
end

#gateway_ipObject

Returns the value of attribute gateway_ip.



43
44
45
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 43

def gateway_ip
  @gateway_ip
end

#idObject

Returns the value of attribute id.



41
42
43
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 41

def id
  @id
end

#nameObject

Returns the value of attribute name.



42
43
44
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 42

def name
  @name
end

#netmaskObject

Returns the value of attribute netmask.



44
45
46
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 44

def netmask
  @netmask
end

#network_typeObject

Returns the value of attribute network_type.



47
48
49
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 47

def network_type
  @network_type
end

#public_ip_bridgeObject

Returns the value of attribute public_ip_bridge.



49
50
51
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 49

def public_ip_bridge
  @public_ip_bridge
end

Class Method Details

.cleanup_instances(gw_ip) ⇒ Object



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 311

def self.cleanup_instances(gw_ip)
  Kytoon::Util.remote_exec(%{
    for UUID in $(xe vm-list is-control-domain=false | grep uuid | sed -e 's|.*: ||'); do
      # destroy all instances except the basevm's
      if ! xe vm-param-get param-name=other-config uuid=$UUID | grep -c kytoon_base_vm; then
        echo "Destroying Xen instance uuid: $UUID"
        xe vm-shutdown force=true uuid=$UUID
        xe vm-uninstall uuid=$UUID force=true
      fi
    done
    for VDI_UUID in $(xe vdi-list read-only=false | grep -v sr-uuid | grep uuid | sed -e 's|.*: ||'); do
      # destroy all vdi's which aren't in use
      IN_USE=$(xe vbd-list vdi-uuid=$VDI_UUID | grep vdi-uuid | grep -c $VDI_UUID)
      if [[ "$IN_USE" -eq "0" ]]; then
        echo "removing VDI: $VDI_UUID"
        xe vdi-destroy uuid=$VDI_UUID
      fi
    done
  }, gw_ip) do |ok, out|
    if not ok
      puts out
      raise "Failed to cleanup instances."
    end
  end
end

.configure_static_networking(gw_ip, hostname, ip_address, netmask, gateway, broadcast, mac, dns_nameserver) ⇒ Object



337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 337

def self.configure_static_networking(gw_ip, hostname, ip_address, netmask, gateway, broadcast, mac, dns_nameserver)

  # networking
  network_info = {
    "label" => "public",
    "broadcast" => broadcast,
    "ips" => [{
    "ip" => ip_address,
    "netmask" => netmask,
    "enabled" => "1"}],
    "mac" => mac,
    "dns" => [dns_nameserver],
    "gateway" => gateway
  }

  Kytoon::Util.remote_exec(%{
    UUID=$(xe vm-list name-label=#{hostname} | grep uuid | sed -e 's|.*: ||')
    DOMID=$(xe vm-param-get uuid=$UUID param-name="dom-id")
    xenstore-write -s /local/domain/$DOMID/vm-data/hostname '#{hostname}'
    xenstore-write -s /local/domain/$DOMID/vm-data/networking/123_nw_info '#{network_info.to_json}'

    xenstore-write -s /local/domain/$DOMID/data/host/123_reset_nw '{"name": "resetnetwork", "value": ""}'
    xenstore-rm -s /local/domain/$DOMID/data/guest/123_reset_nw 2> /dev/null
    until [ -n "$NW_RETVAL" ]; do
      NW_RETVAL=$(xenstore-read -s /local/domain/$DOMID/data/guest/123_reset_nw 2> /dev/null)
    done
    xenstore-rm -s /local/domain/$DOMID/data/host/123_reset_nw
    xe vm-reboot uuid=$UUID &> /dev/null
    COUNT=0
    until ping -c 1 #{hostname} &> /dev/null; do
      COUNT=$(( $COUNT + 1 ))
      [ $COUNT -eq 10 ] && break
    done
    until ssh -o ConnectTimeout=1 #{hostname} &> /dev/null; do
      COUNT=$(( $COUNT + 1 ))
      [ $COUNT -eq 20 ] && break
      sleep 1
    done
    ssh #{hostname} bash <<-EOF_SSH_BASH
hostname #{hostname}
    EOF_SSH_BASH
    scp /etc/hosts #{hostname}:/etc/hosts
  }, gw_ip) do |ok, out|
    puts out
    if not ok
      puts out
      raise "Failed to setup static networking for #{hostname}."
    end
  end

end

.create(sg) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 174

def self.create(sg)
  ServerGroup.cleanup_instances(sg.gateway_ip) if sg.cleanup_before_create
  sg.cache_to_disk
  init_host(sg)
  status, host_ssh_public_key = Kytoon::Util.remote_exec(%{
if [ -f /root/.ssh/id_rsa.pub ]; then
cat /root/.ssh/id_rsa.pub
elif [ -f /root/.ssh/id_dsa.pub ]; then
cat /root/.ssh/id_dsa.pub
else
exit 1
fi
  }, sg.gateway_ip)
  sg.servers.each do |server|
      create_instance(sg.gateway_ip, server['image_path'], server['hostname'], server['mac'], sg.bridge, host_ssh_public_key)
      network_type = sg.network_type
      if network_type == 'static' then
          configure_static_networking(sg.gateway_ip, server['hostname'], server['ip_address'], sg.netmask, sg.gateway, sg.broadcast, server['mac'], sg.dns_nameserver)
      else
        raise "Unsupported network type '#{sg.network_type}'"
      end
  end
  sg
end

.create_instance(gw_ip, image_path, hostname, mac, xen_bridge = 'xenbr1', ssh_public_key = nil) ⇒ Object



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 268

def self.create_instance(gw_ip, image_path, hostname, mac, xen_bridge='xenbr1', ssh_public_key=nil)
  file_data = Base64.encode64("/root/.ssh/authorized_keys,#{ssh_public_key}")

  Kytoon::Util.remote_exec(%{
    BASE_VM_NAME=$(basename #{image_path})
    BASE_VM_UUID=$(xe vm-list name-label=$BASE_VM_NAME | grep uuid | sed -e 's|.*: ||')

    if [ -z "$BASE_VM_UUID" ]; then
      # create base vm for future use
      BASE_UUID=$(xe vm-import filename=#{image_path})
      xe vm-param-set name-label=$BASE_VM_NAME uuid=$BASE_UUID
      xe vm-param-set other-config:kytoon_base_vm=true uuid=$BASE_UUID
    fi

    UUID=$(xe vm-clone vm=$BASE_VM_NAME new-name-label=${BASE_VM_NAME}_new)
    xe vm-param-remove param-name=other-config param-key=kytoon_base_vm uuid=$UUID
    xe vm-param-set name-label=#{hostname} uuid=$UUID
    NETWORK_UUID=$(xe network-list bridge=#{xen_bridge} | grep -P "^uuid" | cut -f2 -d: | cut -f2 -d" ")
    xe vif-destroy uuid=$VIF_UUID &> /dev/null
    for VIF_UUID in $(xe vif-list vm-uuid=$UUID | grep uuid | sed -e 's|.*: ||'); do
      echo "Destroying Xen VIF uuid: $VIF_UUID"
      xe vif-destroy uuid=$VIF_UUID &> /dev/null
    done
    xe vif-create vm-uuid=$UUID mac=#{mac} network-uuid=$NETWORK_UUID device=0 &> /dev/null
    xe vm-start uuid=$UUID &> /dev/null

    # inject ssh from host
    DOMID=$(xe vm-param-get uuid=$UUID param-name="dom-id")
    xenstore-rm -s /local/domain/$DOMID/data/guest/ssh_key 2> /dev/null
    xenstore-write -s /local/domain/$DOMID/data/host/ssh_key '{"name": "injectfile", "value": "#{file_data}"}'
    until [ -n "$INJECT_RETVAL" ]; do
      INJECT_RETVAL=$(xenstore-read -s /local/domain/$DOMID/data/guest/ssh_key 2> /dev/null)
    done
    xenstore-rm -s /local/domain/$DOMID/data/host/ssh_key

  }, gw_ip) do |ok, out|
    if not ok
      puts out
      raise KytoonException, "Failed to create instance #{hostname}."
    end
  end
end

.data_dirObject



31
32
33
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 31

def self.data_dir
  @@data_dir
end

.data_dir=(dir) ⇒ Object



35
36
37
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 35

def self.data_dir=(dir)
  @@data_dir=dir
end

.from_json(json) ⇒ Object

generate a Server Group XML from server_group.json



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 84

def self.from_json(json)

  json_hash=JSON.parse(json)

  sg=ServerGroup.new(
    :id => json_hash["id"],
    :name => json_hash["name"],
    :netmask => json_hash['netmask'],
    :gateway => json_hash['gateway'],
    :gateway_ip => json_hash['gateway_ip'],
    :broadcast => json_hash['broadcast'],
    :dns_nameserver => json_hash['dns_nameserver'],
    :network_type => json_hash['network_type'],
    :public_ip_bridge => json_hash['public_ip_bridge'],
    :cleanup_before_create => json_hash['cleanup_before_create'],
    :bridge => json_hash['bridge']
  )
  json_hash["servers"].each do |server_hash|
    sg.servers << {
      'hostname' => server_hash['hostname'],
      'ip_address' => server_hash['ip_address'],
      'mac' => server_hash['mac'],
      'image_path' => server_hash['image_path']
    }
  end
  return sg

end

.get(options = {}) ⇒ Object



199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 199

def self.get(options={})
  id = options[:id]
  if id.nil? then
    group=ServerGroup.most_recent
    raise NoServerGroupExists, "No server group files exist." if group.nil?
    id=group.id
  end

  out_file=File.join(@@data_dir, "#{id}.json")
  raise NoServerGroupExists, "No server group files exist." if not File.exists?(out_file)
  ServerGroup.from_json(IO.read(out_file))
end

.index(options = {}) ⇒ Object



212
213
214
215
216
217
218
219
220
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 212

def self.index(options={})

  server_groups=[]
  Dir[File.join(ServerGroup.data_dir, '*.json')].each do  |file|
    server_groups << ServerGroup.from_json(IO.read(file))
  end
  server_groups

end

.init_host(sg) ⇒ Object



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 234

def self.init_host(sg)

  cidr = IPAddr.new(sg.netmask).to_i.to_s(2).count("1")

  hosts_file_data = "127.0.0.1\tlocalhost localhost.localdomain\n"
  sg.servers.each do |server|
    hosts_file_data += "#{server['ip_address']}\t#{server['hostname']}\n"
  end

  Kytoon::Util.remote_exec(%{
# Add first IP to bridge
if ! ip a | grep #{sg.gateway}/#{cidr} | grep #{sg.bridge}; then
ip a add #{sg.gateway}/#{cidr} dev #{sg.bridge}
fi

cat > /etc/hosts <<-EOF_CAT
#{hosts_file_data}
EOF_CAT
# FIXME... probably a bit insecure but most people are probably using
# boxes behind another firewall anyway.
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -t nat -A POSTROUTING -o #{sg.public_ip_bridge} -j MASQUERADE
echo 1 > /proc/sys/net/ipv4/ip_forward
  }, sg.gateway_ip)
end

.most_recentObject



222
223
224
225
226
227
228
229
230
231
232
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 222

def self.most_recent
  server_groups=[]
  Dir[File.join(@@data_dir, "*.json")].each do  |file|
    server_groups << ServerGroup.from_json(IO.read(file))
  end
  if server_groups.size > 0 then
    server_groups.sort { |a,b| b.id <=> a.id }[0]
  else
    nil
  end
end

Instance Method Details

#cache_to_diskObject



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 141

def cache_to_disk

  sg_hash = {
      'id' => @id,
      'name' => @name,
      'netmask' => @netmask,
      'gateway' => @gateway,
      'gateway_ip' => @gateway_ip,
      'broadcast' => @broadcast,
      'dns_nameserver' => @dns_nameserver,
      'cleanup_before_create' => @cleanup_before_create,
      'network_type' => @network_type,
      'public_ip_bridge' => @public_ip_bridge,
      'bridge' => @bridge,
      'servers' => []
  }
  @servers.each do |server|
      sg_hash['servers'] << {'hostname' => server['hostname'], 'ip_address' => server['ip_address'], 'image_path' => server['image_path'], 'mac' => server['mac']}
  end

  FileUtils.mkdir_p(@@data_dir)
  File.open(File.join(@@data_dir, "#{@id}.json"), 'w') do |f|
    f.chmod(0600)
    f.write(sg_hash.to_json)
  end
end

#deleteObject



168
169
170
171
172
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 168

def delete
  ServerGroup.cleanup_instances(@gateway_ip)
  out_file=File.join(@@data_dir, "#{@id}.json")
  File.delete(out_file) if File.exists?(out_file)
end

#pretty_printObject



113
114
115
116
117
118
119
120
121
122
123
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 113

def pretty_print

  puts "Group ID: #{@id}"
  puts "name: #{@name}"
  puts "Servers:"
  servers.each do |server|
    puts "\tname: #{server['hostname']}"
    puts "\t--"
  end

end

#server(name) ⇒ Object



71
72
73
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 71

def server(name)
  @servers.select {|s| s['hostname'] == name}[0] if @servers.size > 0
end

#server_namesObject



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 125

def server_names

  names=[]  

  servers.each do |server|
    if block_given? then
      yield server['hostname']
    else
      names << server['hostname']
    end  
  end

  names
  
end

#serversObject



75
76
77
# File 'lib/kytoon/providers/xenserver/server_group.rb', line 75

def servers
  @servers
end