Skip to content

Instantly share code, notes, and snippets.

@robbmanes
Last active September 23, 2020 13:01
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robbmanes/6cdb222b2f77227c8d75f9f9e9cb6a83 to your computer and use it in GitHub Desktop.
Save robbmanes/6cdb222b2f77227c8d75f9f9e9cb6a83 to your computer and use it in GitHub Desktop.
Join two Vmware providers in ManageIQ
# GPLv2 License
# =====================
#
# Copyright © 2018 Robb Manes <robb.manes@gmail.com>
#
# join_vmware_providers is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# join_vmware_providers is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with join_vmware_providers. If not, see <http://www.gnu.org/licenses/>.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# cd /var/www/miq/vmdb/
# bundle exec rails runner join_vmware_providers.rb --source <SRC_PROVIDER> --dest <DEST_PROVIDER>
require 'trollop'
require 'logger'
module JoinVmwareProvider
class Cli
attr_accessor :options
def parse(args, env = {})
args.shift if args.first == "--"
self.options = Trollop.options(args) do
banner "" \
"Usage: cd /var/www/miq/vmdb/" \
" bundle exec rails runner tools/join_provider_data.rb [options]"
opt :source, "Source", :type => :string
opt :destination, "Destination", :type => :string
opt :verbose, "Verbose", :type => :boolean
end
self.options = options.delete_if { |_n, v| v.blank? }
Trollop::die :source, "must specify source provider" unless self.options.key? :source
Trollop::die :destination, "must specify destination provider" unless self.options.key? :destination
self
end
def run
::JoinVmwareProvider::ProviderMerger.new(options).run
end
def self.run(args, env = {})
new.parse(args, env).run
end
end
class ProviderMerger
def initialize(args = {})
@logger = Logger.new(STDOUT)
if args.verbose
@logger.level = Logger::DEBUG
else
@logger.level = Logger::INFO
end
@src_ems = get_provider args.source
@dst_ems = get_provider args.destination
@logger.info("Using source provider #{@src_ems.name}:#{@src_ems.id} and destination provider #{@dst_ems.name}:#{@dst_ems.id}.")
end
def get_provider(provider_string)
unless provider_string.scan(/\D/).empty?
ems = ManageIQ::Providers::Vmware::InfraManager.find_by(:name => provider_string)
else
ems = ManageIQ::Providers::Vmware::InfraManager.find_by(:id => provider_string)
end
if ems.nil?
@logger.error("Found no EMS for id or string #{provider_string}.")
exit
end
@logger.info("Found provider #{ems.name} with id #{ems.id}.")
ems
end
def run()
merge_duplicate_vms
merge_duplicate_hosts
merge_duplicate_clusters
end
def merge_duplicate_vms
src_vms = @src_ems.vms
dst_vms = @dst_ems.vms
@logger.debug("Found #{src_vms.count} VM's on source provider and #{dst_vms.count} VM's on destination provider.")
@logger.info("Merging duplicate VM data...")
dups = 0
dst_vms.each do |dst_vm|
src_vm = src_vms.find_by(:name => dst_vm.name)
unless src_vm.nil?
@logger.debug("Beginning VM merge of #{dst_vm.name}...")
@logger.debug("Merging metrics for VM #{dst_vm.name}...")
src_metrics = src_vm.metrics.all
src_metrics.each do |metric|
dst_vm.metrics << metric
end
@logger.debug("Merging tags for VM #{dst_vm.name}...")
src_tags = src_vm.tags.all
src_tags.each do |tag|
dst_vm.tags << tag
end
@logger.debug("Updating EVM owner for VM #{dst_vm.name}...")
dst_vm.update(:evm_owner => src_vm.evm_owner)
@logger.debug("Merging groups for VM #{dst_vm.name}...")
dst_vm.update(:miq_group => src_vm.miq_group)
@logger.debug("Updating retirement settings for VM #{dst_vm.name}...")
dst_vm.retirement_state = src_vm.retirement_state
dst_vm.retires_on = src_vm.retires_on
dst_vm.save
dups = dups + 1
@logger.debug("Finished merge for VM #{dst_vm.name}...")
end
end
@logger.info("Merged #{dups} duplicate VM's.")
end
def merge_duplicate_hosts
src_hosts = @src_ems.hosts
dst_hosts = @dst_ems.hosts
@logger.debug("Found #{src_hosts.count} hosts on source provider and #{dst_hosts.count} hosts on destination provider.")
@logger.info("Merging duplicate hosts data...")
dups = 0
dst_hosts.each do |dst_vm|
src_host = src_hosts.find_by(:name => dst_host.name)
unless src_host.nil?
@logger.debug("Beginning host merge of #{dst_host.name}")
@logger.debug("Merging metrics for host #{dst_host.name}")
src_metrics = src_host.metrics.all
src_metrics.each do |metric|
dst_host.metrics << metric
end
@logger.debug("Merging tags for host #{dst_host.name}")
src_tags = src_host.tags.all
src_tags.each do |tag|
dst_host.tags << tag
end
dups = dups + 1
@logger.debug("Finished merge for host #{dst_host.name}...")
end
end
@logger.info("Merged #{dups} duplicate hosts.")
end
def merge_duplicate_clusters
src_clusters = @src_ems.clusters
dst_clusters = @dst_ems.clusters
@logger.debug("Found #{src_clusters.count} clusters on source provider and #{dst_clusters.count} clusters on destination provider.")
@logger.info("Merging duplicate cluster data...")
dups = 0
dst_clusters.each do |dst_cluster|
src_cluster = src_clusters.find_by(:name => dst_cluster.name)
unless src_cluster.nil?
@logger.debug("Beginning cluster merge of #{dst_cluster.name}")
@logger.debug("Merging metrics for cluster #{dst_cluster.name}")
src_metrics = src_cluster.metrics.all
src_metrics.each do |metric|
dst_cluster.metrics << metric
end
@logger.debug("Merging tags for cluster #{dst_cluster.name}")
src_tags = src_cluster.tags.all
src_tags.each do |tag|
dst_cluster.tags << tag
end
dups = dups + 1
@logger.debug("Finished merge for cluster #{dst_cluster.name}...")
end
end
@logger.info("Merged #{dups} duplicate clusters.")
end
end
end
JoinVmwareProvider::Cli.run(ARGV, ENV)
@wddossett
Copy link

