Skip to content

Commit c53a6fe

Browse files
author
Miha Pleško
committed
Basic cloud inventory
With this commit we enhance cloud manager so that it now inventories a couple of the most basic entities: flavors, availability zones and vms. Signed-off-by: Miha Pleško <miha.plesko@xlab.si>
1 parent e32c3d5 commit c53a6fe

File tree

12 files changed

+1113
-42
lines changed

12 files changed

+1113
-42
lines changed

app/models/manageiq/providers/azure_stack/cloud_manager/vm.rb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
class ManageIQ::Providers::AzureStack::CloudManager::Vm < ManageIQ::Providers::CloudManager::Vm
22

3+
POWER_STATES = {
4+
'PowerState/running' => 'on',
5+
'PowerState/starting' => 'powering_up',
6+
'PowerState/stopped' => 'suspended',
7+
'PowerState/stopping' => 'suspending',
8+
'PowerState/deallocated' => 'off',
9+
'PowerState/deallocating' => 'powering_down',
10+
'PowerState/unknown' => 'unknown'
11+
}.freeze
12+
313
def provider_object(connection = nil)
414
connection ||= ext_management_system.connect
515
# find vm instance via connection and return it
@@ -32,10 +42,8 @@ def raw_suspend
3242
update_attributes!(:raw_power_state => "suspended")
3343
end
3444

35-
# TODO: this method could be the default in a baseclass
3645
def self.calculate_power_state(raw_power_state)
37-
# do some mapping on powerstates
38-
# POWER_STATES[raw_power_state.to_s] || "terminated"
39-
raw_power_state
46+
# https://docs.microsoft.com/en-us/azure/virtual-machines/windows/states-lifecycle
47+
POWER_STATES[raw_power_state.to_s] || 'unknown'
4048
end
4149
end
Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,42 @@
11
class ManageIQ::Providers::AzureStack::Inventory::Collector < ManageIQ::Providers::Inventory::Collector
22
require_nested :CloudManager
33

4+
def initialize(manager, refresh_target)
5+
super(manager, refresh_target)
6+
@token = nil
7+
end
8+
49
def azure_resources
5-
@azure_resources ||= manager.connect
10+
with_shared_token { |token| @azure_resources ||= manager.connect(:token => token) }
11+
end
12+
13+
def azure_compute
14+
with_shared_token { |token| @azure_compute ||= manager.connect(:token => token, :service => :Compute) }
615
end
716

817
def azure_network
9-
@azure_network ||= manager.connect(:service => :Network)
18+
with_shared_token { |token| @azure_network ||= manager.connect(:token => token, :service => :Network) }
19+
end
20+
21+
def with_shared_token
22+
client = yield @token
23+
@token ||= client.credentials
24+
client
25+
end
26+
27+
def resource_group_name(ems_ref)
28+
if (match = ems_ref.match(%r{/subscriptions/[^/]+/resourceGroups/(?<name>[^/]+)/.+}))
29+
match[:name].downcase
30+
end
31+
end
32+
33+
def resource_group_id(ems_ref)
34+
if (match = ems_ref.match(%r{(?<id>/subscriptions/[^/]+/resourceGroups/[^/]+)/.+}))
35+
match[:id].downcase
36+
end
37+
end
38+
39+
def raw_power_state(instance_view)
40+
instance_view&.statuses&.detect { |s| s.code.start_with?('PowerState/') }&.code
1041
end
1142
end

app/models/manageiq/providers/azure_stack/inventory/collector/cloud_manager.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,22 @@ class ManageIQ::Providers::AzureStack::Inventory::Collector::CloudManager < Mana
22
def resource_groups
33
azure_resources.resource_groups.list
44
end
5+
6+
def flavors
7+
azure_compute.virtual_machine_sizes.list(manager.provider_region).value
8+
end
9+
10+
def vms
11+
if azure_compute.respond_to?(:instance_view)
12+
$azure_stack_log.debug("Fetching VMs, then fetching instance view for each")
13+
azure_compute.virtual_machines.list_all.each do |vm|
14+
vm.instance_view = azure_compute.virtual_machines.instance_view(resource_group_name(vm.id), vm.name)
15+
end
16+
else
17+
$azure_stack_log.debug("Fetching VM ids, then fetching full data for each")
18+
azure_resources.resources.list(:filter => "resourceType eq 'Microsoft.Compute/virtualMachines'").map do |vm|
19+
azure_compute.virtual_machines.get(resource_group_name(vm.id), vm.name, :expand => 'instanceView')
20+
end
21+
end
22+
end
523
end

app/models/manageiq/providers/azure_stack/inventory/parser/cloud_manager.rb

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ def parse
44
$azure_stack_log.info("#{log_header}...")
55

66
resource_groups
7+
availability_zones
8+
flavors
9+
vms
710

