| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| package com.cloud.upgrade.dao; | |
| import java.io.InputStream; | |
| import java.io.UnsupportedEncodingException; | |
| import java.sql.Connection; | |
| import java.sql.PreparedStatement; | |
| import java.sql.ResultSet; | |
| import java.sql.SQLException; | |
| import java.util.ArrayList; | |
| import java.util.List; | |
| import java.util.UUID; | |
| import org.apache.log4j.Logger; | |
| import com.cloud.utils.crypt.DBEncryptionUtil; | |
| import com.cloud.utils.exception.CloudRuntimeException; | |
| public class Upgrade304to305 extends Upgrade30xBase { | |
| final static Logger s_logger = Logger.getLogger(Upgrade304to305.class); | |
| @Override | |
| public String[] getUpgradableVersionRange() { | |
| return new String[] {"3.0.4", "3.0.5"}; | |
| } | |
| @Override | |
| public String getUpgradedVersion() { | |
| return "3.0.5"; | |
| } | |
| @Override | |
| public boolean supportsRollingUpgrade() { | |
| return true; | |
| } | |
| @Override | |
| public InputStream[] getPrepareScripts() { | |
| final String scriptFile = "META-INF/db/schema-304to305.sql"; | |
| final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); | |
| if (script == null) { | |
| throw new CloudRuntimeException("Unable to find " + scriptFile); | |
| } | |
| return new InputStream[] {script}; | |
| } | |
| @Override | |
| public void performDataMigration(Connection conn) { | |
| addHostDetailsUniqueKey(conn); | |
| addVpcProvider(conn); | |
| updateRouterNetworkRef(conn); | |
| fixZoneUsingExternalDevices(conn); | |
| // updateSystemVms(conn); | |
| fixForeignKeys(conn); | |
| encryptClusterDetails(conn); | |
| } | |
| @Override | |
| public InputStream[] getCleanupScripts() { | |
| final String scriptFile = "META-INF/db/schema-304to305-cleanup.sql"; | |
| final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); | |
| if (script == null) { | |
| throw new CloudRuntimeException("Unable to find " + scriptFile); | |
| } | |
| return new InputStream[] {script}; | |
| } | |
| private void updateSystemVms(Connection conn) { | |
| PreparedStatement pstmt = null; | |
| ResultSet rs = null; | |
| boolean VMware = false; | |
| try { | |
| pstmt = conn.prepareStatement("select distinct(hypervisor_type) from `cloud`.`cluster` where removed is null"); | |
| rs = pstmt.executeQuery(); | |
| while (rs.next()) { | |
| if ("VMware".equals(rs.getString(1))) { | |
| VMware = true; | |
| } | |
| } | |
| } catch (SQLException e) { | |
| throw new CloudRuntimeException("Error while iterating through list of hypervisors in use", e); | |
| } | |
| // Just update the VMware system template. Other hypervisor templates are unchanged from previous 3.0.x versions. | |
| s_logger.debug("Updating VMware System Vms"); | |
| try { | |
| //Get 3.0.5 VMware system Vm template Id | |
| pstmt = conn.prepareStatement("select id from `cloud`.`vm_template` where name = 'systemvm-vmware-3.0.5' and removed is null"); | |
| rs = pstmt.executeQuery(); | |
| if (rs.next()) { | |
| long templateId = rs.getLong(1); | |
| rs.close(); | |
| pstmt.close(); | |
| // change template type to SYSTEM | |
| pstmt = conn.prepareStatement("update `cloud`.`vm_template` set type='SYSTEM' where id = ?"); | |
| pstmt.setLong(1, templateId); | |
| pstmt.executeUpdate(); | |
| pstmt.close(); | |
| // update template ID of system Vms | |
| pstmt = conn.prepareStatement("update `cloud`.`vm_instance` set vm_template_id = ? where type <> 'User' and hypervisor_type = 'VMware'"); | |
| pstmt.setLong(1, templateId); | |
| pstmt.executeUpdate(); | |
| pstmt.close(); | |
| } else { | |
| if (VMware) { | |
| throw new CloudRuntimeException("3.0.5 VMware SystemVm template not found. Cannot upgrade system Vms"); | |
| } else { | |
| s_logger.warn("3.0.5 VMware SystemVm template not found. VMware hypervisor is not used, so not failing upgrade"); | |
| } | |
| } | |
| } catch (SQLException e) { | |
| throw new CloudRuntimeException("Error while updating VMware systemVm template", e); | |
| } | |
| s_logger.debug("Updating System Vm Template IDs Complete"); | |
| } | |
| private void addVpcProvider(Connection conn) { | |
| //Encrypt config params and change category to Hidden | |
| s_logger.debug("Adding vpc provider to all physical networks in the system"); | |
| PreparedStatement pstmt = null; | |
| ResultSet rs = null; | |
| try { | |
| pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`physical_network` WHERE removed is NULL"); | |
| rs = pstmt.executeQuery(); | |
| while (rs.next()) { | |
| Long pNtwkId = rs.getLong(1); | |
| //insert provider | |
| pstmt = | |
| conn.prepareStatement("INSERT INTO `cloud`.`physical_network_service_providers` " | |
| + "(`physical_network_id`, `provider_name`, `state`, `vpn_service_provided`, `dhcp_service_provided`, " | |
| + "`dns_service_provided`, `gateway_service_provided`, `firewall_service_provided`, `source_nat_service_provided`," | |
| + " `load_balance_service_provided`, `static_nat_service_provided`, `port_forwarding_service_provided`," | |
| + " `user_data_service_provided`, `security_group_service_provided`) " | |
| + "VALUES (?, 'VpcVirtualRouter', 'Enabled', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)"); | |
| pstmt.setLong(1, pNtwkId); | |
| pstmt.executeUpdate(); | |
| //get provider id | |
| pstmt = | |
| conn.prepareStatement("SELECT id FROM `cloud`.`physical_network_service_providers` " | |
| + "WHERE physical_network_id=? and provider_name='VpcVirtualRouter'"); | |
| pstmt.setLong(1, pNtwkId); | |
| ResultSet rs1 = pstmt.executeQuery(); | |
| rs1.next(); | |
| long providerId = rs1.getLong(1); | |
| //insert VR element | |
| pstmt = conn.prepareStatement("INSERT INTO `cloud`.`virtual_router_providers` (`nsp_id`, `type`, `enabled`) " + "VALUES (?, 'VPCVirtualRouter', 1)"); | |
| pstmt.setLong(1, providerId); | |
| pstmt.executeUpdate(); | |
| s_logger.debug("Added VPC Virtual router provider for physical network id=" + pNtwkId); | |
| } | |
| } catch (SQLException e) { | |
| throw new CloudRuntimeException("Unable add VPC physical network service provider ", e); | |
| } finally { | |
| closeAutoCloseable(rs); | |
| closeAutoCloseable(pstmt); | |
| } | |
| s_logger.debug("Done adding VPC physical network service providers to all physical networks"); | |
| } | |
| private void updateRouterNetworkRef(Connection conn) { | |
| //Encrypt config params and change category to Hidden | |
| s_logger.debug("Updating router network ref"); | |
| PreparedStatement pstmt = null; | |
| ResultSet rs = null; | |
| try { | |
| pstmt = conn.prepareStatement("SELECT d.id, d.network_id FROM `cloud`.`domain_router` d, `cloud`.`vm_instance` v " + "WHERE d.id=v.id AND v.removed is NULL"); | |
| rs = pstmt.executeQuery(); | |
| while (rs.next()) { | |
| Long routerId = rs.getLong(1); | |
| Long networkId = rs.getLong(2); | |
| //get the network type | |
| pstmt = conn.prepareStatement("SELECT guest_type from `cloud`.`networks` where id=?"); | |
| pstmt.setLong(1, networkId); | |
| ResultSet rs1 = pstmt.executeQuery(); | |
| rs1.next(); | |
| String networkType = rs1.getString(1); | |
| //insert the reference | |
| pstmt = conn.prepareStatement("INSERT INTO `cloud`.`router_network_ref` (router_id, network_id, guest_type) " + "VALUES (?, ?, ?)"); | |
| pstmt.setLong(1, routerId); | |
| pstmt.setLong(2, networkId); | |
| pstmt.setString(3, networkType); | |
| pstmt.executeUpdate(); | |
| s_logger.debug("Added reference for router id=" + routerId + " and network id=" + networkId); | |
| } | |
| } catch (SQLException e) { | |
| throw new CloudRuntimeException("Failed to update the router/network reference ", e); | |
| } finally { | |
| closeAutoCloseable(rs); | |
| closeAutoCloseable(pstmt); | |
| } | |
| s_logger.debug("Done updating router/network references"); | |
| } | |
| private void addHostDetailsUniqueKey(Connection conn) { | |
| s_logger.debug("Checking if host_details unique key exists, if not we will add it"); | |
| PreparedStatement pstmt = null; | |
| ResultSet rs = null; | |
| try { | |
| pstmt = conn.prepareStatement("SHOW INDEX FROM `cloud`.`host_details` WHERE KEY_NAME = 'uk_host_id_name'"); | |
| rs = pstmt.executeQuery(); | |
| if (rs.next()) { | |
| s_logger.debug("Unique key already exists on host_details - not adding new one"); | |
| } else { | |
| //add the key | |
| PreparedStatement pstmtUpdate = | |
| conn.prepareStatement("ALTER IGNORE TABLE `cloud`.`host_details` ADD CONSTRAINT UNIQUE KEY `uk_host_id_name` (`host_id`, `name`)"); | |
| pstmtUpdate.executeUpdate(); | |
| s_logger.debug("Unique key did not exist on host_details - added new one"); | |
| pstmtUpdate.close(); | |
| } | |
| } catch (SQLException e) { | |
| throw new CloudRuntimeException("Failed to check/update the host_details unique key ", e); | |
| } finally { | |
| closeAutoCloseable(rs); | |
| closeAutoCloseable(pstmt); | |
| } | |
| } | |
| // This fix does two things | |
| // | |
| // 1) ensure that networks using external load balancer/firewall in 2.2.14 or prior releases deployments | |
| // has entry in network_external_lb_device_map and network_external_firewall_device_map | |
| // | |
| // 2) Some keys of host details for F5 and SRX devices were stored in Camel Case in 2.x releases. From 3.0 | |
| // they are made in lowercase. On upgrade change the host details name to lower case | |
| private void fixZoneUsingExternalDevices(Connection conn) { | |
| //Get zones to upgrade | |
| List<Long> zoneIds = new ArrayList<Long>(); | |
| PreparedStatement pstmt = null; | |
| PreparedStatement pstmtUpdate = null; | |
| ResultSet rs = null; | |
| long networkOfferingId, networkId; | |
| long f5DeviceId, f5HostId; | |
| long srxDevivceId, srxHostId; | |
| try { | |
| pstmt = | |
| conn.prepareStatement("select id from `cloud`.`data_center` where lb_provider='F5BigIp' or firewall_provider='JuniperSRX' or gateway_provider='JuniperSRX'"); | |
| rs = pstmt.executeQuery(); | |
| while (rs.next()) { | |
| zoneIds.add(rs.getLong(1)); | |
| } | |
| } catch (SQLException e) { | |
| throw new CloudRuntimeException("Unable to create network to LB & firewalla device mapping for networks that use them", e); | |
| } | |
| if (zoneIds.size() == 0) { | |
| return; // no zones using F5 and SRX devices so return | |
| } | |
| // find the default network offering created for external devices during upgrade from 2.2.14 | |
| try { | |
| pstmt = conn.prepareStatement("select id from `cloud`.`network_offerings` where unique_name='Isolated with external providers' "); | |
| rs = pstmt.executeQuery(); | |
| if (rs.first()) { | |
| networkOfferingId = rs.getLong(1); | |
| } else { | |
| throw new CloudRuntimeException("Cannot upgrade as there is no 'Isolated with external providers' network offering crearted ."); | |
| } | |
| } catch (SQLException e) { | |
| throw new CloudRuntimeException("Unable to create network to LB & firewalla device mapping for networks that use them", e); | |
| } | |
| for (Long zoneId : zoneIds) { | |
| try { | |
| // find the F5 device id in the zone | |
| pstmt = conn.prepareStatement("SELECT id FROM host WHERE data_center_id=? AND type = 'ExternalLoadBalancer' AND removed IS NULL"); | |
| pstmt.setLong(1, zoneId); | |
| rs = pstmt.executeQuery(); | |
| if (rs.first()) { | |
| f5HostId = rs.getLong(1); | |
| } else { | |
| throw new CloudRuntimeException("Cannot upgrade as there is no F5 load balancer device found in data center " + zoneId); | |
| } | |
| pstmt = conn.prepareStatement("SELECT id FROM external_load_balancer_devices WHERE host_id=?"); | |
| pstmt.setLong(1, f5HostId); | |
| rs = pstmt.executeQuery(); | |
| if (rs.first()) { | |
| f5DeviceId = rs.getLong(1); | |
| } else { | |
| throw new CloudRuntimeException("Cannot upgrade as there is no F5 load balancer device with host ID " + f5HostId + | |
| " found in external_load_balancer_device"); | |
| } | |
| // find the SRX device id in the zone | |
| pstmt = conn.prepareStatement("SELECT id FROM host WHERE data_center_id=? AND type = 'ExternalFirewall' AND removed IS NULL"); | |
| pstmt.setLong(1, zoneId); | |
| rs = pstmt.executeQuery(); | |
| if (rs.first()) { | |
| srxHostId = rs.getLong(1); | |
| } else { | |
| throw new CloudRuntimeException("Cannot upgrade as there is no SRX firewall device found in data center " + zoneId); | |
| } | |
| pstmt = conn.prepareStatement("SELECT id FROM external_firewall_devices WHERE host_id=?"); | |
| pstmt.setLong(1, srxHostId); | |
| rs = pstmt.executeQuery(); | |
| if (rs.first()) { | |
| srxDevivceId = rs.getLong(1); | |
| } else { | |
| throw new CloudRuntimeException("Cannot upgrade as there is no SRX firewall device found with host ID " + srxHostId + | |
| " found in external_firewall_devices"); | |
| } | |
| // check if network any uses F5 or SRX devices in the zone | |
| pstmt = | |
| conn.prepareStatement("select id from `cloud`.`networks` where guest_type='Virtual' and data_center_id=? and network_offering_id=? and removed IS NULL"); | |
| pstmt.setLong(1, zoneId); | |
| pstmt.setLong(2, networkOfferingId); | |
| rs = pstmt.executeQuery(); | |
| while (rs.next()) { | |
| // get the network Id | |
| networkId = rs.getLong(1); | |
| // add mapping for the network in network_external_lb_device_map | |
| String insertLbMapping = | |
| "INSERT INTO `cloud`.`network_external_lb_device_map` (uuid, network_id, external_load_balancer_device_id, created) VALUES ( ?, ?, ?, now())"; | |
| pstmtUpdate = conn.prepareStatement(insertLbMapping); | |
| pstmtUpdate.setString(1, UUID.randomUUID().toString()); | |
| pstmtUpdate.setLong(2, networkId); | |
| pstmtUpdate.setLong(3, f5DeviceId); | |
| pstmtUpdate.executeUpdate(); | |
| s_logger.debug("Successfully added entry in network_external_lb_device_map for network " + networkId + " and F5 device ID " + f5DeviceId); | |
| // add mapping for the network in network_external_firewall_device_map | |
| String insertFwMapping = | |
| "INSERT INTO `cloud`.`network_external_firewall_device_map` (uuid, network_id, external_firewall_device_id, created) VALUES ( ?, ?, ?, now())"; | |
| pstmtUpdate = conn.prepareStatement(insertFwMapping); | |
| pstmtUpdate.setString(1, UUID.randomUUID().toString()); | |
| pstmtUpdate.setLong(2, networkId); | |
| pstmtUpdate.setLong(3, srxDevivceId); | |
| pstmtUpdate.executeUpdate(); | |
| s_logger.debug("Successfully added entry in network_external_firewall_device_map for network " + networkId + " and SRX device ID " + srxDevivceId); | |
| } | |
| // update host details for F5 and SRX devices | |
| s_logger.debug("Updating the host details for F5 and SRX devices"); | |
| pstmt = conn.prepareStatement("SELECT host_id, name FROM `cloud`.`host_details` WHERE host_id=? OR host_id=?"); | |
| pstmt.setLong(1, f5HostId); | |
| pstmt.setLong(2, srxHostId); | |
| rs = pstmt.executeQuery(); | |
| while (rs.next()) { | |
| long hostId = rs.getLong(1); | |
| String camlCaseName = rs.getString(2); | |
| if (!(camlCaseName.equalsIgnoreCase("numRetries") || camlCaseName.equalsIgnoreCase("publicZone") || camlCaseName.equalsIgnoreCase("privateZone") || | |
| camlCaseName.equalsIgnoreCase("publicInterface") || camlCaseName.equalsIgnoreCase("privateInterface") || camlCaseName.equalsIgnoreCase("usageInterface"))) { | |
| continue; | |
| } | |
| String lowerCaseName = camlCaseName.toLowerCase(); | |
| pstmt = conn.prepareStatement("update `cloud`.`host_details` set name=? where host_id=? AND name=?"); | |
| pstmt.setString(1, lowerCaseName); | |
| pstmt.setLong(2, hostId); | |
| pstmt.setString(3, camlCaseName); | |
| pstmt.executeUpdate(); | |
| } | |
| s_logger.debug("Successfully updated host details for F5 and SRX devices"); | |
| } catch (SQLException e) { | |
| throw new CloudRuntimeException("Unable create a mapping for the networks in network_external_lb_device_map and network_external_firewall_device_map", e); | |
| } finally { | |
| closeAutoCloseable(rs); | |
| closeAutoCloseable(pstmt); | |
| } | |
| s_logger.info("Successfully upgraded network using F5 and SRX devices to have a entry in the network_external_lb_device_map and network_external_firewall_device_map"); | |
| } | |
| } | |
| private void fixForeignKeys(Connection conn) { | |
| s_logger.debug("Fixing foreign keys' names in ssh_keypairs table"); | |
| //Drop the keys (if exist) | |
| List<String> keys = new ArrayList<String>(); | |
| keys.add("fk_ssh_keypair__account_id"); | |
| keys.add("fk_ssh_keypair__domain_id"); | |
| keys.add("fk_ssh_keypairs__account_id"); | |
| keys.add("fk_ssh_keypairs__domain_id"); | |
| DbUpgradeUtils.dropKeysIfExist(conn, "ssh_keypairs", keys, true); | |
| keys = new ArrayList<String>(); | |
| keys.add("fk_ssh_keypair__account_id"); | |
| keys.add("fk_ssh_keypair__domain_id"); | |
| keys.add("fk_ssh_keypairs__account_id"); | |
| keys.add("fk_ssh_keypairs__domain_id"); | |
| DbUpgradeUtils.dropKeysIfExist(conn, "ssh_keypairs", keys, false); | |
| //insert the keys anew | |
| try { | |
| PreparedStatement pstmt; | |
| pstmt = | |
| conn.prepareStatement("ALTER TABLE `cloud`.`ssh_keypairs` ADD " | |
| + "CONSTRAINT `fk_ssh_keypairs__account_id` FOREIGN KEY `fk_ssh_keypairs__account_id` (`account_id`)" | |
| + " REFERENCES `account` (`id`) ON DELETE CASCADE"); | |
| pstmt.executeUpdate(); | |
| pstmt.close(); | |
| } catch (SQLException e) { | |
| throw new CloudRuntimeException("Unable to execute ssh_keypairs table update for adding account_id foreign key", e); | |
| } | |
| try { | |
| PreparedStatement pstmt; | |
| pstmt = | |
| conn.prepareStatement("ALTER TABLE `cloud`.`ssh_keypairs` ADD CONSTRAINT" | |
| + " `fk_ssh_keypairs__domain_id` FOREIGN KEY `fk_ssh_keypairs__domain_id` (`domain_id`) " + "REFERENCES `domain` (`id`) ON DELETE CASCADE"); | |
| pstmt.executeUpdate(); | |
| pstmt.close(); | |
| } catch (SQLException e) { | |
| throw new CloudRuntimeException("Unable to execute ssh_keypairs table update for adding domain_id foreign key", e); | |
| } | |
| } | |
| private void encryptClusterDetails(Connection conn) { | |
| s_logger.debug("Encrypting cluster details"); | |
| PreparedStatement pstmt = null; | |
| ResultSet rs = null; | |
| try { | |
| pstmt = conn.prepareStatement("select id, value from `cloud`.`cluster_details` where name = 'password'"); | |
| rs = pstmt.executeQuery(); | |
| while (rs.next()) { | |
| long id = rs.getLong(1); | |
| String value = rs.getString(2); | |
| if (value == null) { | |
| continue; | |
| } | |
| String encryptedValue = DBEncryptionUtil.encrypt(value); | |
| pstmt = conn.prepareStatement("update `cloud`.`cluster_details` set value=? where id=?"); | |
| pstmt.setBytes(1, encryptedValue.getBytes("UTF-8")); | |
| pstmt.setLong(2, id); | |
| pstmt.executeUpdate(); | |
| } | |
| } catch (SQLException e) { | |
| throw new CloudRuntimeException("Unable encrypt cluster_details values ", e); | |
| } catch (UnsupportedEncodingException e) { | |
| throw new CloudRuntimeException("Unable encrypt cluster_details values ", e); | |
| } finally { | |
| closeAutoCloseable(rs); | |
| closeAutoCloseable(pstmt); | |
| } | |
| s_logger.debug("Done encrypting cluster_details"); | |
| } | |
| } |