opennebula 5.12.9 → 6.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ActionManager.rb +1 -1
  3. data/lib/CommandManager.rb +1 -1
  4. data/lib/DriverExecHelper.rb +44 -28
  5. data/lib/OpenNebulaDriver.rb +8 -4
  6. data/lib/VirtualMachineDriver.rb +9 -2
  7. data/lib/cloud/CloudClient.rb +3 -3
  8. data/lib/datacenter.rb +1257 -0
  9. data/lib/datastore.rb +1025 -0
  10. data/lib/distributed_firewall.rb +280 -0
  11. data/lib/file_helper.rb +370 -0
  12. data/lib/host.rb +1517 -0
  13. data/lib/logical_port.rb +50 -0
  14. data/lib/logical_switch.rb +77 -0
  15. data/lib/memoize.rb +74 -0
  16. data/lib/models.rb +32 -0
  17. data/lib/models/role.rb +1126 -0
  18. data/lib/models/service.rb +709 -0
  19. data/lib/network.rb +699 -0
  20. data/lib/nsx_client.rb +144 -0
  21. data/lib/nsx_component.rb +28 -0
  22. data/lib/nsx_constants.rb +149 -0
  23. data/lib/nsx_driver.rb +78 -0
  24. data/lib/nsx_error.rb +77 -0
  25. data/lib/nsx_rule.rb +193 -0
  26. data/lib/nsxt_client.rb +176 -0
  27. data/lib/nsxt_dfw.rb +196 -0
  28. data/lib/nsxt_logical_port.rb +94 -0
  29. data/lib/nsxt_rule.rb +188 -0
  30. data/lib/nsxt_tz.rb +38 -0
  31. data/lib/nsxv_client.rb +176 -0
  32. data/lib/nsxv_dfw.rb +202 -0
  33. data/lib/nsxv_logical_port.rb +107 -0
  34. data/lib/nsxv_rule.rb +172 -0
  35. data/lib/nsxv_tz.rb +41 -0
  36. data/lib/opaque_network.rb +134 -0
  37. data/lib/opennebula.rb +5 -2
  38. data/lib/opennebula/acl.rb +1 -1
  39. data/lib/opennebula/acl_pool.rb +1 -1
  40. data/lib/opennebula/client.rb +10 -2
  41. data/lib/opennebula/cluster.rb +1 -1
  42. data/lib/opennebula/cluster_pool.rb +1 -1
  43. data/lib/opennebula/datastore.rb +1 -1
  44. data/lib/opennebula/datastore_pool.rb +1 -1
  45. data/lib/opennebula/document.rb +8 -29
  46. data/lib/opennebula/document_json.rb +42 -12
  47. data/lib/opennebula/document_pool.rb +1 -1
  48. data/lib/opennebula/document_pool_json.rb +1 -1
  49. data/lib/opennebula/error.rb +4 -1
  50. data/lib/opennebula/flow.rb +23 -0
  51. data/lib/opennebula/flow/grammar.rb +1195 -0
  52. data/lib/opennebula/flow/service_pool.rb +190 -0
  53. data/lib/opennebula/flow/service_template.rb +608 -0
  54. data/lib/opennebula/flow/service_template_ext.rb +84 -0
  55. data/lib/opennebula/flow/service_template_pool.rb +32 -0
  56. data/lib/opennebula/flow/validator.rb +499 -0
  57. data/lib/opennebula/group.rb +1 -1
  58. data/lib/opennebula/group_pool.rb +1 -1
  59. data/lib/opennebula/hook.rb +5 -12
  60. data/lib/opennebula/hook_log.rb +1 -1
  61. data/lib/opennebula/hook_pool.rb +1 -1
  62. data/lib/opennebula/host.rb +1 -1
  63. data/lib/opennebula/host_pool.rb +1 -1
  64. data/lib/opennebula/image.rb +19 -14
  65. data/lib/opennebula/image_pool.rb +1 -1
  66. data/lib/opennebula/ldap_auth.rb +1 -1
  67. data/lib/opennebula/ldap_auth_spec.rb +1 -1
  68. data/lib/opennebula/lockable_ext.rb +163 -0
  69. data/lib/opennebula/marketplace.rb +1 -1
  70. data/lib/opennebula/marketplace_pool.rb +1 -1
  71. data/lib/opennebula/marketplaceapp.rb +9 -119
  72. data/lib/opennebula/marketplaceapp_ext.rb +586 -0
  73. data/lib/opennebula/marketplaceapp_pool.rb +1 -1
  74. data/lib/opennebula/oneflow_client.rb +4 -3
  75. data/lib/opennebula/pool.rb +6 -3
  76. data/lib/opennebula/pool_element.rb +1 -1
  77. data/lib/opennebula/security_group.rb +1 -1
  78. data/lib/opennebula/security_group_pool.rb +1 -1
  79. data/lib/opennebula/server_cipher_auth.rb +1 -1
  80. data/lib/opennebula/server_x509_auth.rb +1 -1
  81. data/lib/opennebula/ssh_auth.rb +1 -1
  82. data/lib/opennebula/system.rb +1 -1
  83. data/lib/opennebula/template.rb +4 -13
  84. data/lib/opennebula/template_ext.rb +342 -0
  85. data/lib/opennebula/template_pool.rb +1 -1
  86. data/lib/opennebula/user.rb +26 -2
  87. data/lib/opennebula/user_pool.rb +1 -1
  88. data/lib/opennebula/utils.rb +1 -1
  89. data/lib/opennebula/vdc.rb +1 -1
  90. data/lib/opennebula/vdc_pool.rb +1 -1
  91. data/lib/opennebula/virtual_machine.rb +26 -206
  92. data/lib/opennebula/virtual_machine_ext.rb +469 -0
  93. data/lib/opennebula/virtual_machine_pool.rb +1 -1
  94. data/lib/opennebula/virtual_network.rb +4 -10
  95. data/lib/opennebula/virtual_network_pool.rb +1 -1
  96. data/lib/opennebula/virtual_router.rb +4 -12
  97. data/lib/opennebula/virtual_router_pool.rb +1 -1
  98. data/lib/opennebula/vm_group.rb +4 -11
  99. data/lib/opennebula/vm_group_pool.rb +1 -1
  100. data/lib/opennebula/vntemplate.rb +4 -13
  101. data/lib/opennebula/vntemplate_pool.rb +1 -1
  102. data/lib/opennebula/wait_ext.rb +257 -0
  103. data/lib/opennebula/x509_auth.rb +1 -1
  104. data/lib/opennebula/xml_element.rb +1 -1
  105. data/lib/opennebula/xml_pool.rb +1 -1
  106. data/lib/opennebula/xml_utils.rb +1 -1
  107. data/lib/opennebula/zone.rb +1 -1
  108. data/lib/opennebula/zone_pool.rb +1 -1
  109. data/lib/rest_client.rb +201 -0
  110. data/lib/scripts_common.rb +183 -0
  111. data/lib/transport_zone.rb +43 -0
  112. data/lib/vcenter_driver.rb +13 -12
  113. data/lib/vcenter_importer.rb +616 -0
  114. data/lib/vi_client.rb +281 -0
  115. data/lib/vi_helper.rb +313 -0
  116. data/lib/virtual_machine.rb +3477 -0
  117. data/lib/virtual_wire.rb +158 -0
  118. data/lib/vm_device.rb +80 -0
  119. data/lib/vm_disk.rb +202 -0
  120. data/lib/vm_folder.rb +69 -0
  121. data/lib/vm_helper.rb +30 -0
  122. data/lib/vm_monitor.rb +303 -0
  123. data/lib/vm_nic.rb +70 -0
  124. data/lib/vm_template.rb +1963 -0
  125. data/lib/vmm_importer.rb +121 -0
  126. metadata +140 -27
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -34,7 +34,9 @@ module OpenNebula
34
34
  :update => "user.update",
