/*************************************************************************** * Copyright (c) 2012-2013 VMware, Inc. All Rights Reserved. * Licensed 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.vmware.aurora.vc; import java.net.NoRouteToHostException; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.List; import org.apache.http.conn.HttpHostConnectException; import org.apache.log4j.Logger; import com.vmware.aurora.exception.AuroraException; import com.vmware.aurora.util.AuAssert; import com.vmware.aurora.vc.vcservice.VcContext; import com.vmware.aurora.vc.vcservice.VcService; import com.vmware.vim.binding.impl.vmodl.TypeNameImpl; import com.vmware.vim.binding.vim.Folder; import com.vmware.vim.binding.vmodl.ManagedObject; import com.vmware.vim.binding.vmodl.ManagedObjectReference; import com.vmware.vim.binding.vmodl.TypeName; import com.vmware.vim.vmomi.core.types.VmodlType; import com.vmware.vim.vmomi.core.types.VmodlTypeMap; /** * A utility class to support common <code>ManagedObject</code> and * <code>ManagedObjectReference</code> related tasks. */ public class MoUtil { private static final Logger logger = Logger.getLogger(MoUtil.class); private static final String MOREF_GUID_FORMAT = "%1s:%2s:%3s"; /** * XXX This needs to be public only while we store moref values instead of * guids in the database. It should be deleted after migration to vc module. * * @param serverGuid * @param type * @param value * @return */ public static String makeGuid(String serverGuid, String type, String value) { return String.format( MOREF_GUID_FORMAT, serverGuid, type, value); } /** * Returns a guid that represents the supplied ManagedObjectReference. Null * objects are represented by an empty string. */ public static String morefToString(ManagedObjectReference moRef) { if (moRef == null) { return ""; } return makeGuid(moRef.getServerGuid(), moRef.getType(), moRef.getValue()); } /** * Reverse to the previous function: given a string in the format * MOREF_GUID_FORMAT, return ManagedObjectReference. Returns null if the * string has incorrect format. */ public static ManagedObjectReference stringToMoref(String str) { if (str == null) { return null; } String[] comps = str.split(":"); if (comps.length != 2 && comps.length != 3) { return null; } // normalize null string values for (int i = 0; i < comps.length; i++) { if (comps[i] != null && (comps[i].equals("null") || comps[i].equals(""))) { comps[i] = null; } } ManagedObjectReference ref = new ManagedObjectReference(); if (comps.length == 2) { ref.setServerGuid(null); ref.setType(comps[0]); ref.setValue(comps[1]); } else { ref.setServerGuid(comps[0]); ref.setType(comps[1]); ref.setValue(comps[2]); } // make sure the reference object is valid if (ref.getType() == null || ref.getValue() == null || (ref.getServerGuid() != null && !ref.getServerGuid().equals(VcContext.getServerGuid()))) { return null; } return ref; } /** * Utility method to determine if the type of a specific * {@link ManagedObjectReference} instance is of the provided * <code>clazz</code>. * * The following example demonstrates the intended usage: * @code { * boolean isStoragePod = ManagedObjectUtil.isOfType(entity, StoragePod.class); * } * * This method is more type safe and less error prone compared to the * version which accepts string parameter: * {@link MoUtil#isOfType(ManagedObjectReference, String)} * * @param entity * The {@link ManagedObjectReference} instance which type to be * compared. * * @param clazz * The <code>Class</code> instance of the class for which we want to check. * * @return * <code>true</code> if the <code>entity</code> is of the specified type or * <code>false</code> otherwise. * * @see #isOfType(ManagedObjectReference, String) */ public static boolean isOfType(ManagedObjectReference entity, Class<?> clazz) { if (entity == null || clazz == null) { return false; } VmodlType type = VmodlTypeMap.Factory.getTypeMap().getVmodlType(clazz); return type.getWsdlName().equalsIgnoreCase(entity.getType()); } /** * Utility method to determine if the type of a specific refId * is of the provided <code>clazz</code>. * @param entityId: refId of the VIM object to check. * @param clazz: instance of the class we want to check for. * @return * <code>true</code> if the object referred to by the * <code>entityId</code> is of the specified type or * <code>false</code> otherwise. */ public static boolean isOfType(String entityId, Class<?> clazz) { return isOfType(stringToMoref(entityId), clazz); } /** * Convert a managed object reference to managed object. */ public static <T extends ManagedObject> T getManagedObject(ManagedObjectReference moRef) { AuAssert.check(moRef != null && moRef.getValue() != null && (moRef.getServerGuid() == null || moRef.getServerGuid().equals(VcContext.getServerGuid()))); // Sun's javac requires explicit casting. // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 AuAssert.check(VcContext.isInSession()); return VcContext.getService().<T>getManagedObject(moRef); } /** * Convert a list of managed object reference to managed object list. */ public static <T extends ManagedObject> List<T> getManagedObjects(ManagedObjectReference[] moRefs) throws Exception { ArrayList<T> list = new ArrayList<T>(moRefs.length); for (ManagedObjectReference moRef : moRefs) { list.add(MoUtil.<T>getManagedObject(moRef)); } return list; } public static Folder getRootFolder() throws Exception { AuAssert.check(VcContext.isInSession()); VcService svc = VcContext.getService(); ManagedObjectReference rootFolderRef = svc.getServiceInstanceContent().getRootFolder(); return svc.getManagedObject(rootFolderRef); } /** * Helper function to get all child entities of type <code>T</code> * in the <code>Folder</code> managed object. */ public static <T extends ManagedObject> List<T> getChildEntity(Folder folder, Class<T> clazz) { AuAssert.check(VcContext.isInSession()); VcService svc = VcContext.getService(); ManagedObjectReference[] childEntities = folder.getChildEntity(); List<T> entities = new ArrayList<T>(); for (ManagedObjectReference child : childEntities) { if (isOfType(child, clazz)) { entities.add(svc.<T>getManagedObject(child)); } } return entities; } /** * Similar to <code>getChildEntity()</code>, but does a deep traversal to * find all descendants of type <code>T</code> starting with a given folder. * @param <T> * @param folder * @param clazz * @return */ public static <T extends ManagedObject> List<ManagedObjectReference> getDescendantsMoRef(Folder folder, Class<T> clazz) { List<ManagedObjectReference> descendants = new ArrayList<ManagedObjectReference>(); getDescendants(folder, clazz, descendants); return descendants; } private static <T extends ManagedObject> void getDescendants(Folder folder, Class<T> clazz, List<ManagedObjectReference> descendants) { AuAssert.check(VcContext.isInSession()); VcService svc = VcContext.getService(); ManagedObjectReference[] childEntities = folder.getChildEntity(); for (ManagedObjectReference child : childEntities) { if (isOfType(child, clazz)) { descendants.add(child); } else if(isOfType(child, Folder.class)) { Folder childFolder = svc.<Folder>getManagedObject(child); getDescendants(childFolder, clazz, descendants); } } } /** * Traverse the managed entity tree to find an ancestor of type <T>. * @param <T> * @param parent * @param clazz * @return */ protected static <T extends ManagedObject> ManagedObjectReference getAncestorMoRef(ManagedObjectReference parent, Class<T> clazz) { AuAssert.check(VcContext.isInSession()); VcService svc = VcContext.getService(); if (isOfType(parent, clazz)) { return parent; } else { ManagedObject obj = svc.getManagedObject(parent); if (obj instanceof Folder) { Folder folder = (Folder)obj; return getAncestorMoRef(folder.getParent(), clazz); } } logger.error("cannot find ancestor VC object of type " + clazz.getName() + " for " + parent); throw AuroraException.INTERNAL(); } /** * Returns TypeName for the passed moRef. * @param moRef * @return TypeName */ public static TypeName getTypeName(ManagedObjectReference moRef) { return new TypeNameImpl(moRef.getType()); } /** * Returns true when an exception is caused by connection or network * issues. This includes the connection problems due to vc being down. * @param e exception to check * @return true for network related vc exceptions */ public static boolean isNetworkException(Throwable e) { return e instanceof HttpHostConnectException || /* Temporary connectivity loss? */ e instanceof NoRouteToHostException || /* Network glitch. */ e instanceof SocketTimeoutException || /* Client side. Session might be ok. */ e instanceof SocketException; /* Many cases including vc shutdown. */ } /** * VLSI doesn't convert some special characters supported by VC * from URL format to String format on the receive path. See PR 737040. * * @param s * @return */ public static String fromURLString(String s) { if (s == null) { return s; } StringBuffer buf = new StringBuffer(); int i = 0; while (i < s.length()) { if (s.startsWith("%", i)) { // scan the next two characters String code = s.substring(i + 1, i + 3); if (code.equals("25")) { buf.append('%'); i += 3; continue; } if (code.equals("2f") || code.equals("2F")){ buf.append('/'); i += 3; continue; } if (code.equals("5c") || code.equals("5C")){ buf.append('\\'); i += 3; continue; } } buf.append(s.charAt(i)); i++; } return buf.toString(); } public static String toURLString(String s) { if (s == null) { return s; } StringBuffer buf = new StringBuffer(); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); switch (ch) { case '%': buf.append("%25"); break; case '/': buf.append("%2f"); break; case '\\': buf.append("%5c"); break; default: buf.append(ch); } } return buf.toString(); } }