811
$azure_stack_log.info("#{log_header}...Complete")
912
end
@@ -16,4 +19,86 @@ def resource_groups
1619
)
1720
end
1821
end
22+
23+
def availability_zones
24+
persister.availability_zones.build(
25+
:ems_ref => 'default',
26+
:name => persister.manager.name
27+
)
28+
end
29+
30+
def flavors
31+
collector.flavors.each do |flavor|
32+
persister.flavors.build(
33+
:ems_ref => flavor.name.downcase,
34+
:name => flavor.name,
35+
:cpus => flavor.number_of_cores,
36+
:cpu_cores => flavor.number_of_cores / vcpus_per_socket(flavor.name),
37+
:memory => flavor.memory_in_mb.megabytes,
38+
:root_disk_size => flavor.os_disk_size_in_mb.megabytes,
39+
:swap_disk_size => flavor.resource_disk_size_in_mb.megabytes,
40+
:enabled => true
41+
)
42+
end
43+
end
44+
45+
def vms
46+
collector.vms.each do |vm|
47+
uid = vm.id.downcase
48+
flavor_ref = vm.hardware_profile.vm_size.downcase
49+
50+
power_state = 'unknown' unless (power_state = collector.raw_power_state(vm.instance_view))
51+
52+
vm_obj = persister.vms.build(
53+
:name => vm.name,
54+
:ems_ref => uid,
55+
:uid_ems => uid,
56+
:vendor => 'azure_stack',
57+
:connection_state => 'connected',
58+
:raw_power_state => power_state,
59+
:location => vm.location,
60+
:availability_zone => persister.availability_zones.lazy_find('default'),
61+
:resource_group => persister.resource_groups.lazy_find(collector.resource_group_id(vm.id)),
62+
:flavor => persister.flavors.lazy_find(flavor_ref)
63+
)
64+
65+
persister.operating_systems.build(
66+
:vm_or_template => vm_obj,
67+
:product_name => guest_os(vm)
68+
)
69+
70+
persister.hardwares.build(
71+
:vm_or_template => vm_obj,
72+
:cpu_sockets => persister.flavors.lazy_find(flavor_ref, :key => :cpu_cores),
73+
:cpu_total_cores => persister.flavors.lazy_find(flavor_ref, :key => :cpus),
74+
:memory_mb => persister.flavors.find(flavor_ref).memory,
75+
:disk_capacity => persister.flavors.lazy_find(flavor_ref, :key => :swap_disk_size)
76+
)
77+
end
78+
end
79+
80+
# Fetch full OS info from image (e.g. 'UbuntuServer 16.04 LTS'), fallback to basic info (e.g. 'Linux').
81+
def guest_os(vm)
82+
if (image_reference = vm.storage_profile&.image_reference) && image_reference&.offer
83+
"#{image_reference.offer} #{image_reference.sku.tr('-', ' ')}"
84+
else
85+
vm.storage_profile.os_disk.os_type
86+
end
87+
end
88+
89+
def vcpus_per_socket(flavor_name)
90+
# https://docs.microsoft.com/en-us/azure/virtual-machines/windows/acu
91+
# Ratio 1 means each socket/core contains one vCPU
92+
# 2 means each socket/core contains two vCPUs
93+
case flavor_name.to_s
94+
when /_(D_v3|Ds_v3|E_v3|Es_v3|M)$/
95+
2
96+
when /_F\d+s_v2$/
97+
2
98+
when /_L\d+s_v2$/
99+
2
100+
else
101+
1
102+
end
103+
end
19104
end

app/models/manageiq/providers/azure_stack/inventory/persister/definitions/cloud_collections.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@ module ManageIQ::Providers::AzureStack::Inventory::Persister::Definitions::Cloud
22
extend ActiveSupport::Concern
33

44
def initialize_cloud_inventory_collections
5+
%i[
6+
availability_zones
7+
hardwares
8+
operating_systems
9+
flavors
10+
vms
11+
].each do |name|
12+
add_collection(cloud, name)
13+
end
14+
515
add_resource_groups
616
end
717

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
module ManageIQ::Providers
2-
class AzureStack::NetworkManager::Refresher < ManageIQ::Providers::BaseManager::ManagerRefresher
2+
class AzureStack::NetworkManager::Refresher < ManageIQ::Providers::BaseManager::Refresher
33
end
44
end

