| // 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 |
| // 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.usage; |
| |
| import java.io.BufferedReader; |
| import java.io.BufferedWriter; |
| import java.io.FileReader; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.sql.Connection; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| |
| import com.cloud.utils.db.TransactionLegacy; |
| |
| public class UsageSanityChecker { |
| |
| private StringBuffer errors; |
| private String lastCheckId = ""; |
| private final String lastCheckFile = "/usr/local/libexec/sanity-check-last-id"; |
| |
| private boolean checkMaxUsage(Connection conn) throws SQLException{ |
| |
| PreparedStatement pstmt = conn.prepareStatement("SELECT value FROM `cloud`.`configuration` where name = 'usage.stats.job.aggregation.range'"); |
| ResultSet rs = pstmt.executeQuery(); |
| |
| int aggregationRange = 1440; |
| if(rs.next()){ |
| aggregationRange = rs.getInt(1); |
| } else { |
| System.out.println("Failed to retrieve aggregation range. Using default : "+aggregationRange); |
| } |
| |
| int aggregationHours = aggregationRange / 60; |
| |
| /* |
| * Check for usage records with raw_usage > aggregationHours |
| */ |
| pstmt = conn.prepareStatement("SELECT count(*) FROM `cloud_usage`.`cloud_usage` cu where usage_type not in (4,5) and raw_usage > "+aggregationHours+lastCheckId); |
| rs = pstmt.executeQuery(); |
| if(rs.next() && (rs.getInt(1) > 0)){ |
| errors.append("Error: Found "+rs.getInt(1)+" usage records with raw_usage > "+aggregationHours); |
| errors.append("\n"); |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean checkVmUsage(Connection conn) throws SQLException{ |
| boolean success = true; |
| /* |
| * Check for Vm usage records which are created after the vm is destroyed |
| */ |
| PreparedStatement pstmt = conn.prepareStatement("select count(*) from cloud_usage.cloud_usage cu inner join cloud.vm_instance vm where vm.type = 'User' " + |
| "and cu.usage_type in (1 , 2) and cu.usage_id = vm.id and cu.start_date > vm.removed"+lastCheckId); |
| ResultSet rs = pstmt.executeQuery(); |
| if(rs.next() && (rs.getInt(1) > 0)){ |
| errors.append("Error: Found "+rs.getInt(1)+" Vm usage records which are created after Vm is destroyed"); |
| errors.append("\n"); |
| success = false; |
| } |
| |
| /* |
| * Check for Vms which have multiple running vm records in helper table |
| */ |
| pstmt = conn.prepareStatement("select sum(cnt) from (select count(*) as cnt from cloud_usage.usage_vm_instance where usage_type =1 " + |
| "and end_date is null group by vm_instance_id having count(vm_instance_id) > 1) c ;"); |
| rs = pstmt.executeQuery(); |
| if(rs.next() && (rs.getInt(1) > 0)){ |
| errors.append("Error: Found "+rs.getInt(1)+" duplicate running Vm entries in vm usage helper table"); |
| errors.append("\n"); |
| success = false; |
| } |
| |
| /* |
| * Check for Vms which have multiple allocated vm records in helper table |
| */ |
| pstmt = conn.prepareStatement("select sum(cnt) from (select count(*) as cnt from cloud_usage.usage_vm_instance where usage_type =2 " + |
| "and end_date is null group by vm_instance_id having count(vm_instance_id) > 1) c ;"); |
| rs = pstmt.executeQuery(); |
| if(rs.next() && (rs.getInt(1) > 0)){ |
| errors.append("Error: Found "+rs.getInt(1)+" duplicate allocated Vm entries in vm usage helper table"); |
| errors.append("\n"); |
| success = false; |
| } |
| |
| /* |
| * Check for Vms which have running vm entry without allocated vm entry in helper table |
| */ |
| pstmt = conn.prepareStatement("select count(vm_instance_id) from cloud_usage.usage_vm_instance o where o.end_date is null and o.usage_type=1 and not exists " + |
| "(select 1 from cloud_usage.usage_vm_instance i where i.vm_instance_id=o.vm_instance_id and usage_type=2 and i.end_date is null)"); |
| rs = pstmt.executeQuery(); |
| if(rs.next() && (rs.getInt(1) > 0)){ |
| errors.append("Error: Found "+rs.getInt(1)+" running Vm entries without corresponding allocated entries in vm usage helper table"); |
| errors.append("\n"); |
| success = false; |
| } |
| return success; |
| } |
| |
| private boolean checkVolumeUsage(Connection conn) throws SQLException{ |
| boolean success = true; |
| /* |
| * Check for Volume usage records which are created after the volume is removed |
| */ |
| PreparedStatement pstmt = conn.prepareStatement("select count(*) from cloud_usage.cloud_usage cu inner join cloud.volumes v " + |
| "where cu.usage_type = 6 and cu.usage_id = v.id and cu.start_date > v.removed"+lastCheckId); |
| ResultSet rs = pstmt.executeQuery(); |
| if(rs.next() && (rs.getInt(1) > 0)){ |
| errors.append("Error: Found "+rs.getInt(1)+" volume usage records which are created after volume is removed"); |
| errors.append("\n"); |
| success = false; |
| } |
| |
| /* |
| * Check for duplicate records in volume usage helper table |
| */ |
| pstmt = conn.prepareStatement("select sum(cnt) from (select count(*) as cnt from cloud_usage.usage_volume " + |
| "where deleted is null group by id having count(id) > 1) c;"); |
| rs = pstmt.executeQuery(); |
| if(rs.next() && (rs.getInt(1) > 0)){ |
| errors.append("Error: Found "+rs.getInt(1)+" duplicate records is volume usage helper table"); |
| errors.append("\n"); |
| success = false; |
| } |
| return success; |
| } |
| |
| private boolean checkTemplateISOUsage(Connection conn) throws SQLException{ |
| /* |
| * Check for Template/ISO usage records which are created after it is removed |
| */ |
| PreparedStatement pstmt = conn.prepareStatement("select count(*) from cloud_usage.cloud_usage cu inner join cloud.template_zone_ref tzr " + |
| "where cu.usage_id = tzr.template_id and cu.zone_id = tzr.zone_id and cu.usage_type in (7,8) and cu.start_date > tzr.removed"+lastCheckId); |
| ResultSet rs = pstmt.executeQuery(); |
| if(rs.next() && (rs.getInt(1) > 0)){ |
| errors.append("Error: Found "+rs.getInt(1)+" template/ISO usage records which are created after it is removed"); |
| errors.append("\n"); |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean checkSnapshotUsage(Connection conn) throws SQLException{ |
| /* |
| * Check for snapshot usage records which are created after snapshot is removed |
| */ |
| PreparedStatement pstmt = conn.prepareStatement("select count(*) from cloud_usage.cloud_usage cu inner join cloud.snapshots s " + |
| "where cu.usage_id = s.id and cu.usage_type = 9 and cu.start_date > s.removed"+lastCheckId); |
| ResultSet rs = pstmt.executeQuery(); |
| if(rs.next() && (rs.getInt(1) > 0)){ |
| errors.append("Error: Found "+rs.getInt(1)+" snapshot usage records which are created after snapshot is removed"); |
| errors.append("\n"); |
| return false; |
| } |
| return true; |
| } |
| |
| public String runSanityCheck() throws SQLException{ |
| try { |
| BufferedReader reader = new BufferedReader( new FileReader (lastCheckFile)); |
| String last_id = null; |
| if( (reader != null) && ( last_id = reader.readLine() ) != null ) { |
| int lastId = Integer.parseInt(last_id); |
| if(lastId > 0){ |
| lastCheckId = " and cu.id > "+last_id; |
| } |
| } |
| reader.close(); |
| } catch (Exception e) { |
| // Error while reading last check id |
| } |
| |
| Connection conn = TransactionLegacy.getStandaloneConnection(); |
| int maxId = 0; |
| PreparedStatement pstmt = conn.prepareStatement("select max(id) from cloud_usage.cloud_usage"); |
| ResultSet rs = pstmt.executeQuery(); |
| if(rs.next() && (rs.getInt(1) > 0)){ |
| maxId = rs.getInt(1); |
| lastCheckId += " and cu.id <= "+maxId; |
| } |
| errors = new StringBuffer(); |
| checkMaxUsage(conn); |
| checkVmUsage(conn); |
| checkVolumeUsage(conn); |
| checkTemplateISOUsage(conn); |
| checkSnapshotUsage(conn); |
| FileWriter fstream; |
| try { |
| fstream = new FileWriter(lastCheckFile); |
| BufferedWriter out = new BufferedWriter(fstream); |
| out.write(""+maxId); |
| out.close(); |
| } catch (IOException e) { |
| // Error while writing last check id |
| } |
| return errors.toString(); |
| } |
| |
| public static void main(String args[]){ |
| UsageSanityChecker usc = new UsageSanityChecker(); |
| String sanityErrors; |
| try { |
| sanityErrors = usc.runSanityCheck(); |
| if(sanityErrors.length() > 0){ |
| System.out.println(sanityErrors.toString()); |
| } |
| } catch (SQLException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |