// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by 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. // // Automatically generated by addcopyright.py at 04/03/2012 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.Transaction; 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 = Transaction.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(); } } }