Sometimes it’s necessary to maintain 2 different CloudStack setups (let’s call them Cloud-A and Cloud-B) sharing the same IP-address ranges. For example, we might need that when we’re building a new CloudStack-driven environment and going to move all the virtual infrastructure from the old setup to the new one, but as we can’t just make move everything in a couple of hours (let’s say, we have about a thousand of VMs), we have to let 2 different CloudStack-driven virtual datacenters use the same networks. We should understand that we might have to maintain 2 different CloudStack setups for a few days or weeks or even months, so we have to take care about the situation when someone deploys a new VM or assign a new IP-address to a currently running VM. It means that both CloudStack setups should understand that the IP-address has been allocated, otherwise we might come to the situation when both CloudStacks will assign the same IP-address to their VMs and we’ll face a major trouble.
We can solve it by making a simple script that would look up what IP-addresses are being in the “Allocated” state in Cloud-A and make them “Allocated” to a certain account in Cloud-B. Then the script would do the same to mark as “Allocated” in Cloud-A the addresses that are really assigned to some instances in Cloud-B. Also it would be great if the script would mark as “Free” the IP-addresses that aren’t being used, of course.
In the very first apporach such a sctipt might be looking like that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
#!/usr/bin/env perl # # vim: autoindent tabstop=4 shiftwidth=4 expandtab softtabstop=4 filetype=perl # use strict; use warnings; use DBI; my $zones = { z1 => { _db_host => '172.24.0.1', _db_database => 'cloud', _db_username => 'ip-keeper', _db_password => '***', _ip_keeper_account_id => 666, # The ID of the account that will own the assigned addresses _ip_keeper_domain_id => 13, # The ID of the domain to which the account belongs _networks => [ 200, 201, 202 ] # The IDs of the networks that are being shared }, z2 => { _db_host => '172.23.0.1', _db_database => 'cloud', _db_username => 'ip-keeper', _db_password => '***', _ip_keeper_account_id => 999, _ip_keeper_domain_id => 31, _networks => [ 210, 211, 212 ] } }; while (my($zone_name, $zone_parameters) = each(%{ $zones })) { $zone_parameters->{'dbh'} = DBI->connect( sprintf('dbi:mysql:database=%s;host=%s', $zone_parameters->{'_db_database'}, $zone_parameters->{'_db_host'} ), $zone_parameters->{'_db_username'}, $zone_parameters->{'_db_password'}, { PrintError => 0 } ) or die('Can\'t connect to the database: ' . $DBI::errstr); $zone_parameters->{'sth_get_allocated'} = $zone_parameters->{'dbh'}->prepare( sprintf( 'SELECT public_ip_address ' . 'FROM user_ip_address ' . 'WHERE ' . 'source_network_id IN (%s) AND ' . 'state = \'Allocated\' AND ' . 'NOT (account_id = ? AND domain_id = ?)', join(',', map('?', @{ $zone_parameters->{'_networks'} })) ) ) or die('Can\'t prepare the statement: ' . $zone_parameters->{'dbh'}->errstr); $zone_parameters->{'sth_is_free'} = $zone_parameters->{'dbh'}->prepare( sprintf( 'SELECT id, public_ip_address, state, account_id, domain_id ' . 'FROM user_ip_address ' . 'WHERE ' . 'public_ip_address = ? AND ' . 'source_network_id IN (%s) AND ' . 'state = \'Free\'', join(',', map('?', @{ $zone_parameters->{'_networks'} })) ) ) or die('Can\'t prepare the statement: ' . $zone_parameters->{'dbh'}->errstr); $zone_parameters->{'sth_set_allocated'} = $zone_parameters->{'dbh'}->prepare( 'UPDATE user_ip_address ' . 'SET ' . 'allocated = NOW(), ' . 'account_id = ?, ' . 'domain_id = ?, ' . 'state = \'Allocated\' ' . 'WHERE id = ?' ) or die('Can\'t prepare the statement: ' . $zone_parameters->{'dbh'}->errstr); $zone_parameters->{'sth_get_free'} = $zone_parameters->{'dbh'}->prepare( sprintf( 'SELECT public_ip_address ' . 'FROM user_ip_address ' . 'WHERE ' . 'source_network_id IN (%s) AND ' . 'state = \'Free\'', join(',', map('?', @{ $zone_parameters->{'_networks'} })) ) ) or die('Can\'t prepare the statement: ' . $zone_parameters->{'dbh'}->errstr); $zone_parameters->{'sth_is_allocated'} = $zone_parameters->{'dbh'}->prepare( sprintf( 'SELECT id, public_ip_address, state, account_id, domain_id ' . 'FROM user_ip_address ' . 'WHERE ' . 'public_ip_address = ? AND ' . 'source_network_id IN (%s) AND (' . 'state = \'Allocated\' AND ' . '(account_id = ? AND domain_id = ?)' . ')', join(',', map('?', @{ $zone_parameters->{'_networks'} })) ) ) or die('Can\'t prepare the statement: ' . $zone_parameters->{'dbh'}->errstr); $zone_parameters->{'sth_set_free'} = $zone_parameters->{'dbh'}->prepare( 'UPDATE user_ip_address ' . 'SET ' . 'allocated = NULL, ' . 'account_id = NULL, ' . 'domain_id = NULL, ' . 'state = \'Free\' ' . 'WHERE id = ?' ) or die('Can\'t prepare the statement: ' . $zone_parameters->{'dbh'}->errstr); } my @zones_list = keys(%{ $zones }); if(@zones_list != 2) { die('There are more or less than 2 zones, can\'t work!'); } for(my $i = 0; $i < @zones_list; $i++) { my $source_zone_name = $zones_list[$i]; my $source_zone_parameters = $zones->{$source_zone_name}; my $target_zone_name = $zones_list[not $i]; my $target_zone_parameters = $zones->{$target_zone_name}; printf("*** Processing the %s zone ***\n", $source_zone_name); $source_zone_parameters->{'sth_get_allocated'}->execute( @{ $source_zone_parameters->{'_networks'} }, $source_zone_parameters->{'_ip_keeper_account_id'}, $source_zone_parameters->{'_ip_keeper_domain_id'}, ) or die(sprintf('Can\'t execute the statement: %s', $source_zone_parameters->{'dbh'}->errstr)); while(my $result_source = $source_zone_parameters->{'sth_get_allocated'}->fetchrow_hashref) { $target_zone_parameters->{'sth_is_free'}->execute( $result_source->{'public_ip_address'}, @{ $target_zone_parameters->{'_networks'} } ) or die(sprintf('Can\'t execute the statement: %s', $target_zone_parameters->{'dbh'}->errstr)); while(my $result_target = $target_zone_parameters->{'sth_is_free'}->fetchrow_hashref) { printf( 'To set allocated: %s (%s) ...', $result_target->{'public_ip_address'}, $result_target->{'id'} ); my $have_been_set = $target_zone_parameters->{'sth_set_allocated'}->execute( $target_zone_parameters->{'_ip_keeper_account_id'}, $target_zone_parameters->{'_ip_keeper_domain_id'}, $result_target->{'id'} ) or die(sprintf('Can\'t execute the statement: %s', $target_zone_parameters->{'dbh'}->errstr)); printf(" %s results\n", $have_been_set); } } $source_zone_parameters->{'sth_get_free'}->execute( @{ $source_zone_parameters->{'_networks'} } ) or die(sprintf('Can\'t execute the statement: %s', $source_zone_parameters->{'dbh'}->errstr)); while(my $result_source = $source_zone_parameters->{'sth_get_free'}->fetchrow_hashref) { $target_zone_parameters->{'sth_is_allocated'}->execute( $result_source->{'public_ip_address'}, @{ $target_zone_parameters->{'_networks'} }, $target_zone_parameters->{'_ip_keeper_account_id'}, $target_zone_parameters->{'_ip_keeper_domain_id'} ) or die(sprintf('Can\'t execute the statement: %s', $target_zone_parameters->{'dbh'}->errstr)); while(my $result_target = $target_zone_parameters->{'sth_is_allocated'}->fetchrow_hashref) { printf( 'To set free: %s (%s) ...', $result_target->{'public_ip_address'}, $result_target->{'id'} ); my $have_been_set = $target_zone_parameters->{'sth_set_free'}->execute( $result_target->{'id'} ) or die(sprintf('Can\'t execute the statement: %s', $target_zone_parameters->{'dbh'}->errstr)); printf(" %s results\n", $have_been_set); } } } |
I’m going to add some functionality (for example, notifications about the IP-address that are already having the same assigned/free state in both clouds), so you’re welcome to get the latest revision on GitHub.