- CLOUDSTACK-3229: Properly serialize the https property to the s3Xen

plugin
  - Extracts the duplicated serializeProperties methods to
    ReflectUtils#flattenProperties
  - Adds unit tests for ReflectUtils#flattenProperties
This commit is contained in:
John Burwell 2013-07-31 23:14:28 -04:00
parent 51e4f597e0
commit 1ae682de78
5 changed files with 261 additions and 194 deletions

View File

@ -16,92 +16,6 @@
// under the License. // under the License.
package com.cloud.hypervisor.xen.resource; package com.cloud.hypervisor.xen.resource;
import com.google.gson.Gson;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
import javax.xml.parsers.DocumentBuilderFactory;
import com.cloud.agent.api.to.DhcpTO;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import org.apache.xmlrpc.XmlRpcException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import com.trilead.ssh2.SCPClient;
import com.xensource.xenapi.Bond;
import com.xensource.xenapi.Connection;
import com.xensource.xenapi.Console;
import com.xensource.xenapi.Host;
import com.xensource.xenapi.HostCpu;
import com.xensource.xenapi.HostMetrics;
import com.xensource.xenapi.Network;
import com.xensource.xenapi.PBD;
import com.xensource.xenapi.PIF;
import com.xensource.xenapi.PIF.Record;
import com.xensource.xenapi.Pool;
import com.xensource.xenapi.SR;
import com.xensource.xenapi.Session;
import com.xensource.xenapi.Task;
import com.xensource.xenapi.Types;
import com.xensource.xenapi.Types.BadAsyncResult;
import com.xensource.xenapi.Types.BadServerResponse;
import com.xensource.xenapi.Types.ConsoleProtocol;
import com.xensource.xenapi.Types.IpConfigurationMode;
import com.xensource.xenapi.Types.OperationNotAllowed;
import com.xensource.xenapi.Types.SrFull;
import com.xensource.xenapi.Types.VbdType;
import com.xensource.xenapi.Types.VmBadPowerState;
import com.xensource.xenapi.Types.VmPowerState;
import com.xensource.xenapi.Types.XenAPIException;
import com.xensource.xenapi.VBD;
import com.xensource.xenapi.VBDMetrics;
import com.xensource.xenapi.VDI;
import com.xensource.xenapi.VIF;
import com.xensource.xenapi.VLAN;
import com.xensource.xenapi.VM;
import com.xensource.xenapi.VMGuestMetrics;
import com.xensource.xenapi.XenAPIObject;
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import com.cloud.agent.IAgentControl; import com.cloud.agent.IAgentControl;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.Answer;
import com.cloud.agent.api.AttachIsoCommand; import com.cloud.agent.api.AttachIsoCommand;
@ -242,6 +156,7 @@ import com.cloud.agent.api.storage.ResizeVolumeAnswer;
import com.cloud.agent.api.storage.ResizeVolumeCommand; import com.cloud.agent.api.storage.ResizeVolumeCommand;
import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DataTO;
import com.cloud.agent.api.to.DhcpTO;
import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.DiskTO;
import com.cloud.agent.api.to.FirewallRuleTO; import com.cloud.agent.api.to.FirewallRuleTO;
import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.IpAddressTO;
@ -299,6 +214,82 @@ import com.cloud.vm.DiskProfile;
import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.snapshot.VMSnapshot; import com.cloud.vm.snapshot.VMSnapshot;
import com.google.gson.Gson;
import com.trilead.ssh2.SCPClient;
import com.xensource.xenapi.Bond;
import com.xensource.xenapi.Connection;
import com.xensource.xenapi.Console;
import com.xensource.xenapi.Host;
import com.xensource.xenapi.HostCpu;
import com.xensource.xenapi.HostMetrics;
import com.xensource.xenapi.Network;
import com.xensource.xenapi.PBD;
import com.xensource.xenapi.PIF;
import com.xensource.xenapi.PIF.Record;
import com.xensource.xenapi.Pool;
import com.xensource.xenapi.SR;
import com.xensource.xenapi.Session;
import com.xensource.xenapi.Task;
import com.xensource.xenapi.Types;
import com.xensource.xenapi.Types.BadAsyncResult;
import com.xensource.xenapi.Types.BadServerResponse;
import com.xensource.xenapi.Types.ConsoleProtocol;
import com.xensource.xenapi.Types.IpConfigurationMode;
import com.xensource.xenapi.Types.OperationNotAllowed;
import com.xensource.xenapi.Types.SrFull;
import com.xensource.xenapi.Types.VbdType;
import com.xensource.xenapi.Types.VmBadPowerState;
import com.xensource.xenapi.Types.VmPowerState;
import com.xensource.xenapi.Types.XenAPIException;
import com.xensource.xenapi.VBD;
import com.xensource.xenapi.VBDMetrics;
import com.xensource.xenapi.VDI;
import com.xensource.xenapi.VIF;
import com.xensource.xenapi.VLAN;
import com.xensource.xenapi.VM;
import com.xensource.xenapi.VMGuestMetrics;
import com.xensource.xenapi.XenAPIObject;
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import org.apache.xmlrpc.XmlRpcException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import static com.cloud.utils.ReflectUtil.flattenProperties;
/** /**
* CitrixResourceBase encapsulates the calls to the XenServer Xapi process * CitrixResourceBase encapsulates the calls to the XenServer Xapi process
@ -7401,53 +7392,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
return new BackupSnapshotAnswer(cmd, success, details, snapshotBackupUuid, fullbackup); return new BackupSnapshotAnswer(cmd, success, details, snapshotBackupUuid, fullbackup);
} }
private static List<String> serializeProperties(final Object object,
final Class<?> propertySet) {
assert object != null;
assert propertySet != null;
assert propertySet.isAssignableFrom(object.getClass());
try {
final BeanInfo beanInfo = Introspector.getBeanInfo(propertySet);
final PropertyDescriptor[] descriptors = beanInfo
.getPropertyDescriptors();
final List<String> serializedProperties = new ArrayList<String>();
for (final PropertyDescriptor descriptor : descriptors) {
serializedProperties.add(descriptor.getName());
final Object value = descriptor.getReadMethod().invoke(object);
serializedProperties.add(value != null ? value.toString()
: "null");
}
return Collections.unmodifiableList(serializedProperties);
} catch (IntrospectionException e) {
s_logger.warn(
"Ignored IntrospectionException when serializing class "
+ object.getClass().getCanonicalName(), e);
} catch (IllegalArgumentException e) {
s_logger.warn(
"Ignored IllegalArgumentException when serializing class "
+ object.getClass().getCanonicalName(), e);
} catch (IllegalAccessException e) {
s_logger.warn(
"Ignored IllegalAccessException when serializing class "
+ object.getClass().getCanonicalName(), e);
} catch (InvocationTargetException e) {
s_logger.warn(
"Ignored InvocationTargetException when serializing class "
+ object.getClass().getCanonicalName(), e);
}
return Collections.emptyList();
}
private boolean backupSnapshotToS3(final Connection connection, private boolean backupSnapshotToS3(final Connection connection,
final S3TO s3, final String srUuid, final String snapshotUuid, final S3TO s3, final String srUuid, final String snapshotUuid,
final Boolean iSCSIFlag, final int wait) { final Boolean iSCSIFlag, final int wait) {
@ -7460,8 +7404,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
try { try {
final List<String> parameters = new ArrayList<String>( final List<String> parameters = flattenProperties(s3,
serializeProperties(s3, S3Utils.ClientOptions.class)); S3Utils.ClientOptions.class);
parameters.addAll(Arrays.asList("operation", "put", "directory", parameters.addAll(Arrays.asList("operation", "put", "directory",
dir, "filename", filename, "iSCSIFlag", dir, "filename", filename, "iSCSIFlag",
iSCSIFlag.toString(), "bucket", s3.getBucketName(), "key", key)); iSCSIFlag.toString(), "bucket", s3.getBucketName(), "key", key));

View File

@ -71,22 +71,17 @@ import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.XmlRpcException;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File; import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import static com.cloud.utils.ReflectUtil.flattenProperties;
public class XenServerStorageProcessor implements StorageProcessor { public class XenServerStorageProcessor implements StorageProcessor {
private static final Logger s_logger = Logger.getLogger(XenServerStorageProcessor.class); private static final Logger s_logger = Logger.getLogger(XenServerStorageProcessor.class);
protected CitrixResourceBase hypervisorResource; protected CitrixResourceBase hypervisorResource;
@ -1066,53 +1061,6 @@ public class XenServerStorageProcessor implements StorageProcessor {
return lfilename; return lfilename;
} }
private static List<String> serializeProperties(final Object object,
final Class<?> propertySet) {
assert object != null;
assert propertySet != null;
assert propertySet.isAssignableFrom(object.getClass());
try {
final BeanInfo beanInfo = Introspector.getBeanInfo(propertySet);
final PropertyDescriptor[] descriptors = beanInfo
.getPropertyDescriptors();
final List<String> serializedProperties = new ArrayList<String>();
for (final PropertyDescriptor descriptor : descriptors) {
serializedProperties.add(descriptor.getName());
final Object value = descriptor.getReadMethod().invoke(object);
serializedProperties.add(value != null ? value.toString()
: "null");
}
return Collections.unmodifiableList(serializedProperties);
} catch (IntrospectionException e) {
s_logger.warn(
"Ignored IntrospectionException when serializing class "
+ object.getClass().getCanonicalName(), e);
} catch (IllegalArgumentException e) {
s_logger.warn(
"Ignored IllegalArgumentException when serializing class "
+ object.getClass().getCanonicalName(), e);
} catch (IllegalAccessException e) {
s_logger.warn(
"Ignored IllegalAccessException when serializing class "
+ object.getClass().getCanonicalName(), e);
} catch (InvocationTargetException e) {
s_logger.warn(
"Ignored InvocationTargetException when serializing class "
+ object.getClass().getCanonicalName(), e);
}
return Collections.emptyList();
}
private boolean backupSnapshotToS3(final Connection connection, private boolean backupSnapshotToS3(final Connection connection,
final S3TO s3, final String srUuid, final String snapshotUuid, final S3TO s3, final String srUuid, final String snapshotUuid,
final Boolean iSCSIFlag, final int wait) { final Boolean iSCSIFlag, final int wait) {
@ -1125,8 +1073,8 @@ public class XenServerStorageProcessor implements StorageProcessor {
try { try {
final List<String> parameters = new ArrayList<String>( final List<String> parameters = flattenProperties(s3,
serializeProperties(s3, S3Utils.ClientOptions.class)); S3Utils.ClientOptions.class);
parameters.addAll(Arrays.asList("operation", "put", "directory", parameters.addAll(Arrays.asList("operation", "put", "directory",
dir, "filename", filename, "iSCSIFlag", dir, "filename", filename, "iSCSIFlag",
iSCSIFlag.toString(), "bucket", s3.getBucketName(), "key", key)); iSCSIFlag.toString(), "bucket", s3.getBucketName(), "key", key));

View File

@ -306,7 +306,7 @@ def parseArguments(args):
# the com.cloud.utils.S3Utils#ClientOptions interface # the com.cloud.utils.S3Utils#ClientOptions interface
client = S3Client( client = S3Client(
args['accessKey'], args['secretKey'], args['endPoint'], args['accessKey'], args['secretKey'], args['endPoint'],
args['isHttps'], args['connectionTimeout'], args['socketTimeout']) args['https'], args['connectionTimeout'], args['socketTimeout'])
operation = args['operation'] operation = args['operation']
bucket = args['bucket'] bucket = args['bucket']

View File

@ -16,8 +16,17 @@
// under the License. // under the License.
package com.cloud.utils; package com.cloud.utils;
import com.cloud.utils.exception.CloudRuntimeException;
import com.google.common.collect.ImmutableSet;
import org.apache.log4j.Logger;
import org.reflections.Reflections;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -25,10 +34,14 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import com.cloud.utils.exception.CloudRuntimeException; import static java.beans.Introspector.getBeanInfo;
import org.reflections.Reflections; import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
public class ReflectUtil { public class ReflectUtil {
private static final Logger s_logger = Logger.getLogger(ReflectUtil.class);
public static Pair<Class<?>, Field> getAnyField(Class<?> clazz, String fieldName) { public static Pair<Class<?>, Field> getAnyField(Class<?> clazz, String fieldName) {
try { try {
return new Pair<Class<?>, Field>(clazz, clazz.getDeclaredField(fieldName)); return new Pair<Class<?>, Field>(clazz, clazz.getDeclaredField(fieldName));
@ -129,4 +142,75 @@ public class ReflectUtil {
return fields; return fields;
} }
public static List<String> flattenProperties(final Object target,
final Class<?> clazz) {
return flattenPropeties(target, clazz, "class");
}
public static List<String> flattenPropeties(final Object target,
final Class<?> clazz,
final String...
excludedProperties) {
return flattenProperties(target, clazz,
ImmutableSet.copyOf(excludedProperties));
}
private static List<String> flattenProperties(final Object target,
final Class<?> clazz,
final ImmutableSet<String>
excludedProperties) {
assert clazz != null;
if (target == null) {
return emptyList();
}
assert clazz.isAssignableFrom(target.getClass());
try {
final BeanInfo beanInfo = getBeanInfo(clazz);
final PropertyDescriptor[] descriptors = beanInfo
.getPropertyDescriptors();
final List<String> serializedProperties = new ArrayList<String>();
for (final PropertyDescriptor descriptor : descriptors) {
if (excludedProperties.contains(descriptor.getName())) {
continue;
}
serializedProperties.add(descriptor.getName());
final Object value = descriptor.getReadMethod().invoke(target);
serializedProperties.add(value != null ? value.toString()
: "null");
}
return unmodifiableList(serializedProperties);
} catch (IntrospectionException e) {
s_logger.warn(
"Ignored IntrospectionException when serializing class "
+ target.getClass().getCanonicalName(), e);
} catch (IllegalArgumentException e) {
s_logger.warn(
"Ignored IllegalArgumentException when serializing class "
+ target.getClass().getCanonicalName(), e);
} catch (IllegalAccessException e) {
s_logger.warn(
"Ignored IllegalAccessException when serializing class "
+ target.getClass().getCanonicalName(), e);
} catch (InvocationTargetException e) {
s_logger.warn(
"Ignored InvocationTargetException when serializing class "
+ target.getClass().getCanonicalName(), e);
}
return emptyList();
}
} }

View File

@ -0,0 +1,91 @@
// 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.utils;
package com.cloud.utils;
import java.util.List;
import org.junit.Test;
import static com.cloud.utils.ReflectUtil.flattenProperties;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.Boolean.TRUE;
import static java.util.Collections.emptyList;
import static org.junit.Assert.assertEquals;
public final class ReflectUtilTest {
@Test
public void testFlattenNonNullProperties() throws Exception {
final List<String> expectedResult = newArrayList("booleanProperty",
TRUE.toString(), "intProperty", "1",
"stringProperty", "foo");
final Bean bean = new Bean(1, true, "foo");
assertEquals(expectedResult, flattenProperties(bean, Bean.class));
}
@Test
public void testFlattenNullProperties() throws Exception {
final List<String> expectedResult = newArrayList("booleanProperty",
TRUE.toString(), "intProperty", "1",
"stringProperty", "null");
final Bean bean = new Bean(1, true, null);
assertEquals(expectedResult, flattenProperties(bean, Bean.class));
}
@Test
public void testFlattenPropertiesNullTarget() throws Exception {
assertEquals(emptyList(), flattenProperties(null, Bean.class));
}
public static final class Bean {
private final int intProperty;
private final boolean booleanProperty;
private final String stringProperty;
private Bean(final int intProperty, final boolean booleanProperty,
final String stringProperty) {
super();
this.intProperty = intProperty;
this.booleanProperty = booleanProperty;
this.stringProperty = stringProperty;
}
public int getIntProperty() {
return intProperty;
}
public boolean isBooleanProperty() {
return booleanProperty;
}
public String getStringProperty() {
return stringProperty;
}
}
}