PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/project_razor/model/vmware_esxi.rb

https://github.com/Acidburn0zzz/Razor
Ruby | 329 lines | 304 code | 11 blank | 14 comment | 10 complexity | b0663264da9b454fe4fcfe25c924d496 MD5 | raw file
Possible License(s): Apache-2.0
  1. require "erb"
  2. # Root ProjectRazor namespace
  3. module ProjectRazor
  4. module ModelTemplate
  5. # Root Model object
  6. # @abstract
  7. class VMwareESXi < ProjectRazor::ModelTemplate::Base
  8. include(ProjectRazor::Logging)
  9. # Assigned image
  10. attr_accessor :image_uuid
  11. # Metadata
  12. attr_accessor :hostname
  13. # Compatible Image Prefix
  14. attr_accessor :image_prefix
  15. def initialize(hash)
  16. super(hash)
  17. # Static config
  18. @hidden = true
  19. @template = :vmware_hypervisor
  20. @name = "vmware_esx_generic"
  21. @description = "vmware esxi generic"
  22. # Metadata vars
  23. @hostname_prefix = nil
  24. # State / must have a starting state
  25. @current_state = :init
  26. # Image UUID
  27. @image_uuid = true
  28. # Image prefix we can attach
  29. @image_prefix = "esxi"
  30. # Enable agent brokers for this model
  31. @broker_plugin = :proxy
  32. @final_state = :os_complete
  33. # Metadata vars
  34. @esx_license = nil
  35. @ip_range_network = nil
  36. @ip_range_start = nil
  37. @ip_range_end = nil
  38. @gateway = nil
  39. @hostname_prefix = nil
  40. @nameserver = nil
  41. @ntpserver = nil
  42. @vcenter_name = nil
  43. @vcenter_datacenter_path = nil
  44. @vcenter_cluster_path = nil
  45. # Metadata
  46. @req_metadata_hash = {
  47. "@esx_license" => { :default => "",
  48. :example => "AAAAA-BBBBB-CCCCC-DDDDD-EEEEE",
  49. :validation => '^[A-Z\d]{5}-[A-Z\d]{5}-[A-Z\d]{5}-[A-Z\d]{5}-[A-Z\d]{5}$',
  50. :required => true,
  51. :description => "ESX License Key" },
  52. "@root_password" => { :default => "test1234",
  53. :example => "P@ssword!",
  54. :validation => '^[\S]{8,}',
  55. :required => true,
  56. :description => "root password (> 8 characters)"
  57. },
  58. "@ip_range_network" => { :default => "",
  59. :example => "192.168.10",
  60. :validation => '^\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b$',
  61. :required => true,
  62. :description => "IP Network for hosts" },
  63. "@ip_range_subnet" => { :default => "255.255.255.0",
  64. :example => "255.255.255.0",
  65. :validation => '^\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b$',
  66. :required => true,
  67. :description => "IP Subnet" },
  68. "@ip_range_start" => { :default => "",
  69. :example => "1",
  70. :validation => '^\b(25[0-4]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',
  71. :required => true,
  72. :description => "Starting IP address (1-254)" },
  73. "@ip_range_end" => { :default => "",
  74. :example => "50",
  75. :validation => '^\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',
  76. :required => true,
  77. :description => "Ending IP address (2-255)" },
  78. "@gateway" => { :default => "",
  79. :example => "192.168.1.1",
  80. :validation => '^\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b$',
  81. :required => true,
  82. :description => "Gateway for node" },
  83. "@hostname_prefix" => { :default => "",
  84. :example => "esxi-node",
  85. :validation => '^[A-Za-z\d-]{3,}$',
  86. :required => true,
  87. :description => "Prefix for naming node" },
  88. "@nameserver" => { :default => "",
  89. :example => "192.168.10.10",
  90. :validation => '^\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b$',
  91. :required => true,
  92. :description => "Nameserver for node" },
  93. "@ntpserver" => { :default => "",
  94. :example => "ntp.razor.example.local",
  95. :validation => '^[\w.]{3,}$',
  96. :required => true,
  97. :description => "NTP server for node" },
  98. "@vcenter_name" => { :default => "",
  99. :example => "vcenter01",
  100. :validation => '^[\w.-]{3,}$',
  101. :required => false,
  102. :description => "Optional for broker use: the vCenter to attach ESXi node to" },
  103. "@vcenter_datacenter_path" => { :default => "",
  104. :example => "Datacenter01",
  105. :validation => '^[a-zA-Z\d-]{3,}$',
  106. :required => false,
  107. :description => "Optional for broker use: the vCenter Datacenter path to place ESXi host in" },
  108. "@vcenter_cluster_path" => { :default => "",
  109. :example => "Cluster01",
  110. :validation => '^[a-zA-Z\d-]{3,}$',
  111. :required => false,
  112. :description => "Optional for broker use: the vCenter Cluster to place ESXi node in" }
  113. }
  114. from_hash(hash) unless hash == nil
  115. end
  116. def node_ip_address
  117. "#{@ip_range_network}.#{(@ip_range_start..@ip_range_end).to_a[@counter - 1]}"
  118. end
  119. def node_hostname
  120. @hostname_prefix + @counter.to_s
  121. end
  122. def broker_proxy_handoff
  123. logger.debug "Broker proxy called for: #{@broker.name}"
  124. if node_ip_address
  125. options = {
  126. :username => "root",
  127. :password => @root_password,
  128. :metadata => node_metadata,
  129. :hostname => node_hostname,
  130. :uuid => @node.uuid,
  131. :ipaddress => node_ip_address,
  132. :vcenter_name => @vcenter_name,
  133. :vcenter_datacenter_path => @vcenter_datacenter_path,
  134. :vcenter_cluster_path => @vcenter_cluster_path,
  135. }
  136. @current_state = @broker.proxy_hand_off(options)
  137. else
  138. logger.error "Node IP address isn't known"
  139. @current_state = :broker_fail
  140. end
  141. broker_fsm_log
  142. end
  143. def callback
  144. {
  145. "broker" => :broker_agent_handoff,
  146. "boot_cfg" => :boot_cfg,
  147. "kickstart" => :kickstart,
  148. "postinstall" => :postinstall,
  149. }
  150. end
  151. def fsm_tree
  152. {
  153. :init => { :mk_call => :init,
  154. :boot_call => :init,
  155. :kickstart_start => :preinstall,
  156. :kickstart_file => :init,
  157. :kickstart_end => :postinstall,
  158. :timeout => :timeout_error,
  159. :error => :error_catch,
  160. :else => :init },
  161. :preinstall => { :mk_call => :preinstall,
  162. :boot_call => :preinstall,
  163. :kickstart_start => :preinstall,
  164. :kickstart_file => :init,
  165. :kickstart_end => :postinstall,
  166. :kickstart_timeout => :timeout_error,
  167. :error => :error_catch,
  168. :else => :preinstall },
  169. :postinstall => { :mk_call => :postinstall,
  170. :boot_call => :postinstall,
  171. :postinstall_end => :os_complete,
  172. :kickstart_file => :postinstall,
  173. :kickstart_end => :postinstall,
  174. :kickstart_timeout => :postinstall,
  175. :error => :error_catch,
  176. :else => :preinstall },
  177. :os_complete => { :mk_call => :os_complete,
  178. :boot_call => :os_complete,
  179. :else => :os_complete,
  180. :reset => :init },
  181. :timeout_error => { :mk_call => :timeout_error,
  182. :boot_call => :timeout_error,
  183. :else => :timeout_error,
  184. :reset => :init },
  185. :error_catch => { :mk_call => :error_catch,
  186. :boot_call => :error_catch,
  187. :else => :error_catch,
  188. :reset => :init },
  189. }
  190. end
  191. def mk_call(node, policy_uuid)
  192. super(node, policy_uuid)
  193. case @current_state
  194. # We need to reboot
  195. when :init, :preinstall, :postinstall, :os_complete
  196. ret = [:reboot, { }]
  197. when :timeout_error, :error_catch
  198. ret = [:acknowledge, { }]
  199. else
  200. ret = [:acknowledge, { }]
  201. end
  202. fsm_action(:mk_call, :mk_call)
  203. ret
  204. end
  205. def boot_call(node, policy_uuid)
  206. super(node, policy_uuid)
  207. case @current_state
  208. when :init, :preinstall
  209. ret = start_install(node, policy_uuid)
  210. when :postinstall, :os_complete, :broker_check, :broker_fail, :broker_success, :complete_no_broker
  211. ret = local_boot(node)
  212. when :timeout_error, :error_catch
  213. engine = ProjectRazor::Engine.instance
  214. ret = engine.default_mk_boot(node.uuid)
  215. else
  216. engine = ProjectRazor::Engine.instance
  217. ret = engine.default_mk_boot(node.uuid)
  218. end
  219. fsm_action(:boot_call, :boot_call)
  220. ret
  221. end
  222. def start_install(node, policy_uuid)
  223. ip = "#!ipxe\n"
  224. ip << "echo Reached #{@label} model boot_call\n"
  225. ip << "echo Our image UUID is: #{@image_uuid}\n"
  226. ip << "echo Our state is: #{@current_state}\n"
  227. ip << "echo Our node UUID: #{node.uuid}\n"
  228. ip << "\n"
  229. ip << "echo We will be running an install now\n"
  230. ip << "sleep 3\n"
  231. ip << "\n"
  232. ip << "kernel --name mboot.c32 #{image_svc_uri}/#{@image_uuid}/mboot.c32\n"
  233. ip << "imgargs mboot.c32 -c #{api_svc_uri}/policy/callback/#{policy_uuid}/boot_cfg\n"
  234. ip << "boot\n"
  235. ip
  236. end
  237. def local_boot(node)
  238. ip = "#!ipxe\n"
  239. ip << "echo Reached #{@label} model boot_call\n"
  240. ip << "echo Our image UUID is: #{@image_uuid}\n"
  241. ip << "echo Our state is: #{@current_state}\n"
  242. ip << "echo Our node UUID: #{node.uuid}\n"
  243. ip << "\n"
  244. ip << "echo Continuing local boot\n"
  245. ip << "sleep 3\n"
  246. ip << "\n"
  247. ip << "sanboot --no-describe --drive 0x80\n"
  248. ip
  249. end
  250. def kickstart
  251. @arg = @args_array.shift
  252. case @arg
  253. when "start"
  254. fsm_action(:kickstart_start, :kickstart)
  255. return "ok"
  256. when "end"
  257. fsm_action(:kickstart_end, :kickstart)
  258. return "ok"
  259. when "file"
  260. fsm_action(:kickstart_file, :kickstart)
  261. return kickstart_file
  262. else
  263. return "error"
  264. end
  265. end
  266. def postinstall
  267. @arg = @args_array.shift
  268. case @arg
  269. when "end"
  270. fsm_action(:postinstall_end, :postinstall)
  271. return "ok"
  272. when "debug"
  273. ret = ""
  274. ret << "vcenter: #{@vcenter_name}\n"
  275. ret << "vcenter: #{@vcenter_datacenter_path}\n"
  276. ret << "vcenter: #{@vcenter_cluster_path}\n"
  277. return ret
  278. else
  279. return "error"
  280. end
  281. end
  282. def boot_cfg
  283. @image = get_data.fetch_object_by_uuid(:images, @image_uuid)
  284. if @node.dhcp_mac
  285. @image.boot_cfg.gsub("/",
  286. "#{image_svc_uri}/#{@image_uuid}/").gsub("runweasel",
  287. "ks=#{api_svc_uri}/policy/callback/#{@policy_uuid}/kickstart/file BOOTIF=#{@node.dhcp_mac}")
  288. else
  289. @image.boot_cfg.gsub("/",
  290. "#{image_svc_uri}/#{@image_uuid}/").gsub("runweasel",
  291. "ks=#{api_svc_uri}/policy/callback/#{@policy_uuid}/kickstart/file")
  292. end
  293. end
  294. # ERB.result(binding) is failing in Ruby 1.9.2 and 1.9.3 so template is processed in the def block.
  295. def template_filepath(filename)
  296. raise ProjectRazor::Error::Slice::InternalError, "must provide esxi version." unless @osversion
  297. filepath = File.join(File.dirname(__FILE__), "esxi/#{@osversion}/#{filename}.erb")
  298. end
  299. def kickstart_file
  300. filepath = template_filepath('kickstart')
  301. ERB.new(File.read(filepath)).result(binding)
  302. end
  303. end
  304. end
  305. end