opennebula 5.12.9 → 6.0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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