spec/models/manageiq/providers/azure_stack/cloud_manager/vcr_specs/refresher_spec.rb

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
1+
#
2+
# This inventory test comes with Azure deployment JSON which makes it reproducible on any Azure Stack.
3+
# VCR cassettes were recorded against a fresh subscription with nothing but that JSON deployed.
4+
# Please make sure tests stay in sync with the JSON - don't ever manually update VCR cassettes.
5+
# Instead, modify the JSON and deploy it and then just re-record the cassettes.
6+
#
7+
# The JSON deployment is here:
8+
# ../vcr_fixtures/full-refresh-deployment.json
9+
#
10+
111
describe ManageIQ::Providers::AzureStack::CloudManager::Refresher do
212
supported_api_versions do |api_version|
313
before do
414
stub_settings_merge(:ems_refresh => { :azure_stack => refresh_settings }) if refresh_settings
515
end
616

717
let(:resource_group) { ResourceGroup.find_by(:name => 'demo-resource-group') }
18+
let(:zone) { AvailabilityZone.find_by(:ems_ref => 'default') }
19+
let(:vm) { Vm.find_by(:name => 'demoVm') }
820
let(:security_group) { SecurityGroup.find_by(:name => 'demoSecurityGroup') }
21+
let(:flavor) { Flavor.find_by(:ems_ref => 'standard_a1') }
22+
923
let(:saving_strategy) { :recursive }
1024
let(:saver_strategy) { :default }
1125
let(:use_ar) { true }
@@ -48,12 +62,18 @@ def full_refresh_twice
4862
def assert_inventory
4963
assert_table_counts
5064
assert_resource_group
65+
assert_availability_zone
66+
assert_specific_flavor
67+
assert_specific_vm
5168
assert_security_group
5269
end
5370

5471
def assert_table_counts
5572
expect(ExtManagementSystem.count).to eq(1 + 1) # cloud + network manager
5673
expect(ResourceGroup.count).to eq(1)
74+
expect(AvailabilityZone.count).to eq(1)
75+
expect(Vm.count).to eq(1)
76+
expect(Flavor.count).to eq(70)
5777
expect(SecurityGroup.count).to eq(1)
5878
end
5979

@@ -62,6 +82,53 @@ def assert_resource_group
6282
expect(ems_ref_suffix(resource_group.ems_ref)).to eq('') # prefix is actually resource group ems_ref
6383
end
6484

85+
def assert_availability_zone
86+
expect(zone).not_to be_nil
87+
expect(zone.name).to eq(ems.name)
88+
end
89+
90+
def assert_specific_flavor
91+
expect(flavor).not_to be_nil
92+
expect(flavor).to have_attributes(
93+
:ems_ref => 'standard_a1',
94+
:name => 'Standard_A1',
95+
:cpus => 1,
96+
:cpu_cores => 1,
97+
:memory => 1.75.gigabytes.round,
98+
:root_disk_size => 1023.gigabytes.round,
99+
:swap_disk_size => 70.gigabytes.round,
100+
:enabled => true
101+
)
102+
end
103+
104+
def assert_specific_vm
105+
expect(vm).not_to be_nil
106+
expect(ems_ref_suffix(vm.ems_ref)).to match(%r{^/providers/microsoft.compute/virtualmachines/[^/]+$})
107+
expect(ems_ref_suffix(vm.uid_ems)).to match(%r{^/providers/microsoft.compute/virtualmachines/[^/]+$})
108+
109+
expect(vm).to have_attributes(
110+
:vendor => 'azure_stack',
111+
:connection_state => 'connected',
112+
:raw_power_state => 'PowerState/running',
113+
:power_state => 'on',
114+
:location => 'westus',
115+
:availability_zone => zone,
116+
:resource_group => resource_group,
117+
:flavor => flavor
118+
)
119+
120+
expect(vm.operating_system).not_to be_nil
121+
expect(vm.operating_system.product_name).to eq('UbuntuServer 16.04 LTS')
122+
123+
expect(vm.hardware).not_to be_nil
124+
expect(vm.hardware).to have_attributes(
125+
:cpu_sockets => 1,
126+
:cpu_total_cores => 1,
127+
:memory_mb => 1.75.gigabytes.round,
128+
:disk_capacity => 70.gigabytes.round
129+
)
130+
end
131+
65132
def assert_security_group
66133
expect(security_group).not_to be_nil
67134
expect(ems_ref_suffix(security_group.ems_ref)).to match(%r{^/providers/microsoft.network/networksecuritygroups/[^/]+$})

spec/spec_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
def vcr_with_auth(casette)
2626
VCR.use_cassette('obtain_endpoints', :allow_unused_http_interactions => true) do
2727
VCR.use_cassette('obtain_token', :allow_playback_repeats => false, :allow_unused_http_interactions => true) do
28-
VCR.use_cassette(casette, :allow_unused_http_interactions => false) do
28+
VCR.use_cassette(casette, :allow_unused_http_interactions => true) do
2929
yield
3030
end
3131
end

spec/vcr_cassettes/manageiq/providers/azure_stack/cloud_manager/refresher/V2017_03_09-network.yml

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)