Rob, I am looking for a script to help with migrating from one vmware provider to another. I am migrating from our old infrastructure with windows vcenter 6.0 to a new VXrail with vcenter appliance 6.5 and I am trying to retain all the ownership and retirement info in CloudForms. Someone suggested this script might help, but I am struggling to understand it. If you have any further info, I would appreciate any guidance at all.

@robbmanes
Copy link
Author

robbmanes commented Jul 23, 2018

Hi @wddossett; the script should be placed in the manageiq directory and run as bundle exec rails runner join_vmware_providers.rb --source <SRC_PROVIDER> --dest <DEST_PROVIDER>, where you can either give the provider names or their ID's in MIQ. After that, it should tell you each step of the operation, merging specifically now metrics, tags, ownership, and retirement for each applicable item (VM's, hosts, clusters) between two VCenters of theoretically any versions. This is not very well tested at the moment, hence the disclaimer, and unsupported, but it has proven to work a few times for various individuals doing what you're doing. If it doesn't in some way, let me know and I'll see if I can address the issue here in the Gist.

EDIT: Concerning retirement states, it moves those, but not much else. Retirement dates might be off, I can see if that can be added, but metrics, ownership, and tags seem to work as expected. I'd have to look up each retirement attribute to move over individually, which might take me a bit.

@yasirsharif
Copy link

Hi @robbmanes,
I am facing the below error, appreciate your kind looking at it:
I have already installed the required 'trollop' and 'logger' but still getting these errors.

[root@drmdcvmiq vmdb]# bundle exec rails runner join_vmware_providers.rb --source aeadsvcr01.wrm.ae  --dest drmdcvvc01.wrm.ae
Traceback (most recent call last):
        14: from bin/rails:4:in `<main>'
        13: from bin/rails:4:in `require'
        12: from /usr/local/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/commands.rb:16:in `<top (required)>'
        11: from /usr/local/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/command.rb:44:in `invoke'
        10: from /usr/local/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/command/base.rb:63:in `perform'
         9: from /usr/local/lib/ruby/gems/2.5.0/gems/thor-1.0.1/lib/thor.rb:392:in `dispatch'
         8: from /usr/local/lib/ruby/gems/2.5.0/gems/thor-1.0.1/lib/thor/invocation.rb:127:in `invoke_command'
         7: from /usr/local/lib/ruby/gems/2.5.0/gems/thor-1.0.1/lib/thor/command.rb:27:in `run'
         6: from /usr/local/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/commands/runner/runner_command.rb:34:in `perform'
         5: from /usr/local/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/commands/runner/runner_command.rb:34:in `load'
         4: from join_vmware_providers.rb:31:in `<top (required)>'
         3: from /usr/local/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/dependencies.rb:292:in `require'
         2: from /usr/local/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/dependencies.rb:258:in `load_dependency'
         1: from /usr/local/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/dependencies.rb:292:in `block in require'
/usr/local/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/dependencies.rb:292:in `require': cannot load such file -- trollop (LoadError)
[root@drmdcvmiq vmdb]#

@robbmanes
Copy link
Author

I haven't used this script since the beginning of 2019, and don't know if something changed in CloudForms that might cause a dependency error.

From the output, it looks like perhaps CloudForms doesn't use trollop anymore and the gem isn't present:

/usr/local/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/dependencies.rb:292:in `require': cannot load such file -- trollop (LoadError)

You might be able to gem install trollop to fix that, but I am not sure of the level of support you can expect from such a thing in terms of support. Otherwise I'd have to rewrite the script without trollop, using some other command-line manager, and I haven't been working with Ruby for some time so I'd have to dive into what's A) current B) included on CloudForms appliances now.

@robbmanes
Copy link
Author

The trollop gem was switched out for the optimist gem, because, rightfully so, it's pretty offensive. Anything on CFME >= 5.0 won't work with the above script.

Changelog in Red Hat documentation is available here.

I wrote this for CFME 4.5 and haven't kept it updated, so it might be possible it doesn't work with the intended consequences anymore as well. You're welcome to try and switch out trollop for optimist where applicable, and if someone verifies that that's all that needs to be done (I no longer work on CloudForms) then I can update it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment