diff --git a/Anvil/Tools/DRBD.pm b/Anvil/Tools/DRBD.pm index 669e23e0..12cd435d 100644 --- a/Anvil/Tools/DRBD.pm +++ b/Anvil/Tools/DRBD.pm @@ -551,7 +551,11 @@ This calls C<< drbdadm >> to collect the configuration of the local system and p On error, C<< 1 >> is returned. On success, C<< 0 >> is returned. -This method takes no parameters. +Parameters; + +=head3 xml (optional) + +If set to the XML generated by C<< drbdadm dump-xml >> elsewhere, this will be parsed instead of making the call. =cut sub gather_data @@ -562,220 +566,230 @@ sub gather_data my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "DRBD->gather_data()" }}); + ### NOTE: Left off here - take an XML and parse that instead of collecting it, if passed. + my $xml = defined $parameter->{xml} ? $parameter->{xml} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + xml => $xml, + }}); + # Is DRBD even installed? - if (not -e $anvil->data->{path}{exe}{drbdadm}) + if (not $xml) { - # This is an error, but it happens a lot because we're called by scan_drbd from Striker - # dashboards often. As such, this log level is '2'. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "error_0251"}); - return(1); + if (not -e $anvil->data->{path}{exe}{drbdadm}) + { + # This is an error, but it happens a lot because we're called by scan_drbd from Striker + # dashboards often. As such, this log level is '2'. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "error_0251"}); + return(1); + } + + ($xml, my $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{drbdadm}." dump-xml"}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + xml => $xml, + return_code => $return_code, + }}); + if ($return_code) + { + # Failed to dump the XML. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "error_0252", variables => { return_code => $return_code }}); + return(1); + } } - my ($drbd_xml, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{drbdadm}." dump-xml"}); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { drbd_xml => $drbd_xml, return_code => $return_code }}); - if ($return_code) + local $@; + my $dom = eval { XML::LibXML->load_xml(string => $xml); }; + if ($@) { - # Failed to dump the XML. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "error_0252", variables => { return_code => $return_code }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "error_0253", variables => { + xml => $xml, + error => $@, + }}); return(1); } else { - local $@; - my $dom = eval { XML::LibXML->load_xml(string => $drbd_xml); }; - if ($@) + # Successful parse! + ### TODO: Might be best to config these default values by calling/parsing + ### 'drbdsetup show --show-defaults'. + $anvil->data->{new}{scan_drbd}{scan_drbd_common_xml} = $xml; + $anvil->data->{new}{scan_drbd}{scan_drbd_flush_disk} = 1; + $anvil->data->{new}{scan_drbd}{scan_drbd_flush_md} = 1; + $anvil->data->{new}{scan_drbd}{scan_drbd_timeout} = 6; # Default is '60', 6 seconds + $anvil->data->{new}{scan_drbd}{scan_drbd_total_sync_speed} = 0; + + foreach my $name ($dom->findnodes('/config/common/section')) { - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "error_0253", variables => { - xml => $drbd_xml, - error => $@, - }}); - return(1); + my $section = $name->{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { section => $section }}); + foreach my $option_name ($name->findnodes('./option')) + { + my $variable = $option_name->{name}; + my $value = $option_name->{value}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:variable' => $variable, + 's2:value' => $value, + }}); + + if ($section eq "net") + { + if ($variable eq "timeout") + { + $value /= 10; + $anvil->data->{new}{scan_drbd}{scan_drbd_timeout} = ($value / 10); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "new::scan_drbd::scan_drbd_timeout" => $anvil->data->{new}{scan_drbd}{scan_drbd_timeout}, + }}); + } + } + if ($section eq "disk") + { + if ($variable eq "disk-flushes") + { + $anvil->data->{new}{scan_drbd}{scan_drbd_flush_disk} = $value eq "no" ? 0 : 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "new::scan_drbd::scan_drbd_flush_disk" => $anvil->data->{new}{scan_drbd}{scan_drbd_flush_disk}, + }}); + } + if ($variable eq "md-flushes") + { + $anvil->data->{new}{scan_drbd}{scan_drbd_flush_md} = $value eq "no" ? 0 : 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "new::scan_drbd::scan_drbd_flush_md" => $anvil->data->{new}{scan_drbd}{scan_drbd_flush_md}, + }}); + } + } + } } - else + + foreach my $name ($dom->findnodes('/config/resource')) { - # Successful parse! - ### TODO: Might be best to config these default values by calling/parsing - ### 'drbdsetup show --show-defaults'. - $anvil->data->{new}{scan_drbd}{scan_drbd_common_xml} = $drbd_xml; - $anvil->data->{new}{scan_drbd}{scan_drbd_flush_disk} = 1; - $anvil->data->{new}{scan_drbd}{scan_drbd_flush_md} = 1; - $anvil->data->{new}{scan_drbd}{scan_drbd_timeout} = 6; # Default is '60', 6 seconds - $anvil->data->{new}{scan_drbd}{scan_drbd_total_sync_speed} = 0; + my $resource = $name->{name}; + my $conf_file = $name->{'conf-file-line'}; + $conf_file =~ s/:\d+$//; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 's1:resource' => $resource, + 's2:conf_file' => $conf_file, + }}); - foreach my $name ($dom->findnodes('/config/common/section')) + $anvil->data->{new}{resource}{$resource}{up} = 0; + $anvil->data->{new}{resource}{$resource}{xml} = $name->toString; + $anvil->data->{new}{resource}{$resource}{config_file} = $conf_file; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "new::resource::${resource}::xml" => $anvil->data->{new}{resource}{$resource}{xml}, + "new::resource::${resource}::config_file" => $anvil->data->{new}{resource}{$resource}{config_file}, + }}); + + foreach my $host ($name->findnodes('./host')) { - my $section = $name->{name}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { section => $section }}); - foreach my $option_name ($name->findnodes('./option')) + my $this_host_name = $host->{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_host_name => $this_host_name }}); + + # Record the details under the hosts + foreach my $volume_vnr ($host->findnodes('./volume')) { - my $variable = $option_name->{name}; - my $value = $option_name->{value}; + my $volume = $volume_vnr->{vnr}; + my $meta_disk = $volume_vnr->findvalue('./meta-disk'); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - 's1:variable' => $variable, - 's2:value' => $value, + 's1:volume' => $volume, + 's2:meta_disk' => $meta_disk, }}); - - if ($section eq "net") - { - if ($variable eq "timeout") - { - $value /= 10; - $anvil->data->{new}{scan_drbd}{scan_drbd_timeout} = ($value / 10); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "new::scan_drbd::scan_drbd_timeout" => $anvil->data->{new}{scan_drbd}{scan_drbd_timeout}, - }}); - } - } - if ($section eq "disk") + + $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{device_path} = $volume_vnr->findvalue('./device'); + $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{backing_disk} = $volume_vnr->findvalue('./disk'); + $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{device_minor} = $volume_vnr->findvalue('./device/@minor'); + $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{size} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "s1:new::resource::${resource}::host::${this_host_name}::volume::${volume}::device_path" => $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{device_path}, + "s2:new::resource::${resource}::host::${this_host_name}::volume::${volume}::backing_disk" => $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{backing_disk}, + "s3:new::resource::${resource}::host::${this_host_name}::volume::${volume}::device_minor" => $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{device_minor}, + }}); + + # Record the local data only. + if (($this_host_name eq $anvil->Get->host_name) or ($this_host_name eq $anvil->Get->short_host_name)) { - if ($variable eq "disk-flushes") - { - $anvil->data->{new}{scan_drbd}{scan_drbd_flush_disk} = $value eq "no" ? 0 : 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "new::scan_drbd::scan_drbd_flush_disk" => $anvil->data->{new}{scan_drbd}{scan_drbd_flush_disk}, - }}); - } - if ($variable eq "md-flushes") - { - $anvil->data->{new}{scan_drbd}{scan_drbd_flush_md} = $value eq "no" ? 0 : 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "new::scan_drbd::scan_drbd_flush_md" => $anvil->data->{new}{scan_drbd}{scan_drbd_flush_md}, - }}); - } + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{device_path} = $volume_vnr->findvalue('./device'); + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{backing_disk} = $volume_vnr->findvalue('./disk'); + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{device_minor} = $volume_vnr->findvalue('./device/@minor'); + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{size} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "s1:new::resource::${resource}::volume::${volume}::device_path" => $anvil->data->{new}{resource}{$resource}{volume}{$volume}{device_path}, + "s2:new::resource::${resource}::volume::${volume}::backing_disk" => $anvil->data->{new}{resource}{$resource}{volume}{$volume}{backing_disk}, + "s3:new::resource::${resource}::volume::${volume}::device_minor" => $anvil->data->{new}{resource}{$resource}{volume}{$volume}{device_minor}, + }}); } } } - foreach my $name ($dom->findnodes('/config/resource')) + foreach my $connection ($name->findnodes('./connection')) { - my $resource = $name->{name}; - my $conf_file = $name->{'conf-file-line'}; - $conf_file =~ s/:\d+$//; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - 's1:resource' => $resource, - 's2:conf_file' => $conf_file, - }}); - - $anvil->data->{new}{resource}{$resource}{up} = 0; - $anvil->data->{new}{resource}{$resource}{xml} = $name->toString; - $anvil->data->{new}{resource}{$resource}{config_file} = $conf_file; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "new::resource::${resource}::xml" => $anvil->data->{new}{resource}{$resource}{xml}, - "new::resource::${resource}::config_file" => $anvil->data->{new}{resource}{$resource}{config_file}, - }}); - - foreach my $host ($name->findnodes('./host')) + my $peer = ""; + foreach my $host ($connection->findnodes('./host')) { my $this_host_name = $host->{name}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_host_name => $this_host_name }}); - # Record the details under the hosts - foreach my $volume_vnr ($host->findnodes('./volume')) + next if (($this_host_name eq $anvil->Get->host_name) or ($this_host_name eq $anvil->Get->short_host_name)); + + $peer = $this_host_name; + $anvil->data->{new}{resource}{$resource}{peer}{$peer}{peer_ip_address} = $host->findvalue('./address'); + $anvil->data->{new}{resource}{$resource}{peer}{$peer}{tcp_port} = $host->findvalue('./address/@port');; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "s1:new::resource::${resource}::peer::${peer}::peer_ip_address" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{peer_ip_address}, + "s2:new::resource::${resource}::peer::${peer}::tcp_port" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{tcp_port}, + }}); + + if (not exists $anvil->data->{new}{resource}{$resource}{peer}{$peer}{protocol}) { - my $volume = $volume_vnr->{vnr}; - my $meta_disk = $volume_vnr->findvalue('./meta-disk'); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - 's1:volume' => $volume, - 's2:meta_disk' => $meta_disk, - }}); - - $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{device_path} = $volume_vnr->findvalue('./device'); - $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{backing_disk} = $volume_vnr->findvalue('./disk'); - $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{device_minor} = $volume_vnr->findvalue('./device/@minor'); - $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{size} = 0; + $anvil->data->{new}{resource}{$resource}{peer}{$peer}{protocol} = "unknown"; + $anvil->data->{new}{resource}{$resource}{peer}{$peer}{fencing} = "unknown"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "s1:new::resource::${resource}::host::${this_host_name}::volume::${volume}::device_path" => $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{device_path}, - "s2:new::resource::${resource}::host::${this_host_name}::volume::${volume}::backing_disk" => $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{backing_disk}, - "s3:new::resource::${resource}::host::${this_host_name}::volume::${volume}::device_minor" => $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{device_minor}, + "s1:new::resource::${resource}::peer::${peer}::protocol" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{protocol}, + "s2:new::resource::${resource}::peer::${peer}::fencing" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{fencing}, }}); - - # Record the local data only. - if (($this_host_name eq $anvil->Get->host_name) or ($this_host_name eq $anvil->Get->short_host_name)) - { - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{device_path} = $volume_vnr->findvalue('./device'); - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{backing_disk} = $volume_vnr->findvalue('./disk'); - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{device_minor} = $volume_vnr->findvalue('./device/@minor'); - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{size} = 0; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "s1:new::resource::${resource}::volume::${volume}::device_path" => $anvil->data->{new}{resource}{$resource}{volume}{$volume}{device_path}, - "s2:new::resource::${resource}::volume::${volume}::backing_disk" => $anvil->data->{new}{resource}{$resource}{volume}{$volume}{backing_disk}, - "s3:new::resource::${resource}::volume::${volume}::device_minor" => $anvil->data->{new}{resource}{$resource}{volume}{$volume}{device_minor}, - }}); - } + } + + foreach my $volume (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}{$resource}{volume}}) + { + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{connection_state} = "disconnected"; + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{local_disk_state} = "down"; + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{peer_disk_state} = "unknown"; + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{local_role} = "down"; + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{peer_role} = "unknown"; + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{out_of_sync_size} = -1; + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{replication_speed} = 0; + $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{estimated_time_to_sync} = 0; } } - foreach my $connection ($name->findnodes('./connection')) + foreach my $name ($connection->findnodes('./section')) { - my $peer = ""; - foreach my $host ($connection->findnodes('./host')) + my $section = $name->{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { section => $section }}); + + foreach my $option_name ($name->findnodes('./option')) { - my $this_host_name = $host->{name}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_host_name => $this_host_name }}); - - next if (($this_host_name eq $anvil->Get->host_name) or ($this_host_name eq $anvil->Get->short_host_name)); - - $peer = $this_host_name; - $anvil->data->{new}{resource}{$resource}{peer}{$peer}{peer_ip_address} = $host->findvalue('./address'); - $anvil->data->{new}{resource}{$resource}{peer}{$peer}{tcp_port} = $host->findvalue('./address/@port');; + my $variable = $option_name->{name}; + my $value = $option_name->{value}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "s1:new::resource::${resource}::peer::${peer}::peer_ip_address" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{peer_ip_address}, - "s2:new::resource::${resource}::peer::${peer}::tcp_port" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{tcp_port}, + 's1:variable' => $variable, + 's2:value' => $value, }}); - - if (not exists $anvil->data->{new}{resource}{$resource}{peer}{$peer}{protocol}) - { - $anvil->data->{new}{resource}{$resource}{peer}{$peer}{protocol} = "unknown"; - $anvil->data->{new}{resource}{$resource}{peer}{$peer}{fencing} = "unknown"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "s1:new::resource::${resource}::peer::${peer}::protocol" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{protocol}, - "s2:new::resource::${resource}::peer::${peer}::fencing" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{fencing}, - }}); - } - - foreach my $volume (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}{$resource}{volume}}) - { - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{connection_state} = "disconnected"; - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{local_disk_state} = "down"; - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{peer_disk_state} = "unknown"; - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{local_role} = "down"; - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{peer_role} = "unknown"; - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{out_of_sync_size} = -1; - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{replication_speed} = 0; - $anvil->data->{new}{resource}{$resource}{volume}{$volume}{peer}{$peer}{estimated_time_to_sync} = 0; - } - } - - foreach my $name ($connection->findnodes('./section')) - { - my $section = $name->{name}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { section => $section }}); - - foreach my $option_name ($name->findnodes('./option')) - { - my $variable = $option_name->{name}; - my $value = $option_name->{value}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - 's1:variable' => $variable, - 's2:value' => $value, - }}); - if ($section eq "net") + if ($section eq "net") + { + if ($variable eq "protocol") { - if ($variable eq "protocol") - { - $anvil->data->{new}{resource}{$resource}{peer}{$peer}{protocol} = $value; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "new::resource::${resource}::peer::${peer}::protocol" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{protocol}, - }}); - } - if ($variable eq "fencing") - { - $anvil->data->{new}{resource}{$resource}{peer}{$peer}{fencing} = $value; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - "new::resource::${resource}::peer::${peer}::fencing" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{fencing}, - }}); - } + $anvil->data->{new}{resource}{$resource}{peer}{$peer}{protocol} = $value; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "new::resource::${resource}::peer::${peer}::protocol" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{protocol}, + }}); + } + if ($variable eq "fencing") + { + $anvil->data->{new}{resource}{$resource}{peer}{$peer}{fencing} = $value; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "new::resource::${resource}::peer::${peer}::fencing" => $anvil->data->{new}{resource}{$resource}{peer}{$peer}{fencing}, + }}); } } } diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index f3f1c265..4bc28177 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -4534,6 +4534,12 @@ ORDER BY "storage_groups::vg_uuid::${storage_group_member_vg_uuid}::storage_group_uuid" => $anvil->data->{storage_groups}{vg_uuid}{$storage_group_member_vg_uuid}{storage_group_uuid}, }}); + # Make it possible to sort the storage groups by name. + $anvil->data->{storage_groups}{anvil_uuid}{$storage_group_anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid} = $storage_group_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "storage_groups::anvil_uuid::${storage_group_anvil_uuid}::storage_group_name::${storage_group_name}::storage_group_uuid}" => $anvil->data->{storage_groups}{anvil_uuid}{$storage_group_anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid}, + }}); + # If scan_lvm has been run, read is the free space on the VG if ($scan_lvm_exists) { diff --git a/Anvil/Tools/Storage.pm b/Anvil/Tools/Storage.pm index 2a1ca252..70f351f6 100644 --- a/Anvil/Tools/Storage.pm +++ b/Anvil/Tools/Storage.pm @@ -25,6 +25,7 @@ my $THIS_FILE = "Storage.pm"; # find # get_file_stats # get_storage_group_details +# get_storage_group_from_path # make_directory # manage_lvm_conf # move_file @@ -1739,6 +1740,352 @@ AND } +=head2 get_storage_group_from_path + +This method takes a block device path and returns the C<< storage_group_uuid >> is belongs to, if any. On success, C<< storage_group_uuid >> is returned. If the path is not found to exist on any storage group, an empty string is returned. If there is a problem, C<< !!error!! >> is returned. + +B<< Note >>: If there are multiple results, the first found will be returned. If the results span multiple Anvil! systems, this could be a problem. If this is a concern, specifify either the C<< host_uuid >> or C<< anvil_uuid >> parameters. + +Parameters; + +=head3 anvil_uuid (optional) + +In the case of an ambiguous path (a path found on multiple Anvil! systems), this can be set to specify which Anvil! we're searching for. + +=head3 host_uuid (optional) + +In the case of an ambiguous path (a path found on multiple hosts), this can be set to specify which host we're searching for. + +=head3 path (required) + +This is the full block device path. + +=cut +sub get_storage_group_from_path +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Storage->get_storage_group_from_path()" }}); + + my $anvil_uuid = defined $parameter->{anvil_uuid} ? $parameter->{anvil_uuid} : ""; + my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : ""; + my $path = defined $parameter->{path} ? $parameter->{path} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + anvil_uuid => $anvil_uuid, + host_uuid => $host_uuid, + path => $path, + }}); + + if (not $path) + { + # No source passed. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Storage->get_storage_group_from_path()", parameter => "path" }}); + return('!!error!!'); + } + + # Is this a DRBD path? + my $logical_volume = ""; + if ($path !~ /drbd/) + { + $logical_volume = $path; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { logical_volume => $logical_volume }}); + } + else + { + # Looks like it. If the device path is '/dev/drbd/by-res/...' we'll need to pull out the + # resource name (server name) and volume number as the path only actually exists when DRBD is + # up and isn't referenced in the config file. + my $resource = ""; + my $volume = ""; + $anvil->DRBD->gather_data({debug => $debug}); + if ($path =~ /\/dev\/drbd\/by-res\/(.*)\/(\d+)$/) + { + $resource = $1; + $volume = $2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + resource => $resource, + volume => $volume, + }}); + } + elsif ($path =~ /\/dev\/drbd_(.*)_(\d+)$/) + { + $resource = $1; + $volume = $2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + resource => $resource, + volume => $volume, + }}); + } + elsif ($path =~ /\/dev\/drbd(\d+)$/) + { + # This is a direct path to a minor device, we'll need to find it in the config. + my $minor = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { minor => $minor }}); + + $anvil->Database->get_anvils({debug => $debug}); + my $local_host_uuid = $anvil->Get->host_uuid({debug => $debug}); + my $local_anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => $debug}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { local_anvil_uuid => $local_anvil_uuid }}); + + my $node1_host_uuid = ""; + my $node2_host_uuid = ""; + my $dr1_host_uuid = ""; + if ($anvil_uuid) + { + $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}; + $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}; + $dr1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + node1_host_uuid => $node1_host_uuid, + node1_host_uuid => $node2_host_uuid, + dr1_host_uuid => $dr1_host_uuid, + }}); + } + + # If we were passed an anvil_uuid but not a host_uuid, don't use this machine's host UUID + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }}); + + # These will be set if multiple options are found in the database. + foreach my $this_resource (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_resource => $this_resource }}); + foreach my $this_host_name (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}{$this_resource}{host}}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_host_name => $this_host_name }}); + foreach my $this_volume (sort {$a cmp $b} keys %{$$anvil->data->{new}{resource}{$this_resource}{host}{$this_host_name}{volume}}) + { + my $this_minor = $anvil->data->{new}{resource}{$this_resource}{host}{$this_host_name}{volume}{$this_volume}{device_minor}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + this_volume => $this_volume, + this_minor => $this_minor, + }}); + next if $this_minor ne $minor; + + my $this_host_uuid = $anvil->Get->host_uuid_from_name({host_name => $this_host_name}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_host_uuid => $this_host_uuid }}); + next if not $this_host_uuid; + + # Sorry, this is a bit of a mess. Logic is; If we're given a + # host_uuid, and it matches, use it. Otherwise, if an + # anvil_uuid is passed, and either node 1 or 2's UUID, or if + # there is a DR host, if it's host UUID matches, then we can + # use this. + if ( + ( + ($host_uuid) && ($host_uuid eq $this_host_uuid) + ) + or + ( + ($anvil_uuid) && + ( + ($this_host_uuid eq $node1_host_uuid) or + ($this_host_uuid eq $node2_host_uuid) or + ( + ($dr1_host_uuid) && + ($this_host_uuid eq $dr1_host_uuid) + ) + ) + ) + ) + { + # This is a node in the requested cluster. + $resource = $this_resource; + $volume = $this_volume; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + resource => $resource, + volume => $volume, + }}); + last; + + if (not $host_uuid) + { + $host_uuid = $this_host_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }}); + } + } + } + } + } + } + + # Did I find the resource and volume? + if ($resource) + { + my $query = " +SELECT + scan_drbd_resource_host_uuid, + scan_drbd_resource_xml, + modified_date +FROM + scan_drbd_resources +WHERE + scan_drbd_resource_name = ".$anvil->Database->quote($resource); + if ($host_uuid) + { + $query .= " +AND + scan_drbd_resource_host_uuid = ".$anvil->Database->quote($host_uuid); + } + $query .= " +ORDER BY + modified_date DESC +LIMIT 1 +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + + if (not $count) + { + # Group not found. + return(""); + } + + my $scan_drbd_resource_host_uuid = $results->[0]->[0]; + my $scan_drbd_resource_xml = $results->[0]->[1]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + scan_drbd_resource_host_uuid => $scan_drbd_resource_host_uuid, + scan_drbd_resource_xml => $scan_drbd_resource_xml, + }}); + + $anvil->DRBD->gather_data({ + debug => 3, + xml => $scan_drbd_resource_xml, + }); + + # Dig out the LV behind the volume. + foreach my $this_host_name (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}{$resource}{host}}) + { + my $this_host_uuid = $anvil->Get->host_uuid_from_name({host_name => $this_host_name}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "s1:this_host_name" => $this_host_name, + "s2:this_host_uuid" => $this_host_uuid, + }}); + next if (($host_uuid) && ($this_host_uuid ne $host_uuid)); + my $device_path = $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{device_path}; + my $backing_disk = $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{backing_disk}; + my $device_minor = $anvil->data->{new}{resource}{$resource}{host}{$this_host_name}{volume}{$volume}{device_minor}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "s3:device_path" => $device_path, + "s4:backing_disk" => $backing_disk, + "s5:device_minor" => $device_minor, + }}); + + if (not $host_uuid) + { + $host_uuid = $scan_drbd_resource_host_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }}); + } + + $logical_volume = $backing_disk; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { logical_volume => $logical_volume }}); + last; + } + } + } + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { logical_volume => $logical_volume }}); + if ($logical_volume) + { + ### NOTE: We're pulling more columns than we need to help with logging. + # Verify this is an LV and, if so, what VG is it on? + my $query = " +SELECT + a.scan_lvm_lv_name, + a.scan_lvm_lv_on_vg, + b.scan_lvm_vg_internal_uuid +FROM + scan_lvm_lvs a, + scan_lvm_vgs b +WHERE + a.scan_lvm_lv_host_uuid = b.scan_lvm_vg_host_uuid +AND + a.scan_lvm_lv_on_vg = b.scan_lvm_vg_name +AND + a.scan_lvm_lv_path = ".$anvil->Database->quote($logical_volume); + if ($host_uuid) + { + $query .= " +AND + scan_lvm_lv_host_uuid = ".$anvil->Database->quote($host_uuid); + } + $query .= " +LIMIT 1 +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + + if (not $count) + { + # LV not found. + return(""); + } + + my $scan_lvm_lv_name = $results->[0]->[0]; + my $scan_lvm_lv_on_vg = $results->[0]->[1]; + my $scan_lvm_vg_internal_uuid = $results->[0]->[2]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + scan_lvm_lv_name => $scan_lvm_lv_name, + scan_lvm_lv_on_vg => $scan_lvm_lv_on_vg, + scan_lvm_vg_internal_uuid => $scan_lvm_vg_internal_uuid, + }}); + + $query = " +SELECT + a.storage_group_uuid, + a.storage_group_name +FROM + storage_groups a, + storage_group_members b +WHERE + a.storage_group_uuid = b.storage_group_member_storage_group_uuid +AND + b.storage_group_member_vg_uuid = ".$anvil->Database->quote($scan_lvm_vg_internal_uuid)." +LIMIT 1 +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + + $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + + if (not $count) + { + # Storage group not found. + return(""); + } + + my $storage_group_uuid = $results->[0]->[0]; + my $storage_group_name = $results->[0]->[1]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + storage_group_uuid => $storage_group_uuid, + storage_group_name => $storage_group_name, + }}); + + # Done! + return($storage_group_uuid); + } + + return(""); +} + + =head2 make_directory This creates a directory (and any parent directories). diff --git a/tools/anvil-manage-server b/tools/anvil-manage-server index f67c52de..c0b97800 100755 --- a/tools/anvil-manage-server +++ b/tools/anvil-manage-server @@ -94,6 +94,9 @@ if ($anvil->data->{switches}{'job-uuid'}) else { # Interactive! + $anvil->data->{new_config}{cpu}{sockets} = ""; + $anvil->data->{new_config}{cpu}{cores} = ""; + $anvil->data->{new_config}{ram}{'bytes'} = ""; interactive_question($anvil); } @@ -633,6 +636,7 @@ sub interactive_configure_main }})."\n"; my $changes = 0; my $cpu_star = ""; + my $ram_star = ""; if (($anvil->data->{new_config}{cpu}{sockets}) or ($anvil->data->{new_config}{cpu}{cores})) { $cpu_star = "*"; @@ -642,9 +646,18 @@ sub interactive_configure_main changes => $changes, }}); } + if ($anvil->data->{new_config}{ram}{'bytes'}) + { + $ram_star = "*"; + $changes = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + ram_star => $ram_star, + changes => $changes, + }}); + } print "[ 1 ] - CPU".$cpu_star."\n"; - print "[ 2 ] - RAM\n"; + print "[ 2 ] - RAM".$ram_star."\n"; print "[ 3 ] - Storage\n"; print "[ 4 ] - Network\n"; print "[ 5 ] - Boot Order\n"; @@ -663,16 +676,15 @@ sub interactive_configure_main if ($answer eq "1") { - interactive_configure_cpu($anvil, $terminal) + interactive_configure_cpu($anvil, $terminal); } elsif ($answer eq "2") { - interactive_configure_ram($anvil, $terminal) + interactive_configure_ram($anvil, $terminal); } elsif ($answer eq "3") { - print "Going to Storage menu\n"; - sleep 1; + interactive_configure_storage($anvil, $terminal); } elsif ($answer eq "4") { @@ -747,6 +759,51 @@ sub interactive_configure_main return(0); } +sub interactive_configure_storage +{ + my ($anvil, $terminal) = @_; + + # Get the + $anvil->Database->get_storage_group_data({debug => 2}); + while(1) + { + # Here, we'll list each storage pool and for each indicate if it's used and, if so + my $anvil_uuid = $anvil->data->{target_server}{anvil_uuid}; + foreach my $storage_group_name (sort {$a cmp $b} keys %{$anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_name}}) + { + my $storage_group_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid}; + my $vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}; + my $free_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size + my $vg_size_on_dr = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size_on_dr}; + my $available_on_dr = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{available_on_dr}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:storage_group_name' => $storage_group_name, + 's2:storage_group_uuid' => $storage_group_uuid, + 's3:vg_size' => $vg_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size}).")", + 's4:free_size' => $free_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $free_size}).")", + 's5:vg_size_on_dr' => $vg_size_on_dr." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size_on_dr}).")", + 's6:available_on_dr' => $free_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $available_on_dr}).")", + }}); + + foreach my $target (sort {$a cmp $b} keys %{$anvil->data->{target_server}{disk}}) + { + my $device_bus = $anvil->data->{target_server}{disk}{$target}{device_bus} + my $cache = $anvil->data->{target_server}{disk}{$target}{cache}; + my $io = $anvil->data->{target_server}{disk}{$target}{io}; + my $path = $anvil->data->{target_server}{disk}{$target}{path}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + device_bus => $device_bus, + cache => $cache, + io => $io, + path => $path, + }}); + } + } + } + + return(0); +} + sub interactive_configure_ram { my ($anvil, $terminal) = @_; @@ -754,39 +811,35 @@ sub interactive_configure_ram while(1) { # Threads will be the maximum, even without hyperthreading being available. - my $anvil_uuid = $anvil->data->{target_server}{anvil_uuid}; - my $say_current_ram = $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{target_server}{configured_ram}}); - my $say_total_ram = $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{hardware}}); - my $say_available_ram = $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available}}); - my $max_assigned = $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available} - $anvil->data->{target_server}{configured_ram}; - my $say_max_assigned = $anvil->Convert->bytes_to_human_readable({'bytes' => $max_assigned}); + my $anvil_uuid = $anvil->data->{target_server}{anvil_uuid}; + my $say_current_ram = $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{target_server}{configured_ram}}); + my $say_total_ram = $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{hardware}}); + my $say_available_ram = $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available}}); + my $max_assignable = $anvil->data->{anvil_resources}{$anvil_uuid}{ram}{available} + $anvil->data->{target_server}{configured_ram}; + my $say_max_assignable = $anvil->Convert->bytes_to_human_readable({'bytes' => $max_assignable}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - anvil_uuid => $anvil_uuid, - say_current_ram => $say_current_ram, - say_total_ram => $say_total_ram, - say_available_ram => $say_available_ram, - max_assigned => $say_max_assigned, + anvil_uuid => $anvil_uuid, + say_current_ram => $say_current_ram, + say_total_ram => $say_total_ram, + say_available_ram => $say_available_ram, + say_max_assignable => $say_max_assignable, }}); my $changes = 0; my $say_new_ram = 0; - if (not exists $anvil->data->{new_config}{ram}{'bytes'}) - { - $anvil->data->{new_config}{ram}{'bytes'} = 0; - } - elsif ($anvil->data->{new_config}{ram}{'bytes'}) + if ($anvil->data->{new_config}{ram}{'bytes'}) { $say_new_ram = $anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new_config}{ram}{'bytes'}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_new_ram => $say_new_ram }}); } - my $star_ram = " "; + my $ram_star = " "; if (($anvil->data->{new_config}{ram}{'bytes'}) && ($anvil->data->{new_config}{ram}{'bytes'} ne $anvil->data->{target_server}{server_ram_bytes})) { - $star_ram = "*"; + $ram_star = "*"; $changes = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - star_ram => $star_ram, + ram_star => $ram_star, changes => $changes, }}); } @@ -796,8 +849,8 @@ sub interactive_configure_ram anvil_name => $anvil->data->{target_server}{anvil_name}, server_name => $anvil->data->{target_server}{server_name}, }})."\n"; - print "* RAM configuration. Currently allocated: [".$say_current_ram."], Maximum RAM: [".$say_max_assigned."] (current + free).\n"; - print "[ 1 ] - RAM".$star_ram." (currently: [".$say_current_ram."], new: [".$say_new_ram."])\n"; + print "* RAM configuration. Currently allocated: [".$say_current_ram."], Maximum RAM: [".$say_max_assignable."] (current + free).\n"; + print "[ 1 ] - RAM".$ram_star." (currently: [".$say_current_ram."], new: [".$say_new_ram."])\n"; print "\n"; print "[ B ] - Back\n"; @@ -833,11 +886,11 @@ sub interactive_configure_ram { # Convert to bytes my $requested_bytes = $answer; - if ($answer =~ /\D/) + if ($requested_bytes =~ /\D/) { $requested_bytes = $anvil->Convert->human_readable_to_bytes({ base2 => 1, - size => $answer, + size => $requested_bytes, }); if ($requested_bytes eq "!!error!!") { @@ -848,13 +901,20 @@ sub interactive_configure_ram $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }}); interactive_configure_ram($anvil, $terminal); } - else - { - $anvil->data->{new_config}{ram}{'bytes'} = $requested_bytes; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { - "new_config::ram::bytes" => $anvil->data->{new_config}{ram}{'bytes'}, - }}); - } + } + elsif (not $requested_bytes) + { + interactive_configure_ram($anvil, $terminal); + } + + if (($requested_bytes =~ /^\d+$/) && ($requested_bytes > $max_assignable)) + { + $anvil->data->{new_config}{ram}{'bytes'} = 0; + print "[ Error ] - You asked for: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $requested_bytes})."] (".$anvil->Convert->add_commas({number => $max_assignable})." bytes) RAM, but the max available is: [".$say_max_assignable."] (".$anvil->Convert->add_commas({number => $max_assignable})." bytes).\n"; + my $answer = ; + chomp $answer; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }}); + interactive_configure_ram($anvil, $terminal); } else { @@ -899,16 +959,7 @@ sub interactive_configure_cpu current_cores => $current_cores, }}); - my $changes = 0; - if (not exists $anvil->data->{new_config}{cpu}{sockets}) - { - $anvil->data->{new_config}{cpu}{sockets} = ""; - } - if (not exists $anvil->data->{new_config}{cpu}{cores}) - { - $anvil->data->{new_config}{cpu}{cores} = ""; - } - + my $changes = 0; my $star_cores = " "; if (($anvil->data->{new_config}{cpu}{cores}) && ($anvil->data->{new_config}{cpu}{cores} ne $anvil->data->{target_server}{server_cpu_cores})) {