35
35
  :chauth => "user.chauth",
36
36
  :quota => "user.quota",
37
- :login => "user.login"
37
+ :login => "user.login",
38
+ :enable => "user.enable",
39
+ :disable => "user.disable"
38
40
  }
39
41
 
40
42
  SELF = -1
@@ -128,6 +130,16 @@ module OpenNebula
128
130
  super(USER_METHODS[:delete])
129
131
  end
130
132
 
133
+ # Enable the User
134
+ def enable()
135
+ set_enabled(true)
136
+ end
137
+
138
+ # Disable the User
139
+ def disable()
140
+ set_enabled(false)
141
+ end
142
+
131
143
  # Changes the password of the given User
132
144
  #
133
145
  # +password+ String containing the new password
@@ -230,5 +242,17 @@ module OpenNebula
230
242
  all_groups = self.retrieve_elements("GROUPS/ID")
231
243
  all_groups.collect! {|x| x.to_i}
232
244
  end
245
+
246
+ private
247
+
248
+ def set_enabled(enabled)
249
+ return Error.new('ID not defined') if !@pe_id
250
+
251
+ rc = @client.call(USER_METHODS[:enable], @pe_id, enabled)
252
+ rc = nil if !OpenNebula.is_error?(rc)
253
+
254
+ return rc
255
+ end
256
+
233
257
  end
234
258
  end
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -14,6 +14,7 @@
14
14
  # limitations under the License. #
15
15
  #--------------------------------------------------------------------------- #
16
16
 
17
+ require 'opennebula/lockable_ext'
17
18
  require 'opennebula/pool_element'
18
19
 
19
20
  module OpenNebula
@@ -123,6 +124,9 @@ module OpenNebula
123
124
  DISK_RESIZE_POWEROFF
124
125
  DISK_RESIZE_UNDEPLOYED
