| // 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 templete 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"); |
| } |
| } |