125
126
  HOTPLUG_NIC_POWEROFF
127
+ HOTPLUG_RESIZE
128
+ HOTPLUG_SAVEAS_UNDEPLOYED
129
+ HOTPLUG_SAVEAS_STOPPED
126
130
  }
127
131
 
128
132
  SHORT_VM_STATES={
@@ -204,7 +208,10 @@ module OpenNebula
204
208
  "DISK_RESIZE" => "drsz",
205
209
  "DISK_RESIZE_POWEROFF" => "drsz",
206
210
  "DISK_RESIZE_UNDEPLOYED" => "drsz",
207
- "HOTPLUG_NIC_POWEROFF" => "hotp"
211
+ "HOTPLUG_NIC_POWEROFF" => "hotp",
212
+ "HOTPLUG_RESIZE" => "hotp",
213
+ "HOTPLUG_SAVEAS_UNDEPLOYED" => "hotp",
214
+ "HOTPLUG_SAVEAS_STOPPED" => "hotp"
208
215
  }
209
216
 
210
217
  HISTORY_ACTION=%w{none migrate live-migrate shutdown shutdown-hard
@@ -271,6 +278,8 @@ module OpenNebula
271
278
 
272
279
  # Class constructor
273
280
  def initialize(xml, client)
281
+ LockableExt.make_lockable(self, VM_METHODS)
282
+
274
283
  super(xml,client)
275
284
  end
276
285
 
@@ -701,9 +710,9 @@ module OpenNebula
701
710
  end
702
711
  end
703
712
 
704
- # Changes the attributes of a VM in power off, failure and undeploy
705
- # states
706
- # @param new_conf, string describing the new attributes. Each attribute
713
+ # Changes the attributes of a VM in power off, failure and undeploy
714
+ # states
715
+ # @param new_conf, string describing the new attributes. Each attribute
707
716
  # will replace the existing ones or delete it if empty. Attributes that
708
717
  # can be updated are: INPUT/{TYPE, BUS}; RAW/{TYPE, DATA, DATA_VMX},
709
718
  # OS/{BOOT, BOOTLOADER, ARCH, MACHINE, KERNEL, INITRD},
@@ -711,20 +720,10 @@ module OpenNebula
711
720
  # and GRAPHICS/{TYPE, LISTEN, PASSWD, KEYMAP}
712
721
  # @return [nil, OpenNebula::Error] nil in case of success, Error
713
722
  # otherwise
714
- def updateconf(new_conf)
723
+ def updateconf(new_conf)
715
724
  return call(VM_METHODS[:updateconf], @pe_id, new_conf)
716
725
  end
717
726
 
718
- # Lock a VM
719
- def lock(level)
720
- return call(VM_METHODS[:lock], @pe_id, level)
721
- end
722
-
723
- # Unlock a VM
724
- def unlock()
725
- return call(VM_METHODS[:unlock], @pe_id)
726
- end
727
-
728
727
  ########################################################################
729
728
  # Helpers to get VirtualMachine information
730
729
  ########################################################################
@@ -771,205 +770,26 @@ module OpenNebula
771
770
  self['DEPLOY_ID']
772
771
  end
773
772
 
774
- # Clones the VM's source Template, replacing the disks with live snapshots
775
- # of the current disks. The VM capacity and NICs are also preserved
776
- #
777
- # @param name [String] Name for the new Template
778
- # @param name [true,false,nil] Optional, true to make the saved images
779
- # persistent, false make them non-persistent
780
- #
781
- # @return [Integer, OpenNebula::Error] the new Template ID in case of
782
- # success, error otherwise
783
- REMOVE_VNET_ATTRS = %w{AR_ID BRIDGE CLUSTER_ID IP MAC TARGET NIC_ID
784
- NETWORK_ID VN_MAD SECURITY_GROUPS VLAN_ID}
785
-
786
- REMOVE_IMAGE_ATTRS = %w{DEV_PREFIX SOURCE ORIGINAL_SIZE SIZE
787
- DISK_SNAPSHOT_TOTAL_SIZE DRIVER IMAGE_STATE SAVE CLONE READONLY
788
- PERSISTENT TARGET ALLOW_ORPHANS CLONE_TARGET CLUSTER_ID DATASTORE
789
- DATASTORE_ID DISK_ID DISK_TYPE IMAGE_ID IMAGE IMAGE_UNAME IMAGE_UID
790
- LN_TARGET TM_MAD TYPE OPENNEBULA_MANAGED}
791
-
792
- def save_as_template(name,description, persistent=nil)
793
- img_ids = []
794
- new_tid = nil
795
- begin
796
- rc = info()
797
- raise if OpenNebula.is_error?(rc)
798
-
799
- tid = self['TEMPLATE/TEMPLATE_ID']
800
- if tid.nil? || tid.empty?
801
- rc = Error.new('VM has no template to be saved')
802
- raise
803
- end
804
-
805
- if state_str() != "POWEROFF"
806
- rc = Error.new("VM state must be POWEROFF, "<<
807
- "current state is #{state_str()}, #{lcm_state_str()}")
808
- raise
809
- end
810
-
811
- # Clone the source template
812
- rc = OpenNebula::Template.new_with_id(tid, @client).clone(name)
813
- raise if OpenNebula.is_error?(rc)
814
-
815
- new_tid = rc
816
-
817
- # Replace the original template's capacity with the actual VM values
818
- replace = ""
819
- if !description.nil?
820
- replace << "DESCRIPTION = \"#{description}\"\n"
821
- end
822
- cpu = self['TEMPLATE/CPU']
823
- if !cpu.nil? && !cpu.empty?
824
- replace << "CPU = #{cpu}\n"
825
- end
826
-
827
- vcpu = self['TEMPLATE/VCPU']
828
- if !vcpu.nil? && !vcpu.empty?
829
- replace << "VCPU = #{vcpu}\n"
830
- end
831
-
832
- mem = self['TEMPLATE/MEMORY']
833
- if !mem.nil? && !mem.empty?
834
- replace << "MEMORY = #{mem}\n"
835
- end
836
-
837
- self.each('TEMPLATE/DISK') do |disk|
838
- # While the previous snapshot is still in progress, we wait
839
- # indefinitely
840
- rc = info()
841
- raise if OpenNebula.is_error?(rc)
842
-
843
- steps = 0
844
- while lcm_state_str() == "HOTPLUG_SAVEAS_POWEROFF"
845
- if steps < 30
846
- sleep 1
847
- else
848
- sleep 15
849
- end
850
-
851
- rc = info()
852
- raise if OpenNebula.is_error?(rc)
853
-
854
- steps += 1
855
- end
856
-
857
- # If the VM is not busy with a previous disk snapshot, we wait
858
- # but this time with a timeout
859
- rc = wait_state("POWEROFF")
860
- raise if OpenNebula.is_error?(rc)
861
-
862
- disk_id = disk["DISK_ID"]
863
- if disk_id.nil? || disk_id.empty?
864
- rc = Error.new('The DISK_ID is missing from the VM template')
865
- raise
866
- end
867
-
868
- image_id = disk["IMAGE_ID"]
869
- opennebula_managed = disk["OPENNEBULA_MANAGED"]
870
- type = disk["TYPE"]
871
-
872
- REMOVE_IMAGE_ATTRS.each do |attr|
873
- disk.delete_element(attr)
874
- end
875
-
876
- if !image_id.nil? && !image_id.empty?
877
- if type == 'CDROM'
878
- replace << "DISK = [ IMAGE_ID = #{image_id}"
879
- if opennebula_managed
880
- replace << ", OPENNEBULA_MANAGED=#{opennebula_managed}"
881
- end
882
- replace << " ]\n"
883
- else
884
- rc = disk_saveas(disk_id.to_i,"#{name}-disk-#{disk_id}","",-1)
885
-
886
- raise if OpenNebula.is_error?(rc)
887
-
888
- if persistent == true
889
- OpenNebula::Image.new_with_id(rc.to_i, @client).persistent()
890
- end
891
-
892
- img_ids << rc.to_i
893
-
894
- disk_template = disk.template_like_str(".").tr("\n", ",\n")
895
-
896
- if disk_template.empty?
897
- replace << "DISK = [ IMAGE_ID = #{rc} ] \n"
898
- else
899
- replace << "DISK = [ IMAGE_ID = #{rc}, " <<
900
- disk_template << " ] \n"
901
- end
902
- end
903
- else
904
- # Volatile disks cannot be saved, so the definition is copied
905
- replace << self.template_like_str(
906
- "TEMPLATE", true, "DISK[DISK_ID=#{disk_id}]") << "\n"
907
- end
908
- end
909
-
910
- self.each('TEMPLATE/NIC') do |nic|
911
- nic_id = nic["NIC_ID"]
773
+ def wait_state(state, timeout=120)
774
+ require 'opennebula/wait_ext'
912
775
 
913
- if nic_id.nil? || nic_id.empty?
914
- rc = Error.new('The NIC_ID is missing from the VM template')
915
- raise
916
- end
776
+ extend OpenNebula::WaitExt
917
777
 
918
- REMOVE_VNET_ATTRS.each do |attr|
919
- # Remove every automatically generated value
920
- # The vnet will be referenced via NAME + UNAME (if defined)
921
- nic.delete_element(attr)
922
- end
778
+ rc = wait2(state, 'LCM_INIT', timeout)
923
779
 
924
- replace << "NIC = [ " << nic.template_like_str(".").tr("\n", ",\n") << " ] \n"
925
- end
926
-
927
- # Required by the Sunstone Cloud View
928
- replace << "SAVED_TEMPLATE_ID = #{tid}\n"
929
-
930
- new_tmpl = OpenNebula::Template.new_with_id(new_tid, @client)
931
-
932
- rc = new_tmpl.update(replace, true)
933
- raise if OpenNebula.is_error?(rc)
934
-
935
- return new_tid
936
-
937
- rescue
938
- # Rollback. Delete the template and the images created
939
- if !new_tid.nil?
940
- new_tmpl = OpenNebula::Template.new_with_id(new_tid, @client)
941
- new_tmpl.delete()
942
- end
780
+ return Error.new("Timeout expired for state #{state}.") unless rc
943
781
 
944
- img_ids.each do |id|
945
- img = OpenNebula::Image.new_with_id(id, @client)
946
- img.delete()
947
- end
948
-
949
- return rc
950
- end
782
+ true
951
783
  end
952
784
 
953
- def wait_state(state, timeout=10)
954
- vm_state = ""
955
- lcm_state = ""
785
+ def wait_state2(state, lcm_state, timeout=120)
786
+ extend OpenNebula::WaitExt
956
787
 
957
- timeout.times do
958
- rc = info()
959
- return rc if OpenNebula.is_error?(rc)
960
-
961
- vm_state = state_str()
962
- lcm_state = lcm_state_str()
963
-
964
- if vm_state == state
965
- return true
966
- end
788
+ rc = wait2(state, lcm_state, timeout)
967
789
 
968
- sleep 1
969
- end
790
+ return Error.new("Timeout expired for state #{state}.") unless rc
970
791
 
971
- return Error.new("Timeout expired for state #{state}. "<<
972
- "VM is in state #{vm_state}, #{lcm_state}")
792
+ true
973
793
  end
974
794
 
975
795
  private
@@ -0,0 +1,469 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # a copy of the License at #
7
+ # #
8
+ # http://www.apache.org/licenses/LICENSE-2.0 #
9
+ # #
10
+ # Unless required by applicable law or agreed to in writing, software #
11
+ # distributed under the License is distributed on an "AS IS" BASIS, #
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
13
+ # See the License for the specific language governing permissions and #
14
+ # limitations under the License. #
15
+ #--------------------------------------------------------------------------- #
16
+
17
+ require 'opennebula/template_ext'
18
+
19
+ # Module to decorate VirtualMachine class with additional helpers not directly
20
+ # exposed through the OpenNebula XMLRPC API. The extensions include
21
+ # - mp_import helper that imports a template into a marketplace
22
+ #
23
+ # rubocop:disable Style/ClassAndModuleChildren
24
+ module OpenNebula::VirtualMachineExt
25
+
26
+ def self.extend_object(obj)
27
+ if !obj.is_a?(OpenNebula::VirtualMachine)
28
+ raise StandardError, "Cannot extended #{obj.class} "\
29
+ ' with MarketPlaceAppExt'
30
+ end
31
+
32
+ class << obj
33
+
34
+ ####################################################################
35
+ # Public extended interface
36
+ ####################################################################
37
+
38
+ #-------------------------------------------------------------------
39
+ # Clones the VM's source Template, replacing the disks with live
40
+ # snapshots of the current disks. The VM capacity and NICs are also
41
+ # preserved
42
+ #
43
+ # @param name [String] Name for the new Template
44
+ # @param name [true,false,nil] Optional, true to make the saved
45
+ # images persistent, false make them non-persistent
46
+ #
47
+ # @return [Integer, OpenNebula::Error] the new Template ID in case
48
+ # of success, error otherwise
49
+ #-------------------------------------------------------------------
50
+ REMOVE_VNET_ATTRS = %w[AR_ID BRIDGE CLUSTER_ID IP MAC TARGET NIC_ID
51
+ NETWORK_ID VN_MAD SECURITY_GROUPS VLAN_ID
52
+ BRIDGE_TYPE]
53
+
54
+ REMOVE_IMAGE_ATTRS = %w[DEV_PREFIX SOURCE ORIGINAL_SIZE SIZE
55
+ DISK_SNAPSHOT_TOTAL_SIZE DRIVER IMAGE_STATE
56
+ SAVE CLONE READONLY PERSISTENT TARGET
57
+ ALLOW_ORPHANS CLONE_TARGET CLUSTER_ID
58
+ DATASTORE DATASTORE_ID DISK_ID DISK_TYPE
59
+ IMAGE_ID IMAGE IMAGE_UNAME IMAGE_UID
60
+ LN_TARGET TM_MAD TYPE OPENNEBULA_MANAGED]
61
+
62
+ def save_as_template(name, desc, opts = {})
63
+ opts = {
64
+ :persistent => false,
65
+ :poweroff => false,
66
+ :logger => nil
67
+ }.merge(opts)
68
+
69
+ img_ids = []
70
+ ntid = nil
71
+ logger = opts[:logger]
72
+ poweron = false
73
+
74
+ rc = info
75
+
76
+ raise rc.message if OpenNebula.is_error?(rc)
77
+
78
+ tid = self['TEMPLATE/TEMPLATE_ID']
79
+
80
+ raise 'VM has no associated template' unless valid?(tid)
81
+
82
+ # --------------------------------------------------------------
83
+ # Check VM state and poweroff it if needed
84
+ # --------------------------------------------------------------
85
+ if state_str != 'POWEROFF'
86
+ raise 'VM must be POWEROFF' unless opts[:poweroff]
87
+
88
+ logger.info 'Powering off VM' if logger
89
+
90
+ poweron = true
91
+
92
+ rc = poweroff
93
+
94
+ raise rc.message if OpenNebula.is_error?(rc)
95
+ end
96
+
97
+ # --------------------------------------------------------------
98
+ # Clone the source template
99
+ # --------------------------------------------------------------
100
+ vm_template = OpenNebula::Template.new_with_id(tid, @client)
101
+
102
+ ntid = vm_template.clone(name)
103
+
104
+ raise ntid.message if OpenNebula.is_error?(ntid)
105
+
106
+ # --------------------------------------------------------------
107
+ # Replace the original template's capacity with VM values
108
+ # --------------------------------------------------------------
109
+ cpu = self['TEMPLATE/CPU']
110
+ vcpu = self['TEMPLATE/VCPU']
111
+ mem = self['TEMPLATE/MEMORY']
112
+
113
+ replace = ''
114
+ replace << "DESCRIPTION = \"#{desc}\"\n" if valid?(desc)
115
+ replace << "CPU = #{cpu}\n" if valid?(cpu)
116
+ replace << "VCPU = #{vcpu}\n" if valid?(vcpu)
117
+ replace << "MEMORY = #{mem}\n" if valid?(mem)
118
+
119
+ # --------------------------------------------------------------
120
+ # Process VM DISKs
121
+ # --------------------------------------------------------------
122
+ logger.info 'Processing VM disks' if logger
123
+
124
+ each('TEMPLATE/DISK') do |disk|
125
+ # Wait for any pending operation
126
+ rc = wait_state2('POWEROFF', 'LCM_INIT')
127
+
128
+ raise rc.message if OpenNebula.is_error?(rc)
129
+
130
+ disk_id = disk['DISK_ID']
131
+ image_id = disk['IMAGE_ID']
132
+
133
+ omng = disk['OPENNEBULA_MANAGED']
134
+ type = disk['TYPE']
135
+
136
+ raise 'Missing DISK_ID' unless valid?(disk_id)
137
+
138
+ REMOVE_IMAGE_ATTRS.each do |attr|
139
+ disk.delete_element(attr)
140
+ end
141
+
142
+ # Volatile disks cannot be saved, copy definition
143
+ if !valid?(image_id)
144
+ logger.info 'Adding volatile disk' if logger
145
+
146
+ disk_str = template_like_str(
147
+ 'TEMPLATE',
148
+ true,
149
+ "DISK [ DISK_ID = #{disk_id} ]"
150
+ )
151
+
152
+ replace << "#{disk_str}\n"
153
+
154
+ next
155
+ end
156
+
157
+ # CDROM disk, copy definition
158
+ if type == 'CDROM'
159
+ logger.info 'Adding CDROM disk' if logger
160
+
161
+ replace << "DISK = [ IMAGE_ID = #{image_id}"
162
+ replace << ", OPENNEBULA_MANAGED=#{omng}" if omng
163
+ replace << " ]\n"
164
+
165
+ next
166
+ end
167
+
168
+ # Regular disk, saveas it
169
+ logger.info 'Adding and saving regular disk' if logger
170
+
171
+ ndisk_name = "#{name}-disk-#{disk_id}"
172
+
173
+ rc = disk_saveas(disk_id.to_i, ndisk_name, '', -1)
174
+
175
+ raise rc.message if OpenNebula.is_error?(rc)
176
+
177
+ if opts[:persistent]
178
+ logger.info 'Making disk persistent' if logger
179
+
180
+ nimg = OpenNebula::Image.new_with_id(rc.to_i, @client)
181
+ nimg.persistent
182
+ end
183
+
184
+ img_ids << rc.to_i
185
+
186
+ disk_tmpl = disk.template_like_str('.').tr("\n", ",\n")
187
+
188
+ replace << "DISK = [ IMAGE_ID = #{rc} "
189
+ replace << ", #{disk_tmpl}" unless disk_tmpl.empty?
190
+ replace << " ]\n"
191
+ end
192
+
193
+ # --------------------------------------------------------------
194
+ # Process VM NICs
195
+ # --------------------------------------------------------------
196
+ logger.info 'Processing VM NICs' if logger
197
+
198
+ each('TEMPLATE/NIC') do |nic|
199
+ nic_id = nic['NIC_ID']
200
+
201
+ raise 'Missing NIC_ID' unless valid?(nic_id)
202
+
203
+ REMOVE_VNET_ATTRS.each do |attr|
204
+ nic.delete_element(attr)
205
+ end
206
+
207
+ replace << 'NIC = [ '
208
+ replace << nic.template_like_str('.').tr("\n", ",\n")
209
+ replace << " ] \n"
210
+ end
211
+
212
+ # --------------------------------------------------------------
213
+ # Extra. Required by the Sunstone Cloud View
214
+ # --------------------------------------------------------------
215
+ replace << "SAVED_TEMPLATE_ID = #{tid}\n"
216
+
217
+ new_tmpl = OpenNebula::Template.new_with_id(ntid, @client)
218
+
219
+ logger.info 'Updating VM Template' if logger
220
+
221
+ rc = new_tmpl.update(replace, true)
222
+
223
+ raise rc.message if OpenNebula.is_error?(rc)
224
+
225
+ # --------------------------------------------------------------
226
+ # Resume VM if needed
227
+ # --------------------------------------------------------------
228
+ if poweron
229
+ logger.info 'Powering on VM' if logger
230
+
231
+ rc = wait_state2('POWEROFF', 'LCM_INIT')
232
+
233
+ raise rc.message if OpenNebula.is_error?(rc)
234
+
235
+ resume
236
+
237
+ rc = wait_state2('ACTIVE', 'RUNNING')
238
+
239
+ raise rc.message if OpenNebula.is_error?(rc)
240
+ end
241
+
242
+ ntid
243
+ rescue StandardError
244
+ # --------------------------------------------------------------
245
+ # Rollback. Delete the template and the images created
246
+ # --------------------------------------------------------------
247
+ if ntid
248
+ ntmpl = OpenNebula::Template.new_with_id(ntid, @client)
249
+ ntmpl.delete
250
+ end
251
+
252
+ img_ids.each do |id|
253
+ img = OpenNebula::Image.new_with_id(id, @client)
254
+ img.delete
255
+ end
256
+
257
+ raise
258
+ end
259
+
260
+ #-------------------------------------------------------------------
261
+ # Backups a VM. TODO Add final description
262
+ # @param keep [Bool]
263
+ # @param logger[Logger]
264
+ # @param binfo[Hash] Oneshot
265
+ #-------------------------------------------------------------------
266
+ def backup(keep = false, logger = nil, binfo = nil)
267
+ # --------------------------------------------------------------
268
+ # Check backup consistency
269
+ # --------------------------------------------------------------
270
+ unless binfo
271
+ rc = info
272
+ raise rc.message if OpenNebula.is_error?(rc)
273
+
274
+ binfo = backup_info
275
+ end
276
+
277
+ raise 'No backup information' if binfo.nil?
278
+
279
+ raise 'No frequency defined' unless valid?(binfo[:freq])
280
+
281
+ return if Time.now.to_i - binfo[:last].to_i < binfo[:freq].to_i
282
+
283
+ # --------------------------------------------------------------
284
+ # Save VM as new template
285
+ # --------------------------------------------------------------
286
+ logger.info 'Saving VM as template' if logger
287
+
288
+ tid = save_as_template(
289
+ binfo[:name], '', :poweroff => true, :logger => logger
290
+ )
291
+
292
+ tmp = OpenNebula::Template.new_with_id(tid, @client)
293
+ rc = tmp.info
294
+
295
+ raise rc.message if OpenNebula.is_error?(rc)
296
+
297
+ # --------------------------------------------------------------
298
+ # Import template into Marketplace & update VM info
299
+ # --------------------------------------------------------------
300
+ logger.info "Importing template #{tmp.id} to marketplace "\
301
+ "#{binfo[:market]}" if logger
302
+
303
+ tmp.extend(OpenNebula::TemplateExt)
304
+
305
+ rc, ids = tmp.mp_import(binfo[:market], true, binfo[:name],
306
+ :wait => true, :logger => logger)
307
+
308
+ raise rc.message if OpenNebula.is_error?(rc)
309
+
310
+ logger.info "Imported app ids: #{ids.join(',')}" if logger
311
+
312
+ rc = update(backup_attr(binfo, ids), true)
313
+
314
+ if OpenNebula.is_error?(rc)
315
+ raise 'Could not update the backup reference: ' \
316
+ " #{rc.message}. New backup ids are #{ids.join(',')}."
317
+ end
318
+
319
+ # --------------------------------------------------------------
320
+ # Cleanup
321
+ # --------------------------------------------------------------
322
+ logger.info "Deleting template #{tmp.id}" if logger
323
+
324
+ tmp.delete(true)
325
+
326
+ binfo[:apps].each do |id|
327
+ logger.info "Deleting applicance #{id}" if logger
328
+
329
+ papp = OpenNebula::MarketPlaceApp.new_with_id(id, @client)
330
+
331
+ papp.delete
332
+ end if !keep && binfo[:apps]
333
+ rescue Error, StandardError => e
334
+ logger.fatal(e.inspect) if logger
335
+ raise
336
+ end
337
+
338
+ #-------------------------------------------------------------------
339
+ # Restores VM information from previous backup
340
+ #
341
+ # @param datastore [Integer] Datastore ID to import app backup
342
+ # @param logger [Logger] Logger instance to print debug info
343
+ #
344
+ # @return [Integer] VM ID
345
+ #-------------------------------------------------------------------
346
+ def restore(datastore, logger = nil)
347
+ rc = info
348
+
349
+ if OpenNebula.is_error?(rc)
350
+ raise "Error getting VM: #{rc.message}"
351
+ end
352
+
353
+ logger.info 'Reading backup information' if logger
354
+
355
+ backup_ids = backup_info[:apps]
356
+
357
+ # highest (=last) of the app ids is the template id
358
+ app_id = backup_ids.last
359
+
360
+ app = OpenNebula::MarketPlaceApp.new_with_id(app_id, @client)
361
+ rc = app.info
362
+
363
+ if OpenNebula.is_error?(rc)
364
+ raise "Can not find appliance #{app_id}: #{rc.message}."
365
+ end
366
+
367
+ if logger
368
+ logger.info "Restoring VM #{self['ID']} from " \
369
+ "saved appliance #{app_id}"
370
+ end
371
+
372
+ app.extend(OpenNebula::MarketPlaceAppExt)
373
+
374
+ exp = app.export(:dsid => Integer(datastore),
375
+ :name => "#{self['NAME']} - RESTORED")
376
+
377
+ if OpenNebula.is_error?(exp)
378
+ raise "Can not restore app: #{exp.message}."
379
+ end
380
+
381
+ # Check possible errors when exporting apps
382
+ exp[:image].each do |image|
383
+ next unless OpenNebula.is_error?(image)
384
+
385
+ raise "Error restoring image: #{image.message}."
386
+ end
387
+
388
+ template = exp[:vmtemplate].first
389
+
390
+ if OpenNebula.is_error?(template)
391
+ raise "Error restoring template: #{template.message}."
392
+ end
393
+
394
+ if logger
395
+ logger.info(
396
+ "Backup restored, VM template: #{exp[:vmtemplate]}, " \
397
+ "images: #{exp[:image]}"
398
+ )
399
+
400
+ logger.info(
401
+ "Instantiating the template #{exp[:vmtemplate]}"
402
+ )
403
+ end
404
+
405
+ tmpl = OpenNebula::Template.new_with_id(template, @client)
406
+ rc = tmpl.instantiate
407
+
408
+ if OpenNebula.is_error?(rc)
409
+ raise "Can not instantiate the template: #{rc.message}."
410
+ end
411
+
412
+ rc
413
+ rescue Error, StandardError => e
414
+ logger.fatal(e.inspect) if logger
415
+ raise
416
+ end
417
+
418
+ ####################################################################
419
+ # Private extended interface
420
+ ####################################################################
421
+
422
+ private
423
+
424
+ # --------------------------------------------------------------
425
+ # Check an attribute is defined and valid
426
+ # --------------------------------------------------------------
427
+ def valid?(att)
428
+ return false if att.nil?
429
+
430
+ return !att.nil? && !att.empty? if att.is_a? String
431
+
432
+ !att.nil?
433
+ end
434
+
435
+ #-------------------------------------------------------------------
436
+ # Get backup information from the VM
437
+ #-------------------------------------------------------------------
438
+ def backup_info
439
+ base = '//USER_TEMPLATE/BACKUP'
440
+ binfo = {}
441
+
442
+ app_ids = self["#{base}/MARKETPLACE_APP_IDS"] || ''
443
+
444
+ binfo[:name] = "#{self['NAME']} - BACKUP " \
445
+ " - #{Time.now.strftime('%Y%m%d_%k%M')}"
446
+
447
+ binfo[:freq] = self["#{base}/FREQUENCY_SECONDS"]
448
+ binfo[:last] = self["#{base}/LAST_BACKUP_TIME"]
449
+ binfo[:market] = Integer(self["#{base}/MARKETPLACE_ID"])
450
+ binfo[:apps] = app_ids.split(',')
451
+
452
+ binfo
453
+ rescue StandardError
454
+ binfo
455
+ end
456
+
457
+ def backup_attr(binfo, ids)
458
+ 'BACKUP=[' \
459
+ " MARKETPLACE_APP_IDS = \"#{ids.join(',')}\"," \
460
+ " FREQUENCY_SECONDS = \"#{binfo[:freq]}\"," \
461
+ " LAST_BACKUP_TIME = \"#{Time.now.to_i}\"," \
462
+ " MARKETPLACE_ID = \"#{binfo[:market]}\" ]"
463
+ end
464
+
465
+ end
466
+ end
467
+
468
+ end
469
+ # rubocop:enable Style/ClassAndModuleChildren